アッテ開発の技術:Swift と RxSwift

2.1K Views

April 19, 16

スライド概要

atte FeS【Go・Swift開発編】での発表資料に加筆・修正をしたものです。
http://mercari.connpass.com/event/29506/

profile-image

SwiftとLEGOとBluetooth LEが好きなプログラマ

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

アッテ開発の技術 SwiftとRxSwift ⼤庭 慎⼀郎 株式会社メルカリ / 株式会社ソウゾウ 2016/4/18 atte FeS【Go・Swift開発編】 1

2.

⾃⼰紹介 ⼤庭 慎⼀郎 ooba / bricklife 株式会社メルカリに2013年4⽉⼊社 現在は株式会社ソウゾウへ出向中 「メルカリ」iOS版の⽴ち上げ 「メルカリ アッテ」iOS版の⽴ち上げ 2

3.

メルカリ アッテ 「なんでも募集できる 地域コミュニティアプリ」 iOS 8以降対応 Swift 2.2で絶賛開発中 RxSwiftを全面採用 3

4.

iOS版開発の歴史 できごと 2015年10⽉ ソウゾウに出向 モック作成開始 ライブラリ&設計検討開始 4 メンバー 1 11⽉ RxSwift採⽤決定 本実装開始 合宿 1 12⽉ 幻の初サブミット 1 2016年1⽉ 孤独な⼀⼈開発から脱出 2 2⽉ 紹介制で公開 3 3⽉ 正式オープン 4 4⽉ Go Boldに開発中 4.5

5.

なぜiOS 8以降か

6.

なぜiOS 8以降か 2015年10⽉のタイミングでiOS 7のシェ アはわずか iOS 8と7とではSDKが⼤きく違う 実装スピード優先 Carthageが使いたかった 6

7.

なぜSwiftか 7

8.

なぜSwiftか アッテは新規プロジェクト メルカリiOS版の資産はほぼ使えない ⻑くメンテナンスするコードになる いまSwift採⽤しないでいつ採⽤する? ⼈材募集効果も期待 8

9.

Swiftでないとできないこと 型安全、Optional、enum、etc. APIKitやHimotokiなど⽇本製のイケてる ライブラリを使える • しかしAPIKitはJSON-RPC 2.0と相性が悪かったので泣 く泣く不採⽤ • 9 同じ思想でJSONRPCKitというのを⾃作した

10.
[beta]
JSONRPCKit
struct Like: RequestType {
typealias Response = LikeResult
let offerId: Int64
var method: String {
return "LikeService.Like"
}
var params: AnyObject? {
return ["offer_id": NSNumber(longLong: offerId)]
}
}

{
id: “1”,
jsonrpc: “2.0",
method: “LikeService.Like",
params: {
“offer_id”: 123456;
}
}

10

11.

API側の実装 type LikeParams struct { OfferId int64 `json:"offer_id"` } 11

12.

なぜRxSwiftか

13.

その前に

14.

なぜリアクティブ プログラミングか

15.

なぜリアクティブプログラミングか メルカリiOS版では、Objective-Cでリア クティブプログラミングを実現する ReactiveCocoaをヘビーに使っていた もうリアクティブプログラミングなしには プログラムが組めない! 15

16.

リアクティブプロ グラミングとは

17.

リアクティブプログラミングとは 変なこと⾔うとマサカリが⾶んで来るので 説明割愛 17

18.

リアクティブプログラミングのメリット 様々な同期処理や⾮同期処理を 「ストリームをどう扱うか」 という視点から「統⼀的」に かつ「宣⾔的」に記述できる 18

19.

リアクティブプログラミングが提供するもの 1. ストリームを⽣成する⽅法 2. ストリームを加⼯する⽅法 3. ストリームを監視する⽅法 19

20.

これがストリームだ! データ 時間 開始 エラー or 完了 どちらかの発⽣によって ストリームが終わる 20 ↑この図を「マーブル図」という

21.

ストリームの⽣成 ⽂字列、配列、KVO、UIイベント、ネット ワーク通信、デリゲートメソッド呼び出し、 など、なんでもストリームにできる 21

22.

配列のストリーム化 [0, 1, 2, 3] 0 22 1 2 3

23.

タップのストリーム化 タップ 23 ダブルタップ タップ

24.

テキスト⼊⼒のストリーム化 24 A B C Delete A AB ABC AB

25.

ネットワーク通信のストリーム化 リクエスト 受信中 受信完了 中身は レスポンス 25

26.

26 https://gist.github.com/staltz/868e7e9bc2a7b8c1f754

27.

ストリームの加⼯ ストリーム上のデータに対して filter したり map したり merge したり zip し たり reduce したりできる オペレータと呼ばれる combineLatest や buffer など、時間 概念があるからこそのオペレータもある 27

28.

filter 28 http://rxmarbles.com/#filter

29.

map 29 http://rxmarbles.com/#map

30.

merge 30 http://rxmarbles.com/#merge

31.

zip 31 http://rxmarbles.com/#zip

32.

reduce 32 http://rxmarbles.com/#reduce

33.

combineLatest 33 http://rxmarbles.com/#combineLatest

34.

buffer 34 http://reactivex.io/documentation/operators/ buffer.html

35.

オペレータを組み合わせた例:ダブルタップ 35 https://gist.github.com/staltz/868e7e9bc2a7b8c1f754

36.

ストリームを使ってできること リスト処理 ⾮同期イベント処理 データバインディング Promise MVVM, etc. 36

37.

例:テキストフィールドの変更をラベルに⾃動反映 textField.rx_text .map { "「\($0)」" } .bindTo(label.rx_text) .addDisposableTo(disposeBag) 37

38.

例:条件を満たすまでボタンを押せないようにする let textValidation = textField.rx_text .map { !$0.isEmpty } Observable.combineLatest( textValidation, loading.asObservable()) { text, loading in return text && !loading } .bindTo(submitButton.rx_enabled) .addDisposableTo(disposeBag) 38

39.
[beta]
例:インクリメンタルサーチ

textField.rx_text
.debounce(0.5, scheduler: MainScheduler.instance)
.distinctUntilChanged()
.map { query -> Observable<[Item]> in
if query.isEmpty {
return Observable.just([])
}
let request = GetItemsRequest(query: query)
return API.responseFrom(request)

}
.switchLatest()
.subscribeNext { [weak self] items in
print("next: \(items.count)")
self?.items = items
self?.tableView.reloadData()
}
.addDisposableTo(disposeBag)

39

40.

アッテの実例:画像アップロード後の投稿 画像のアップロードと投稿は別API 画像アップロードAPIで画像をアップロー ドするとメディアIDが発⾏される 投稿APIにはメディアIDの配列を渡す 40

41.

すべての画像をアップロード後の新規投稿 画像1 画像2 画像3 画像4 combineLatest() 41

42.

⼀部の画像を再アップロード後の編集投稿 画像1 画像2 画像3 画像4 combineLatest() 42

43.
[beta]
アッテの実例:新規投稿・編集投稿のコード
class Photo {
var image: UIImage
var mediaId: String?
}
var request = createUpdateRequest()
photos
.map { photo -> Observable<String> in
if let mediaId = photo.mediaId {
return Observable.just(mediaId)
}
return ImageUploader.uploadImage(photo.image)

}
.combineLatest { $0 }
.map { mediaIds -> Observable<UpdateOfferResult> in
request.mediaIds = mediaIds
return APIClient.sharedClient.responseFrom(request)
}
.switchLatest()
.subscribe(
// 投稿APIの結果を処理

43

)
.addDisposableTo(disposeBag)

44.

Swiftにおける リアクティブプログラ ミング

45.

Swiftにおけるリアクティブプログラミング 2015年10⽉時点での選択肢 45 • ReactiveCocoa • RxSwift • ReactKit

46.

検討中の発表 46 https://speakerdeck.com/bricklife/swift-2-dot-0derxswiftreactkit-reactivecocoawoshi-tutemita

47.

なぜRxSwiftか

48.

RxSwiftのメリット その1 RxSwiftとはMicrosoftが2011年にリリース したReactive ExtensionsのSwift版 どの⾔語でもほぼ同じ仕様なので、約5年分 の資産がある RxSwiftは正式にReactiveXへ取り込まれた 開発やコミュニティが活発 48

49.

RxSwiftのメリット その2 他⾔語でRxをしていた⼈は取り組みやすい 今後他⾔語でRxをやるときに経験が活かせ る AndroidではRxJavaがデファクトスタンダー ドなので設計が共有できる? 49

50.

ReactiveCocoaとReactKitの評価 ReactiveCocoaは2015年の時点ではまだα 版で、Readme.mdに書かれているサンプ ルすらコンパイルできない状況… ReactKitは機能的に不⾜を感じた 50

51.

vs ReactiveCocoa現⾏版 いまはReactiveCocoaもかなり成熟 特徴 • Cold ObservableとHot Observableを明確にクラスで分 けている(SignalとSignalProducer) • エラーに型がある • コードがきれいらしい(伝聞) どこかでちゃんと使ってみたい 51

52.

RxSwiftのデメリット

53.

RxSwiftのデメリット RxSwift固有のデメリットはいまのところ あまり感じない 強いて⾔えばReactiveCocoaの特徴の反対 53 • Cold ObservableとHot Observableを混合 • エラーに型がない

54.

リアクティブプログラミングのデメリット 学習コストが⾼い 設計に⼤きく影響(特にMVVM) ライブラリが巨⼤ もし開発が⽌まったらどうする? 頼りきっているとプログラミング能⼒が衰える? 54

55.

チームへの浸透

56.

有益な資料 【翻訳】あなたが求めていたリアクティブプ ログラミング⼊⾨ http://ninjinkun.hatenablog.com/entry/introrxja RxMarbles http://rxmarbles.com 公式ドキュメント(以下は有志の⽇本語版) https://github.com/tid-kijyun/RxSwift/wiki 56

57.

チームへの浸透 前述のドキュメント 既存コード 実装パターンの共有 いい実装にはRPで 🏄 をつける とにかくRxに触れる機会を増やす 57

58.

とにかくRxに触れる機会を増やす 社内Slackに#tech-rxというRx全般の話をす るチャンネルを設置して、気軽に情報共有や 相談をできるようにしている Reactive Swift Meetupの企画 RxSwift勉強会への参加 ズンドコキヨシ with RxSwift 58

59.

⼀緒に川遊びしま しょう!