$30 off During Our Annual Pro Sale. View Details »

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

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

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

Shinichiro Oba

April 19, 2016
Tweet

More Decks by Shinichiro Oba

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

  3. ϝϧΧϦΞος
    3
    ʮͳΜͰ΋ืूͰ͖Δ
    ɹ஍ҬίϛϡχςΟΞϓϦʯ
    iOS 8Ҏ߱ରԠ
    Swift 2.2Ͱઈࢍ։ൃத
    RxSwiftΛશ໘࠾༻

    View Slide

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

    View Slide

  5. なぜiOS 8以降か

    View Slide

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

    View Slide

  7. なぜSwiftか
    7

    View Slide

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

    View Slide

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

    View Slide

  10. JSONRPCKit
    10
    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;
    }
    }

    View Slide

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

    View Slide

  12. なぜRxSwiftか

    View Slide

  13. その前に

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  22. 配列のストリーム化
    22

    [0, 1, 2, 3]

    View Slide

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

    View Slide

  24. テキスト⼊⼒のストリーム化
    24
    " "#$
    "# "#
    A B C Delete

    View Slide

  25. ネットワーク通信のストリーム化
    25
    ϦΫΤετ ड৴த ड৴׬ྃ
    த਎͸
    Ϩεϙϯε

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  38. 例:条件を満たすまでボタンを押せないようにする
    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)

    View Slide

  39. 例:インクリメンタルサーチ
    39
    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)

    View Slide

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

    View Slide

  41. すべての画像をアップロード後の新規投稿
    41
    combineLatest()
    ը૾̍
    ը૾̎
    ը૾̏
    ը૾̐

    View Slide

  42. ⼀部の画像を再アップロード後の編集投稿
    42
    combineLatest()
    ը૾̍
    ը૾̎
    ը૾̏
    ը૾̐

    View Slide

  43. アッテの実例:新規投稿・編集投稿のコード
    43
    class Photo {
    var image: UIImage
    var mediaId: String?
    }
    var request = createUpdateRequest()
    photos
    .map { photo -> Observable in
    if let mediaId = photo.mediaId {
    return Observable.just(mediaId)
    }
    return ImageUploader.uploadImage(photo.image)
    }
    .combineLatest { $0 }
    .map { mediaIds -> Observable in
    request.mediaIds = mediaIds
    return APIClient.sharedClient.responseFrom(request)
    }
    .switchLatest()
    .subscribe(
    // ౤ߘAPIͷ݁ՌΛॲཧ
    )
    .addDisposableTo(disposeBag)

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  47. なぜRxSwiftか

    View Slide

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

    View Slide

  49. RxSwiftのメリット その2
    49
    他⾔語でRxをしていた⼈は取り組みやすい
    今後他⾔語でRxをやるときに経験が活かせ

    AndroidではRxJavaがデファクトスタンダー
    ドなので設計が共有できる?

    View Slide

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

    View Slide

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

    View Slide

  52. RxSwiftのデメリット

    View Slide

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

    View Slide

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

    View Slide

  55. チームへの浸透

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide