開発環境
- XCode7.3
- Swift2.2
- ReactiveCocoa4
Reactiveとは
ここが分かりやすかった
古くて新しいリアクティブプログラミングのすすめ-Frontrend Conference
最初に紹介したReactiveについての解説よりは実装的な解説があって分かりやすい。ObjC、SwiftではなくRxJSというJavaScriptのフレームワークを使って解説しているけど。
「RxJS」初心者入門 – JavaScriptの非同期処理の常識を変えるライブラリ
結論としては、MVVMというアーキテクチャを実装するのに使われるのがReactive・・・で合ってるかな?
iOSアプリで実装するには
選択肢は大雑把には二つ
- ReactiveCocoa
- RxSwift
RxSwiftより前に知ってたので、ReactiveCocoaを選択。
ReactiveCocoaとは
iOSアプリ開発界では先発組だったけど、ObjCからSwiftへの転換は比較的最近。なので資料(googleでの検索結果)が圧倒的にObjC時代のが多く、今のSwiftでのやり方がすぐに見つからない場合もあります。
導入方法
手動、Pods install、Carthageと色々あるけど今回はCarthageにしました。
Pods installと似てるけど、Carthageの方がプロジェクトに対する影響が小さいようです。こちらの方が好みですね
Carthageの具体的な使い方は以下を参照
Carthageを使ってビルド時間を短縮しよう
ReactiveCocoa <- ReadMeにやり方載ってます
実装
お手頃なのが無かったので、とりあえず先日作ったAVCam-iOSをReactiveCocoaを使って作り直してみました。
先に結論を言っておくと、あまり適当な例では無かったw
作ったものはgithubにあげてあります。
やり方がすぐにわからなかったのをピックアップ。
例えば以下のようなボタンのタップに対するActionをReactiveCocoaで実装しようとすると
@IBAction func resumeInterruptedSession(sender: AnyObject) {
dispatch_async(self.sessionQueue, {
self.session.startRunning()
self.sessionRunning = self.session.running
if(!self.session.running){
dispatch_async(dispatch_get_main_queue(), {
let message = NSLocalizedString("Unable to resume", comment: "Alert message when unable to resume the session running")
let alertController = UIAlertController(title: "AVCam", message: message, preferredStyle:.Alert)
let cancleAction = UIAlertAction(title: NSLocalizedString("OK", comment: "Alert OK button"), style: .Cancel, handler: nil)
alertController.addAction(cancleAction)
self.presentViewController(alertController, animated: true, completion: nil)
})
} else {
dispatch_async(dispatch_get_main_queue(), {
self.resumeButton.hidden = true
})
}
})
}
こういう形になる
let resumeInterruptedSessionAction = Action<AnyObject, Void, NSError>{ _ in
dispatch_async(self.sessionQueue, {
self.session.startRunning()
self.sessionRunning = self.session.running
if(!self.session.running){
dispatch_async(dispatch_get_main_queue(), {
let message = NSLocalizedString("Unable to resume", comment: "Alert message when unable to resume the session running")
let alertController = UIAlertController(title: "AVCam", message: message, preferredStyle:.Alert)
let cancleAction = UIAlertAction(title: NSLocalizedString("OK", comment: "Alert OK button"), style: .Cancel, handler: nil)
alertController.addAction(cancleAction)
self.presentViewController(alertController, animated: true, completion: nil)
})
} else {
dispatch_async(dispatch_get_main_queue(), {
self.resumeButton.hidden = true
})
}
})
return SignalProducer.empty
}
resumeButton.addTarget(resumeInterruptedSessionAction.unsafeCocoaAction, action: CocoaAction.selector, forControlEvents: .TouchUpInside)
ジェスチャーの場合は以下のようにしてたものを
@IBAction func focusAndExposeTap(gestureRecognizer: UITapGestureRecognizer) {
let devicePoint = (self.previewView.layer as! AVCaptureVideoPreviewLayer).captureDevicePointOfInterestForPoint(gestureRecognizer.locationInView(gestureRecognizer.view))
self.focusWithMode(AVCaptureFocusMode.AutoFocus, exposeWithMode: AVCaptureExposureMode.AutoExpose, atDevicePoint: devicePoint, motiorSubjectAreaChange: true)
}
このように変更する
let gesRect = UITapGestureRecognizer()
self.previewView.addGestureRecognizer(gesRect)
gesRect.rac_gestureSignal().toSignalProducer().map({ (x) -> CGPoint in
return (self.previewView.layer as! AVCaptureVideoPreviewLayer).captureDevicePointOfInterestForPoint(x!.locationInView(x!.view))
}).startWithNext { (pointInView) -> () in
self.focusWithMode(AVCaptureFocusMode.AutoFocus, exposeWithMode: AVCaptureExposureMode.AutoExpose, atDevicePoint: pointInView, motiorSubjectAreaChange: true)
}
結論
まあお試しなんで良いも悪いもないのですが、今回のサンプルではまだReactiveのメリットを試しきれてないので、もう少し恩恵を受けやすい形のサンプルを用意して試してみたいと思います。
追記
参考になるサイト