[イベントレポート] iOS Discord Offline に参加しました! #ios_discord

はじめに

おばんです、田中です。

今日はDiscord上に存在するios-discord-japanというコミュニティのミートアップに参加してきたので、レポートをお届けします。

ios-discord-japan の普段の活動はDiscord上で行われていて、もっぱらiOSやSwiftに関するアツい議論が繰り広げられるのが特徴です。詳しい内容はオーナーである mono さんの以下の記事をご覧ください。

イベントの内容はDiscord上でも実況され、議論されていました。内容や雰囲気を伝えるために、Discordのスクリーンショットも掲載しながらレポートしていこうと思います。「第一部: モダンなプログラミング言語としてのSwift」、「第二部: iOSアプリの開発言語としてのSwift」として二部構成で行われる予定でしたが、様々な話題が飛び交っていたので、二部構成を無視して箇条書きして紹介していきます!

もくじ

  • 登壇メンバー
  • iOS Discord Offline とは
  • はじまりのわいわい、がやがや
  • Conditional Conformance
  • Synthesizing Equatable and Hashable
  • 休憩&食事タイム
  • UITableViewControllerって使う?
  • ClassClusterの話
  • まとめ

登壇メンバー

今回のミートアップはパネルディスカッション形式で行われました。ここではパネルディスカッションに参加したメンバーを紹介します。メンバー紹介はconnpassから引用しています。

@tarunon

株式会社メルカリ所属。空が青かったから転職した。好きなものはコンパイラで、趣味はコンパイラを虐めること。サンプルコードのAとかBとかわかりづらいって言われて凹む。

@omochimetaru

プログラミングが好き。言語はSwiftが一番好き。Swiftには大統一言語になって欲しい。

@takasek

それなりに型と設計が好きなiOSエンジニア。趣味は勉強会駆動勉強。 Discordではだいたい読む専なので、今回「オーディエンス目線って立ち位置でイケるな、うん」と呟いては心の平穏を保っている。

@k_katsumi

その場の流れで緊急登壇。

iOS Discord Offline とは

Discord上でのやり取りがあまりに速く進むので、ついていけない、怖いといった印象をお持ちの方がいるかもしれませんが、そんなことないよ、平和で楽しいよ、ということがお伝えできればと思います。 (connpassより引用)

ios-discord-japan は、主要メンバーとして今回の登壇メンバーがいます。普段の活動はDiscord上で、文字ベースで主要メンバー同士が議論することが多いです。難しい内容が素早く繰り広げられるそれは、はたから見ると、時にはマサカリを投げ合っている状況に見えがちです。ですが主催曰く、このやりとりは喧嘩をしてマサカリを投げ合っているわけではなくて、意見を交わしているだけなのでぜひ他の人も気軽に参加してほしい、と訴えかけることが今回のミートアップの目的の一つとのこと。

はじまりのわいわい、がやがや

普段 ios-developers-japan のDiscord上やTwitter上でしか姿を見ない @tarunon 氏と @omochimetaru 氏が本当に実在するのか疑われる様子。

(写真左がtarunon氏、右がomochimetaru氏。同一人物ではなく、別個に実在しています。)

Conditional Conformance

Conditional Conformanceがわかりやすいのは以下のコード。Boxの中のTにEquatableが適用されているならば、Boxにも適用されるとする機能で、これはSwift 4.1に入るそうです。

struct Box<T> {
    let value: T
}

extension Box: Equatable where T: Equatable {
    static func == (lhs: Box, rhs: Box) -> Bool {
        return lhs.value == rhs.value
    }
}

これまでであれば、以下のようにオーバーロードで関数を生やして対処していましたが、Conditional Conformanceの登場でこれが必要でなくなります。

func ==<X>(a: [X], b: [X]) where X: Equatable {  ... }

プロポーザルはこちら。

Synthesizing Equatable and Hashable

中のプロパティがHashableのみで構成されているstructはEquatableの実装を省略することができる機能? Swift 4.1で使える模様。Equatableのボイラープレートを減らせるメリットがあるようです。

以下のようなコードを書く必要がなくなり、そのままPerson同士を比較することができるようになります。

struct Person: Equatable {
  static func == (lhs: Person, rhs: Person) -> Bool {
    return lhs.firstName == rhs.firstName &&
           lhs.lastName == rhs.lastName &&
           lhs.birthDate == rhs.birthDate &&
           ...
  }
}

プロポーザルはこちら。

突然SILを出力して読もうとする@omochimetaru氏

Swiftの中間言語であるSILを出力して、ハッシュ化をどのように行なっているかを読もうとする@omochimetaru氏。該当するコードは以下の箇所。

@omochimetaru氏 「このあたりが怪しい」

(田中は「そもそもSILって?」と思ったので、以下の記事を軽く読んだりしました。)

休憩&食事タイム

怒涛の前半戦から、休憩がてらの食事タイムが設けられました。

食事は会場を提供いただいたメルカリ様より。立食しやすく、コミュニケーションもとりやすい串料理!美味しかったです、ありがとうございます!

型安全にInterfaceBuilderからUIを初期化し、同時に値も渡したい

UIStoryboardからUIViewControllerを生成する時に制限されてしまうことに以下の二つがあります。

  • Storyboardから読み込んだUIViewControllerの型がわからない(フォースキャストしがち)
  • Storyboardから初期化するUIViewControllerに初期値を代入することができない(コードからでしかできない)

これを解決するために考えられたのが@tarunon氏が作ったこちら。

上のリポジトリで言いたいことを要約すると、「protocolでinitを宣言して、protocol extensionの中で実装するとselfを上書きできる」ということ。そして直後に変数に値を代入してその結果を返せば、初期化時に値を代入するのと同じ状況が作れるということ。(コードはこちらから部分的に削っています)

public extension StoryboardInstantiatable where Self: UIViewController {
    public init(with dependency:Dependency) {
        let storyboard = (Self.self as StoryboardType.Type).storyboard
        self = storyboard.instantiateInitialViewController() as! Self
        self.inject(dependency)
    }
}

UITableViewControllerって使う?

UITableViewControllerはレイアウトを組みづらいという問題があります。(たとえばUITableViewControllerの子ViewはUIViewではなくUITableViewではじまる、など)そのためUITableViewControllerを使うのではなく、最初からUITableViewをUIViewController上に置いてカスタマイズしていく方が良い、ということがよく叫ばれています。

しかし@k_katsumi氏曰く、UITableViewControllerの方が便利な場面もあるので、使い分けていきましょうとのこと。UITableViewControllerを使うときは、ContainerViewに格納した形で使うとのちのちのレイアウト変更も容易になります。

ClassClusterの話

たとえばこんなコード。Factoryableを適用したAnimalクラスから、そのAnimalクラスを継承した別のクラスを生成して返却することができたりする。

import Foundation

protocol Factoryable {
    init(factory: () -> Self)
}

extension Factoryable {
    init(factory: () -> Self) {
        self = factory()
    }
}

class MyAnimal: Factoryable {
    convenience init(name: String) {
        if name.contains("Cat") {
            self.init { MyCat() }
        } else {
            self.init { MyDog() }
        }
    }
}

class MyCat: MyAnimal {
    var meou: String { return "meou" }
}

class MyDog: MyAnimal {

}

let x = MyCat(name: "Dog") // <- 返却されるのはMyDog
x.meou // Crash!!

MyDogにbarkを定義して、MyCatのmeouを呼び出すとなぜかbowwowと返却される。(田中は理解していませんが、@omochimetaru氏曰く「たまたまバイナリが重なっただけ」)

...
......

class MyCat: MyAnimal {
    var meou: String { return "meou" }
}

class MyDog: MyAnimal {
    func bark: String { return "bowwow" }
}

let x = MyCat(name: "Dog") // <- MyDog
x.meou // <- "bowwow"

CatがDogの振る舞いをしたりするこの状況は、@takasek氏のこちらの資料が思い出されました。

そして最終形がこちら。barkを引数持ちの関数にした形。 (※注意!以下のコードをPlayground上に書くと、開くたびにすぐ落ちる問題が発生して、そのPlaygroundを開けなくなる問題が発生します)

class MyCat: MyAnimal {
    var meou: String { return "meou" }
}

class MyDog: MyAnimal {
    func bark(arg: String) -> String { return arg }
}

let x = MyCat(name: "Dog")
let y = x.meou // Crash!!

Xcodeがクラッシュしたところで、ちょうど区切りもついたということで今回はお開きとなりました。

まとめ

だいたいの時系列順に紹介してきました。Swiftのプロポーザルを追って新しく便利な機能を試していく様子が面白かったです。Discord上だと文字だけのやりとりになるので表情が見えづらいということがありますが、今回のOfflineで登壇されているメンバーの様子は冷たいマサカリで切りつけ合うものではなく、朗らかに意見を交えて楽しんでいるようでした。やりとりをみているのがとても楽しく、これはコンテンツだなと思いました。

この会のような話がDiscord上ではよく行われています。もし「わかりづらい!わからない!」ということがあれば、その場で聞いてもよいし、#beginner-helpという別チャンネルで聞いてもよいので参加してみてくださいとのこと。