Help us understand the problem. What is going on with this article?

【JINSMEME】RxSwiftでJINSMEMESamplerの雛形を作ってみる

More than 3 years have passed since last update.

少しでも英語に慣れるため、タイトルのみ英語にて失礼します。

tl;dr

RxSwiftを使うと

  • delegateを複数もてた
  • UITableViewを簡単に実装できた
  • ただ要勉強

Motivation

新しいことを始める上で、まだ試したことのないRxSwiftを触ってみたくなった、というのと、JINSMEME関連のサンプルを作るたびにあたらしいリポジトリを立てるにしても、接続部分とか毎回同じソースをコピペするのもなんだかなと思い、ベースになるようなプロジェクトを作りたいと思っていたので、JINSMEMESamplerなんて横行な名前で両者を混ぜて作ってみました。

Github

https://github.com/mitolog/JINSMEMESampler
※ Work In Progressなリポジトリですので、参加大歓迎です.

What's RxSwift?

RxSwiftというのは、非同期なイベントベースのプログラムを作る上で、複数のObservable(監視可能)オブジェクトを作成し、それらを時系列で管理して、ViewとModelのbindingを楽にすることができるライブラリです。

より細かい説明は、英語ですが、Getting Startedが勉強になります。

Project concept diagram

JINSMEMESamplerProjImage.png

SDKでは主にMEMELibというクラスがJINS MEMEとのインターフェースとなっていて、

  • JINS MEMEと接続/接続解除
  • Delegateプロトコルでデータを受け取る
  • JINS MEMEのメタ情報(Firmwareのversionとか)を取得する

といった機能を提供しています。

本プロジェクトでは、MEMELibシングルトンのプロキシインスタンス(MEMELibAccess.swift)を作成し、そのインスタンスがJINS MEMEからのデータを受け取り、各sampleファイルがそのデータにアクセスするという形をとっています。

※ DelegateProxyという使い方でextensionを書けるみたいですが、うまくいかなかったので、重複するようで嫌ですが一旦シングルトンのプロキシインスタンスを作っています。

Pros using RxSwift

Multiple Delegates

MEMELibクラスはDelegateプロトコルを介して、データを受け渡します。
しかし、Delegateプロトコルは、同時に1つのインスタンスにしか適用できません。
なので、複数の箇所でデータを利用したい場合、

  • 都度delegateを変更する
  • KVOを利用する
  • delegateチェーンを作る
  • Notificationを利用する

といった手法を取らなければいけませんでした。いずれにしても、余分なコードが増えて可読性が悪くなったり、クラス同士の結合度があがったり、メモリリークの温床となったりして、導入するには躊躇してしまうようなものでした。

それを、RxSwiftのPublishSubject()という機能を使うことで、簡単に且つ安全に?複数のdelegateを持つ振る舞いを実装することができました

具体的には、まずMEMELibAccess.swiftをSingletonにし、delegateのプロキシインスタンスのようになってもらいます。そして、そのdelegateメソッドの中でPublishSubjectのイベントを発火する。

MEMELibAccess.swift
class MEMELibAccess: NSObject, MEMELibDelegate {
    static let sharedInstance = MEMELibAccess()

    var rx_memeConnected = PublishSubject<CBPeripheral>()

    // 中略

    func memePeripheralConnected(peripheral: CBPeripheral!) {
        self.rx_memeConnected.on(.Next(peripheral))
    }
}

すると、それを監視していたObserverにイベントが流れる。

ScanViewController.swift
MEMELibAccess.sharedInstance.rx_memeConnected.subscribeNext { [unowned self] peripheral in
    print("meme connected")
    self.scanBtn.enabled = false
    self.tableView.userInteractionEnabled = false

    // go to next view
    self.performSegueWithIdentifier("SampleListSegue", sender: self)

}.addDisposableTo(self.disposeBag)

という寸法です。

Info: How PublishSubject works in this sample

ReactiveXな世界ではSubjectというやつは監視する側(Observer)にもなれるし、監視される側(Observable)にもなれる機能を持っていますが、ここではObservableとしての機能のみ利用しています。

以下の概念図をみるとイメージしやすいのですが、1番上の資格で囲ったシーケンスがMEMELibAccess.swiftのdelegateメソッドの時系列だと考えてください。

そのすぐ下が、例えばScanViewController.swift上の時系列と考えてください(一番下は無視)。

この時、それぞれの時系列は独立しています。

で、この概念図でいくと、ScanViewController.swiftではMEMELibAccess.sharedInstance.rx_memeConnected.subscribeNextした時点から、delegateメソッドのObservableをウォッチし始めます。

これは、ほぼKVOの動きと同じですが、KVOを実装するよりもコード量も少なく、断然扱いやすく感じます。

publishsubject.png
画像: RxSwiftのPlaygroundより

参考: http://reactivex.io/documentation/subject.html

UITableView+Rx is so powerful

いつもなら、UITableViewのdelegate,dataSourceメソッドを実装して~ってやりますが、そういうのが必要ありません。例えば、ScanViewController.swiftで実装しているUITableView関連の処理は以下のみです。

ScanViewController.swift
viewModel.peripherals.bindTo(self.tableView.rx_itemsWithCellIdentifier("PeripheralCell")) { _, peripheral, cell -> Void in
    cell.textLabel?.text = peripheral.identifier.UUIDString
}.addDisposableTo(self.disposeBag)

// tableview did selected
self.tableView.rx_itemSelected.subscribeNext { [unowned self] indexPath in
    let peripheral = self.viewModel.peripherals.value[indexPath.row]
    MEMELibAccess.connect(peripheral)
}.addDisposableTo(self.disposeBag)

上記は、いわゆるcellForRow~のメソッドと、didSelect~のメソッドに相当します。とてもすっきりしたように感じます。しかも、いつも書いていた、プロトコル準拠の宣言すら書いていません

Cons using RxSwift

Hard to understand its source code

概念を勉強するだけなら

http://reactivex.io/

https://github.com/ReactiveX/RxSwift
あたりを見ればいいのですが、いざ実際にプロジェクトに組み込もうとすると、様々なRxSwiftの様々な機能を使っていかなければいけません。

その際、ソースを掘っていくと、結構意味不明な文法が多用されていて、はて....と手が止まってしまいます。しかもソースも膨大で、堀れすぎてしまい元に戻るのが大変です。

逆にいうとそういうハイレベルなswiftを読めるので全体的なSwift力の底上げにはつながりますが、そのたびにgoogleさんに聞くので非常に時間がかかります。ただ、元々の知識量と思いますので、個人差の問題かもしれません。

Is it safe?

RxSwift初心者的には、ブラックボックスが大きすぎて、自分のしていることがナンセンスなことかそうじゃないかの見分けがつきません...なので、今自分がしていることでリークが起きていないかいつもビクビクしています。特にdisposebagあたりの実装が完全に理解できていないので、これは勉強しておく必要があるかと思いました。

Summarize

RxSwiftを使うと

  • delegateを複数もてた
  • UITableViewを簡単に実装できた
  • ただ要勉強

ということで、JINS MEMEの以後のサンプルでもなるべくRxSwiftで書いてみようと思います。なるべく。

Reference

RxSwiftを使ってGitHubのおすすめユーザーを表示するアプリをつくってみた

Implementing MVVM in iOS with RxSwift

RxSwiftでUITableViewのバインド処理

あと、RxSwiftのplaygroundは非常に勉強になります。

mito_log
ベトナム・ハノイ拠点の個人デベロッパ。東南アジア向けに素敵なサービスと文化を作るべく模索中。ベトナムで1年半農業。newbie skateboarder、ハノイITもくもく会毎週土曜朝9時頃 → https://bit.ly/2Oe9Ehk
http://mitolab.hatenablog.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした