読者です 読者をやめる 読者になる 読者になる

try! Swift Protocol-Oriented Programming in Networking #tryswiftconf Day2-9

try! Swift 2016

Yosuke Ishikawa

メルカリで働くiOSエンジニアです。APIKitというSwiftの言語機能を活かしたネットワークライブラリの開発者でもあります。最近は川遊びに夢中になっています。

twitter.com

Protocol-Oriented ProgrammingはWWDC 2015で紹介された新しいプログラミングのコンセプトです。この講演では、Protocol-Oriented Programmingがなぜネットワーキングに適しているのか、また、型安全性やコードのシンプルさにどのように貢献するのか説明します。実際のアプリケーション開発で使えるような実例や、RxSwiftと組み合わせた発展的な実例についても紹介します。

speakerdeck.com

f:id:niwatako:20160303162837j:plain f:id:niwatako:20160303162846j:plain

f:id:niwatako:20160303162856j:plain

ネットワーキングにおけるPOP

NSURLSessionをプロトコルでラップする

コールサイトのコードをシンプルに、レスポンスをタイプセーフに f:id:niwatako:20160303162954j:plain f:id:niwatako:20160303162950j:plain

右の形にラップします。右の図ではSessionの中に5ステップが入ります。リクエストをプロトコルにすることでレスポンスがリクエストに紐づく。

f:id:niwatako:20160303163047j:plain

指で刺した部分がポイント

例を示します。イシュー作成とリポジトリ検索。

f:id:niwatako:20160303163111j:plain

リクエストの方さえ指定すればレスポンスの方が自動的に決定し、型安全にレスポンスが取得できるようになる。

何故プロトコルを使うことがこの設計に適しているのでしょう。

ひとつ目の理由は、リクエストとレスポンスを紐付けられるからです。

f:id:niwatako:20160303163217j:plain

指の2箇所で紐付けられています。

f:id:niwatako:20160303163227j:plain

ひとつ目の指:リクエストの方の中にResponseという名前の型が存在します。プロトコル上でリクエストとレスポンスのひも付けが可能に。

ふたつ目の指:レスポンスがリクエストによって変わります

SessionのSendRequestのところで利用されます。 f:id:niwatako:20160303163334j:plain

ResponseFromObjectが利用されているので、Resultがリクエストごとに違う方を返します。

この結果が、リクエストに応じてレスポンスの型が変わるということです。

ふたつ目の理由

f:id:niwatako:20160303163421j:plain

デフォルト実装をフレキシブルに実装できる

f:id:niwatako:20160303163440j:plain

GithubRequestプロトコルを提供している。GithubのベースURLはいつも同じなのでデフォルト値を設定している。デフォルト実装で準拠する型の実装ハードルも下がる。

リポジトリ検索の例。arrayにダウンキャストしてRepositoryの型にインスタンス化する。

f:id:niwatako:20160303163539j:plain

これをリクエストごとにこれを実装するのは大変。Responseにデフォルト実装を与える

f:id:niwatako:20160303163638j:plain

Decodable の decode を通してオブジェクトを取り出します。

プロトコルに準拠していればdecodeをよべる。型制約つきExtensionを使う。

f:id:niwatako:20160303163715j:plain

デフォルト実装を提供できました。

f:id:niwatako:20160303163745j:plain

ここまでで、一つ一つのリクエストの定義はこのコードのようになります。これだけ。

WebAPIのドキュメントと同じ情報量。プロトコルのフレキシブルなデフォルト実装のおかげでシンプルに出来た。

なぜプロトコルだったか振り返りましょう

f:id:niwatako:20160303163838j:plain

  • 型安全にできる
  • フレキシブルなデフォルト実装

設計についてだけ述べましたが実装した物があります。

f:id:niwatako:20160303163911j:plain

APIKitはNSURLSession、Himotokiはdecode

Topic2

f:id:niwatako:20160303163955j:plain

ページングなどの制約をプロトコルで表す。

f:id:niwatako:20160303164019j:plain

リポジトリ検索URL

f:id:niwatako:20160303164034j:plain

ページングパラメータが page=1 と付いています

f:id:niwatako:20160303164107j:plain

ページ情報を持ち、ページ指定をするメソッドを用意。typealias では PaginationResponseTypeに準拠することを宣言。

f:id:niwatako:20160303164207j:plain

incomplete_requests true なら次のページが有る。それと結果の配列。

これをプロトコルで表します

f:id:niwatako:20160303164240j:plain

ここまででページネーションとレスポンスを表せた。これを使ってページネーションクライアントを実装する。

f:id:niwatako:20160303164340j:plain

3行目initで初期化段階で基準ページのリクエストを受け取る。

はじめにbaseRequestから1ページ目のリクエストを作成

f:id:niwatako:20160303164413j:plain

普通と思うかもしれませんが、重要なのは、プロトコルで定義したインターフェースしか利用していないということ。

汎用的であらゆるページネーションタイプのクライアントになれます。

振り返り

f:id:niwatako:20160303164518j:plain

リクエストタイプはもともとなんでも送れたら、ページネーションのプロトコルを定義した結果、ページネーション用のクライアントが出来た。より具体的な定義が可能になった。

Topic3 イベントの抽象化

f:id:niwatako:20160303164554j:plain

メソッド呼び出しを入力としてリクエストを行い出力コールバックを実行していた。

RXではこの入出力をストリームとして扱う

f:id:niwatako:20160303164639j:plain

ViewControllerからはリフレッシュや次のページのタイミングを送る。Paginationは次のページが有るかなどを返す。

インターフェースを見てみます。

f:id:niwatako:20160303164714j:plain

ページネーションクライアントとの違いは、メソッド入力で呼び出しを行っていたが、今度はリフレッシュトリガーとネクストページトリガーというストリームのトリガーになって、またストリームが返ってくるようになっています。エレメントは、ちょっと複雑ですが、”リクエストのレスポンスに基づいたエレメントの型”になる。

f:id:niwatako:20160303164852j:plain

Swiftの検索結果を表示します。読み込み中はインジケータが回ります。

f:id:niwatako:20160303164917j:plain

ViewModelを作る

f:id:niwatako:20160303164930j:plain f:id:niwatako:20160303164957j:plain

ViewModelを特殊化させるのはサーチリポジトリ

f:id:niwatako:20160303165034j:plain

次のページを読み込む

f:id:niwatako:20160303165054j:plain

UIをViewModelにつなげました。

ViewModelからのデータをUIにつなぎましょう

ローディングのストリームをインジケータにつなぐ

f:id:niwatako:20160303165129j:plain

データをテーブルビューにバインド、Cellに表示。型が決まってるから型推論されますね。

f:id:niwatako:20160303165225j:plain f:id:niwatako:20160303165310j:plain

バグがなければ動くはずです

f:id:niwatako:20160303165337j:plain

👏

PagenationのVeiwModelはリクエストやUIは変わるが、一般化されたPagenationViewModelを使いまわせる。

ページネーションの実装のためにしたことは

f:id:niwatako:20160303165432j:plain

まとめ

f:id:niwatako:20160303165458j:plain

  • プロトコルはネットワーキングの抽象化に良い
  • プロトコルに制約をつけると、具体的記述ができる
  • 型だけじゃなうてイベントの流れも抽象化すると面白い

SendRequestはSendRequestしたあとからProtocolExtensionで型を書き換えられる可能性はないか。

あまり複雑に絡まる設計はしていなくて、意図していない上書きがあるような状況には遭遇していないです。あったら共有してほしい。

スキルがバラバラなチームなので誰が何処を触るかわからない

なるほど。それは基盤を整える人の責任でも有りますね。

気に入った記事は はてなブックマーク

はてなブックマークアプリiOS開発チームから来ました! はてなブックマークにはSwift特集があります! 良い記事を見逃さないように、ご利用ください! http://b.hatena.ne.jp/hotentry/it/Swift

そして良いまとめ記事があったらはてなブックマークでブックマークしましょう! try! Swift の記事で盛り上がると嬉しいです!