RxSwiftのシンプルな説明
Swiftで簡単なアプリを写経するのは簡単なのであるが、やはり実用的なアプリを作成するには、レスポンシブでないと役な立たない(お客さん受けしない)よねということで、RxSwift
を学んでみた。
正直、3日間くらい、泣きながら、挫けそうになりながら、禿げそうになりながら食らいついていた。
はっきりいって理解するのがキツイ。
■ 開発環境
OS : MacOS High Sierra Version 10.13.6
Xcode : Version 9.4.1
■ サンプリアプリ
ボタンを押すとカウントアップするだけのアプリ。
■ ソースコード解説
ViewController.swift
のコードを説明する。
まずは、必要なライブラリをimportする。ここでは、RxSwift
を利用する。
import UIKit import RxSwift
先に、ラベルとボタンを配置する。
StreatBoardでも良いのであるが、敢えてコードで記述。
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Label let countLbl = UILabel() countLbl.text = "0" countLbl.textAlignment = .center countLbl.textColor = UIColor.blue countLbl.frame = CGRect(x: (self.view.frame.width-110)/2, y: 150, width: 110, height: 21) // Button let countUpBtn = UIButton() countUpBtn.frame = CGRect(x: (self.view.frame.width-100)/2, y: 200, width: 100, height: 30) countUpBtn.layer.borderWidth = 1.0 countUpBtn.layer.borderColor = UIColor.blue.cgColor countUpBtn.layer.cornerRadius = 5.0 countUpBtn.setTitleColor(UIColor.blue, for: .normal) countUpBtn.setTitle("カウントUP", for: .normal) countUpBtn.addTarget(self, action: #selector(ViewController.countUp(_:)), for: UIControlEvents.touchUpInside) self.view.addSubview(countLbl) self.view.addSubview(countUpBtn) }
次に、RxSwift
の肝であるクラスを作成する。
class RxSwiftSample { }
RxSwiftSample
クラスには、private宣言されたsubject
をPublishSubject<Int>()
でインスタンス化して保持しておく。それと、そのsubjectをObservableとして返すeventを定義する。
そして、実際の処理を外部から呼べる myFunc()
メソッドを定義する。その中で、onNext
とかいうイベントを発行する。
これが基本的な雛形という感じかな?
class RxSwiftSample { private let subject = PublishSubject<Int>() var event : Observable<Int>{ return subject } func myFunc(){ // 処理 print("myFuncが呼ばれました。") // イベント発行 subject.onNext(self.data) } }
次に、このRxSwiftSample
クラスの使い方なのであるが、ViewController
から利用できるように、RxSwiftSample
をモデルとしてmodel
で宣言しておく。また、後で利用するdisposeBag
も宣言しておく。
private let model = RxSwiftSample() private let disposeBag = DisposeBag()
上記を、ViewController
から利用する。
先ずは、順を追ってわかりやすく、ボタンに対して、countUp(_ sender: UIButton)
が呼ばれるように記述する。これは特に問題はないはず。
重要なのは、ここからmodel
のmyFunc()
を呼び出すことである。
override func viewDidLoad() { //ボタンへイベント登録 countUpBtn.addTarget(self, action: #selector(ViewController.countUp(_:)), for: UIControlEvents.touchUpInside) } @objc func countUp(_ sender: UIButton){ print("カウントアップ") //Modelの関数呼び出し model.myFunc() }
ここから、理解が進むと思うが、model.myFunc()
で処理を行うと、事前に登録されているsubscribe
で通知される。そして、 subject.onNext(self.data)
で渡されたパラメータも、value で受け取れる。
//Reactive処理 model.event.subscribe( onNext: {value in print("ここで通知") countLbl.text = String(value) }) .disposed(by: disposeBag)
これで、ようやく頭の中で処理シーケンスがつながった。
それでも理解できない場合は、私と同じように1日悩んで、print()
仕掛けて、処理を追ってみると良いと思う。
やはり、このようなフレームワークやデザインパターンの理解は正直キツイ。それでも、時間を掛けて手を動かすのが、ベストプラクティスだと思う。
その助けになればと祈る。情報がちょっと時代遅れ感はあるのだが・・・。
始めたのが遅いので仕方ない。w
■ 最後に
ここに記したコードは、自分がRxSwift
の構造を理解するために作成したコードです。間違い等がございましたらご指摘願います。
下記、全ソースコードです。
import UIKit import RxSwift private let model = RxSwiftSample() private let disposeBag = DisposeBag() class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Label let countLbl = UILabel() countLbl.text = "0" countLbl.textAlignment = .center countLbl.textColor = UIColor.blue countLbl.frame = CGRect(x: (self.view.frame.width-110)/2, y: 150, width: 110, height: 21) // Button let countUpBtn = UIButton() countUpBtn.frame = CGRect(x: (self.view.frame.width-100)/2, y: 200, width: 100, height: 30) countUpBtn.layer.borderWidth = 1.0 countUpBtn.layer.borderColor = UIColor.blue.cgColor countUpBtn.layer.cornerRadius = 5.0 countUpBtn.setTitleColor(UIColor.blue, for: .normal) countUpBtn.setTitle("カウントUP", for: .normal) countUpBtn.addTarget(self, action: #selector(ViewController.countUp(_:)), for: UIControlEvents.touchUpInside) self.view.addSubview(countLbl) self.view.addSubview(countUpBtn) //Reactive処理 model.event.subscribe( onNext: {value in countLbl.text = String(value) }) .disposed(by: disposeBag) } @objc func countUp(_ sender: UIButton){ //Modelの関数呼び出し model.myFunc() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } } class RxSwiftSample { private let subject = PublishSubject<Int>() private var data = 0 var event : Observable<Int>{ return subject } func myFunc(){ // 処理 data = data + 1 // イベント発行 subject.onNext(self.data) } }
Lovly Swift!!!