RxSwiftでLike情報の画面間共有


グローバルな情報(今回はLikeの情報)を複数の画面間で共有する!

SNS系のアプリなどを作る際にグローバルな情報を複数の画面間で共有しないといけない場合がありますよね!例えばLikeの情報など
今回はユーザーが変更したこのLikeの情報をどうやって複数の画面で共有するかをRxSwiftを使って実践してみました。

サンプルコードはこちら!(Sample5です)

実際何をしているのか

今回はAPI通信はせずにローカルだけで情報の共有を実装して、cellにもCellModelを保持させていません。
*Twitter APIを利用してAPI通信も行うサンプルを実装予定

やったこと
- RxSwiftを使ってタイムラインを表示(TableView)
- 詳細画面に遷移後Likeボタンを押してLike情報を更新 → タイムラインの画面でもその情報をもとにLike情報をアップデート!!!

ステップ1: オブジェクトをTableViewに表示

はじめにItemというモデルを作成します。

Item
struct Item {
    let id: Int
    let name: String
    var isLiked: Bool
}

そしてDriver.justを使ってObservableなオブジェクトを作成し、rx_itemsWithCellIdentifierを使ってTableViewに対してバインドを行います!

Sample5ViewController
        let items = Driver.just([
            Item(id: 1, name: "Instagram", isLiked: false),
            Item(id: 2, name: "Facebook", isLiked: true),
            Item(id: 3, name: "Twitter", isLiked: false),
            Item(id: 4, name: "Mercari", isLiked: true),
            Item(id: 5, name: "Atte", isLiked: false),
            Item(id: 6, name: "Line", isLiked: true)
        ])

        items
            .drive(tableView.rx_itemsWithCellIdentifier("Cell", cellType: Sample5TableViewCell.self)) { index, item, cell in
                cell.item = item
            }
            .addDisposableTo(disposeBag)

これでオブジェクトをTableViewに表示できました!

どうやって情報を複数の画面間で共有するのか?

今回Like情報を共有するにあたってRxSwiftPublishSubjectを利用しています。

LikeSubject
class LikeSubject {
    static let ItemDidLikeNotification = PublishSubject<Item>()
}

PublishSubjectは自身がObserverにもObservableにもなれる便利なものです。

これを使用してLike情報を更新した時に更新されたitemをこのPublishSubjectに流します!
見ての通り購読したタイミングより前のオブジェクトはストリームには流れてきません。

詳細画面のボタンのタップに応じてLike情報を更新してグローバルな川に流す。

ここでしていること
- ボタンタップのストリームを作成する。
- ItemのLike情報をmap内で更新する。
- ボタンタップのストリームを利用して、更新されたItemPublishSubjectへ流す。
- 同じボタンタップのストリームを利用してボタンの文字を変える。

Sample5DetailViewController
        let buttonTapped = likeButton.rx_tap.asDriver()

        buttonTapped
            .map { [unowned self] _ -> Item in
                var item = self.item
                item.isLiked = item.isLiked ? false : true
                return item
            }
            .driveNext { updatedItem in

                LikeSubject.ItemDidLikeNotification
                    .onNext(updatedItem)
            }
            .addDisposableTo(disposeBag)

        buttonTapped
            .driveNext { [weak self] _ in
                guard let strongSelf = self else { return }
                if strongSelf.item.isLiked {
                    strongSelf.likeButton.setTitle("👎🏻ライクされていません", forState: .Normal)
                } else {
                    strongSelf.likeButton.setTitle("👍🏻ライクされています", forState: .Normal)
                }
            }
            .addDisposableTo(disposeBag)

ここでアップデートされたitemをグローバルなLikeSubject.ItemDidLikeNotificationonNextしています。
この場合はPublishSubjectobserverとして利用しています。

Sample5DetailViewController
.driveNext { updatedItem in

                LikeSubject.ItemDidLikeNotification
                    .onNext(updatedItem)
            }
            .addDisposableTo(disposeBag)

グローバルに流れてきたitemを購読して、タイムラインのLike情報を更新する。

グローバルなLikeSubject.ItemDidLikeNotificationcellitemを渡してあげているところで購読してあげます。

ここでしていること
- LikeSubject.ItemDidLikeNotificationに流れてくるitemを購読する。
- filterを使って流れてきたitemtableViewに表示するものと同じidの時だけフィルターする。
- cellに新しく流れてきたitemを渡してあげる。

Sample5ViewController
items
            .drive(tableView.rx_itemsWithCellIdentifier("Cell", cellType: Sample5TableViewCell.self)) { index, item, cell in
                cell.item = item
                cell.disposeBag = DisposeBag()

                LikeSubject.ItemDidLikeNotification
                    .filter { $0.id == item.id }
                    .subscribeNext { [weak cell] item in
                        cell?.item = item
                    }
                    .addDisposableTo(cell.disposeBag)
            }
            .addDisposableTo(disposeBag)

これで詳細画面でのLikeの変化を元のタイムラインでも共有できるようになりました!

次にやりたいこと!

次はローカルだけでなくて、実際にTwitterかなにかのAPIを利用して、そしてcellにcellModelを持たせて共有できるサンプルを作りたいです!