2K Views
April 19, 16
スライド概要
atte FeS【Go・Swift開発編】での発表資料に加筆・修正をしたものです。
http://mercari.connpass.com/event/29506/
SwiftとLEGOとBluetooth LEが好きなプログラマ
アッテ開発の技術 SwiftとRxSwift ⼤庭 慎⼀郎 株式会社メルカリ / 株式会社ソウゾウ 2016/4/18 atte FeS【Go・Swift開発編】 1
⾃⼰紹介 ⼤庭 慎⼀郎 ooba / bricklife 株式会社メルカリに2013年4⽉⼊社 現在は株式会社ソウゾウへ出向中 「メルカリ」iOS版の⽴ち上げ 「メルカリ アッテ」iOS版の⽴ち上げ 2
メルカリ アッテ 「なんでも募集できる 地域コミュニティアプリ」 iOS 8以降対応 Swift 2.2で絶賛開発中 RxSwiftを全面採用 3
iOS版開発の歴史 できごと 2015年10⽉ ソウゾウに出向 モック作成開始 ライブラリ&設計検討開始 4 メンバー 1 11⽉ RxSwift採⽤決定 本実装開始 合宿 1 12⽉ 幻の初サブミット 1 2016年1⽉ 孤独な⼀⼈開発から脱出 2 2⽉ 紹介制で公開 3 3⽉ 正式オープン 4 4⽉ Go Boldに開発中 4.5
なぜiOS 8以降か
なぜiOS 8以降か 2015年10⽉のタイミングでiOS 7のシェ アはわずか iOS 8と7とではSDKが⼤きく違う 実装スピード優先 Carthageが使いたかった 6
なぜSwiftか 7
なぜSwiftか アッテは新規プロジェクト メルカリiOS版の資産はほぼ使えない ⻑くメンテナンスするコードになる いまSwift採⽤しないでいつ採⽤する? ⼈材募集効果も期待 8
Swiftでないとできないこと 型安全、Optional、enum、etc. APIKitやHimotokiなど⽇本製のイケてる ライブラリを使える • しかしAPIKitはJSON-RPC 2.0と相性が悪かったので泣 く泣く不採⽤ • 9 同じ思想でJSONRPCKitというのを⾃作した
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
API側の実装 type LikeParams struct { OfferId int64 `json:"offer_id"` } 11
なぜRxSwiftか
その前に
なぜリアクティブ プログラミングか
なぜリアクティブプログラミングか メルカリiOS版では、Objective-Cでリア クティブプログラミングを実現する ReactiveCocoaをヘビーに使っていた もうリアクティブプログラミングなしには プログラムが組めない! 15
リアクティブプロ グラミングとは
リアクティブプログラミングとは 変なこと⾔うとマサカリが⾶んで来るので 説明割愛 17
リアクティブプログラミングのメリット 様々な同期処理や⾮同期処理を 「ストリームをどう扱うか」 という視点から「統⼀的」に かつ「宣⾔的」に記述できる 18
リアクティブプログラミングが提供するもの 1. ストリームを⽣成する⽅法 2. ストリームを加⼯する⽅法 3. ストリームを監視する⽅法 19
これがストリームだ! データ 時間 開始 エラー or 完了 どちらかの発⽣によって ストリームが終わる 20 ↑この図を「マーブル図」という
ストリームの⽣成 ⽂字列、配列、KVO、UIイベント、ネット ワーク通信、デリゲートメソッド呼び出し、 など、なんでもストリームにできる 21
配列のストリーム化 [0, 1, 2, 3] 0 22 1 2 3
タップのストリーム化 タップ 23 ダブルタップ タップ
テキスト⼊⼒のストリーム化 24 A B C Delete A AB ABC AB
ネットワーク通信のストリーム化 リクエスト 受信中 受信完了 中身は レスポンス 25
26 https://gist.github.com/staltz/868e7e9bc2a7b8c1f754
ストリームの加⼯ ストリーム上のデータに対して filter したり map したり merge したり zip し たり reduce したりできる オペレータと呼ばれる combineLatest や buffer など、時間 概念があるからこそのオペレータもある 27
filter 28 http://rxmarbles.com/#filter
map 29 http://rxmarbles.com/#map
merge 30 http://rxmarbles.com/#merge
zip 31 http://rxmarbles.com/#zip
reduce 32 http://rxmarbles.com/#reduce
combineLatest 33 http://rxmarbles.com/#combineLatest
buffer 34 http://reactivex.io/documentation/operators/ buffer.html
オペレータを組み合わせた例:ダブルタップ 35 https://gist.github.com/staltz/868e7e9bc2a7b8c1f754
ストリームを使ってできること リスト処理 ⾮同期イベント処理 データバインディング Promise MVVM, etc. 36
例:テキストフィールドの変更をラベルに⾃動反映 textField.rx_text .map { "「\($0)」" } .bindTo(label.rx_text) .addDisposableTo(disposeBag) 37
例:条件を満たすまでボタンを押せないようにする 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
例:インクリメンタルサーチ
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
アッテの実例:画像アップロード後の投稿 画像のアップロードと投稿は別API 画像アップロードAPIで画像をアップロー ドするとメディアIDが発⾏される 投稿APIにはメディアIDの配列を渡す 40
すべての画像をアップロード後の新規投稿 画像1 画像2 画像3 画像4 combineLatest() 41
⼀部の画像を再アップロード後の編集投稿 画像1 画像2 画像3 画像4 combineLatest() 42
アッテの実例:新規投稿・編集投稿のコード
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)
Swiftにおける リアクティブプログラ ミング
Swiftにおけるリアクティブプログラミング 2015年10⽉時点での選択肢 45 • ReactiveCocoa • RxSwift • ReactKit
検討中の発表 46 https://speakerdeck.com/bricklife/swift-2-dot-0derxswiftreactkit-reactivecocoawoshi-tutemita
なぜRxSwiftか
RxSwiftのメリット その1 RxSwiftとはMicrosoftが2011年にリリース したReactive ExtensionsのSwift版 どの⾔語でもほぼ同じ仕様なので、約5年分 の資産がある RxSwiftは正式にReactiveXへ取り込まれた 開発やコミュニティが活発 48
RxSwiftのメリット その2 他⾔語でRxをしていた⼈は取り組みやすい 今後他⾔語でRxをやるときに経験が活かせ る AndroidではRxJavaがデファクトスタンダー ドなので設計が共有できる? 49
ReactiveCocoaとReactKitの評価 ReactiveCocoaは2015年の時点ではまだα 版で、Readme.mdに書かれているサンプ ルすらコンパイルできない状況… ReactKitは機能的に不⾜を感じた 50
vs ReactiveCocoa現⾏版 いまはReactiveCocoaもかなり成熟 特徴 • Cold ObservableとHot Observableを明確にクラスで分 けている(SignalとSignalProducer) • エラーに型がある • コードがきれいらしい(伝聞) どこかでちゃんと使ってみたい 51
RxSwiftのデメリット
RxSwiftのデメリット RxSwift固有のデメリットはいまのところ あまり感じない 強いて⾔えばReactiveCocoaの特徴の反対 53 • Cold ObservableとHot Observableを混合 • エラーに型がない
リアクティブプログラミングのデメリット 学習コストが⾼い 設計に⼤きく影響(特にMVVM) ライブラリが巨⼤ もし開発が⽌まったらどうする? 頼りきっているとプログラミング能⼒が衰える? 54
チームへの浸透
有益な資料 【翻訳】あなたが求めていたリアクティブプ ログラミング⼊⾨ http://ninjinkun.hatenablog.com/entry/introrxja RxMarbles http://rxmarbles.com 公式ドキュメント(以下は有志の⽇本語版) https://github.com/tid-kijyun/RxSwift/wiki 56
チームへの浸透 前述のドキュメント 既存コード 実装パターンの共有 いい実装にはRPで 🏄 をつける とにかくRxに触れる機会を増やす 57
とにかくRxに触れる機会を増やす 社内Slackに#tech-rxというRx全般の話をす るチャンネルを設置して、気軽に情報共有や 相談をできるようにしている Reactive Swift Meetupの企画 RxSwift勉強会への参加 ズンドコキヨシ with RxSwift 58
⼀緒に川遊びしま しょう!