Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

var RAC3 = ReactiveCocoa + Swift @ ReactiveCocoa Tokyo #rac_tokyo 10/18

3,789 views

Published on

2014-10-18(土)に開催された「ReactiveCocoa Tokyo #rac_tokyo」の発表資料です。

http://connpass.com/event/8680/

Published in: Engineering
  • Be the first to comment

var RAC3 = ReactiveCocoa + Swift @ ReactiveCocoa Tokyo #rac_tokyo 10/18

  1. 1. var$RAC3$=$Reac,veCocoa$+$Swi2 @ikesyo #rac_tokyo Reac%veCocoa)Tokyo)2014110118)Sat)@freee株式会社
  2. 2. @ikesyo いけしょー/池田翔 京都でフリーランスのiOS/Android エンジニアしています 甘いもの大好き!! !!お仕事待ってます!!! 現在フリュー㈱にて 一部RAC3を使ってお仕事中!
  3. 3. Reac%veCocoaのコミッター(Contributor)やってます
  4. 4. 今日はSwi$ベースになった Reac%veCocoa)3.0のご紹介
  5. 5. ※!注意 2014%10%16,(Xcode(6.1(GM(seed(2時点,( swi4%api%reduxブランチ(#1532)の内容に基づきます APIやクラス名など今後変更される可能性は大いにあります ※!実際に7/26の!#rac_kansai!から大きく変わっています……!※
  6. 6. RAC$3.0
  7. 7. 元々はこれ h"ps://github.com/Reac3veCocoa/ Reac3veCocoa/pull/966
  8. 8. _人人人人人人人_ > 突然のSwi$ < ‾Y^Y^Y^Y^Y^Y‾
  9. 9. だけではなく RACDC%2014:%June%3rd@Github • The$Future$Of$Reac.veCocoa$by$Jus.n$Spahr9Summers$•$ GitHub$Reac.ve$Cocoa$Developer$Conference • h#p://vimeo.com/98100163 • h#ps://github.com/jspahrsummers/the<future<of< reac>vecocoa
  10. 10. h"ps://github.com/jspahrsummers/ RxSwi8 を経て
  11. 11. The$Great$Swi,ening$(a.k.a.$the$ new$3.0) h"ps://github.com/Reac3veCocoa/ Reac3veCocoa/pull/1382
  12. 12. New$Concepts • Generics)Support • ColdSignal)!)Producer)! • Subscriber)!)Consumer)! • HotSignal)!)Signal)! • ObservableProperty)!)SignalingProperty)! • !)Promise)! • Action
  13. 13. Generics(Support 型パラメータは当然サポート public struct ColdSignal<T> {} public final class Subscriber<T>: SinkType {} extension NSNotificationCenter { public func rac_notifications (name: String? = nil, object: AnyObject? = nil) -> HotSignal<NSNotification> } extension NSURLSession { public func rac_dataWithRequest (request: NSURLRequest) -> ColdSignal<(NSData, NSURLResponse)> }
  14. 14. ColdSignal • Cold&RACSignal:&Event&Stream • Subscriberをアタッチしてイベントを購読する • Subscriber毎に受け取るイベントやタイミングは異なる • SubscriberがDisposableを持っていて、ColdSignal生成 時のクロージャではDisposableを返さない • subscriber.disposable.addDisposable(...)
  15. 15. ColdSignal.swi-public struct ColdSignal<T> { public init(generator: Subscriber<T> -> ()) // +[RACSignal createSignal:] public static func empty() -> ColdSignal // +[RACSignal empty] public static func single(value: T) -> ColdSignal // +[RACSignal return:] public static func error(error: NSError) -> ColdSignal // +[RACSignal error:] public static func never() -> ColdSignal // +[RACSignal never] // -[RACSignal subscribe:] public func subscribe(subscriber: Subscriber<T>) -> Disposable // -[RACSignal subscribeNext:error:completed:] public func subscribe( next: T -> () = doNothing, error: NSError -> () = doNothing, completed: () -> () = doNothing ) -> Disposable }
  16. 16. ColdSignalの生成例 extension RACSignal { public func asColdSignal() -> ColdSignal<AnyObject?> { return ColdSignal { subscriber in let next = { (obj: AnyObject?) -> () in subscriber.put(.Next(Box(obj))) } let error = { (maybeError: NSError?) -> () in let nsError = maybeError.orDefault(RACError.Empty.error) subscriber.put(.Error(nsError)) } let completed = { subscriber.put(.Completed) } let disposable: RACDisposable? = self.subscribeNext(next, error: error, completed: completed) subscriber.disposable.addDisposable(disposable) } } }
  17. 17. Subscriber • like:'id<RACSubscriber> subscriber • SinkTypeプロトコルのputにEvent<T>でラップした値を渡す • public func put(event: Event<T>) {} • 従来の'-sendNext:'などの代わり • Event<T> -> ()'のクロージャをハンドラー本体として Subscriberを初期化し、ColdSignalのsubscribeに渡せる
  18. 18. Subscriber.swi+ public final class Subscriber<T>: SinkType { public typealias Element = Event<T> public let disposable = CompositeDisposable() public init<S: SinkType where S.Element == Event<T>>(_ sink: S) public convenience init(handler: Event<T> -> ()) public convenience init( next: T -> (), error: NSError -> (), completed: () -> ()) public func put(event: Event<T>) }
  19. 19. Subscriberの使用例 extension ColdSignal { public func asDeferredRACSignal<U: AnyObject> (evidence: ColdSignal -> ColdSignal<U?>) -> RACSignal { return RACSignal.createSignal { subscriber in let selfDisposable = evidence(self).subscribe(next: { value in subscriber.sendNext(value) }, error: { error in subscriber.sendError(error) }, completed: { subscriber.sendCompleted() }) return RACDisposable { selfDisposable.dispose() } } } }
  20. 20. 補足1 • evidenceという型チェックにより、特定の型パラメータを持 つColdSignalでのみメソッドを呼べるように制限 • Objec've)CとのブリッジなのでStructやEnumは渡せず、 AnyObjectでクラスオブジェクトだけに制限 • 使い方:,引数に,identityという関数を渡すだけ
  21. 21. Iden%ty.swi, /// The identity function, which returns its argument. /// /// This can be used to prove to the typechecker that a given type A is /// equivalent to a given type B. /// /// For example, the following global function is normally impossible to bring /// into the `Signal<T>` class: /// /// func merge<U>(signal: Signal<Signal<U>>) -> Signal<U> /// /// However, you can work around this restriction using an instance method with /// an “evidence” parameter: /// /// func merge<U>(evidence: Signal<T> -> Signal<Signal<U>>) -> Signal<U> /// /// Which would then be invoked with the identity function, like this: /// /// signal.merge(identity) /// /// This will verify that `signal`, which is nominally `Signal<T>`, is logically /// equivalent to `Signal<Signal<U>>`. If that's not actually the case, a type /// error will result. public func identity<T>(id: T) -> T { return id }
  22. 22. 補足2 • 例えばScalaではGeneralized-Type-Constraintsという言語機能で 同様の制限が可能 • h6p://yuroyoro.hatenablog.com/entry/ 20100914/1284471301 • h6p://www.ne.jp/asahi/hishidama/home/tech/scala/ generics.html#hgeneralizedtype_constraints
  23. 23. HotSignal • Hot%RACSignal:%Push+driven%stream • CompletedやErrorによる終了はない • T -> ()%のクロージャがオブサーバー%SinkOf<T>%となって observeする • ColdSignalのSubscriberと異なり、全てのオブザーバー が同じイベントを同じタイミングで受け取る
  24. 24. HotSignal.swi-public final class HotSignal<T> { public init(_ generator: SinkOf<T> -> ()) public class func never() -> HotSignal public func observe<S: SinkType where S.Element == T> (observer: S) -> Disposable public func observe(next: T -> ()) -> Disposable }
  25. 25. HotSignalの生成例 extension NSNotificationCenter { public func rac_notifications (name: String? = nil, object: AnyObject? = nil) -> HotSignal<NSNotification> { let disposable = ScopedDisposable(SerialDisposable()) return HotSignal { sink in let observer = self.addObserverForName(name, object: object, queue: nil) { notification in sink.put(notification) } disposable.innerDisposable.innerDisposable = ActionDisposable { self.removeObserver(observer) } return () } } }
  26. 26. HotSignal/Observer(SinkOf<T>)の使用例 extension HotSignal { /// Creates a RACSignal that will forward values from the receiver. /// /// evidence - Used to prove to the typechecker that the receiver is /// a signal of objects. Simply pass in the `identity` function. /// /// Returns an infinite signal that will forward all values from the /// underlying HotSignal. The returned RACSignal will never complete or /// error, so it must be disposed manually. public func asInfiniteRACSignal<U: AnyObject> (evidence: HotSignal -> HotSignal<U?>) -> RACSignal { return RACSignal.createSignal { subscriber in evidence(self).observe { subscriber.sendNext($0) } return nil } } }
  27. 27. ObservableProperty • 値の変更をColdSignalとして通知することができるプロパテ ィ用オブジェクト • KVOの代替手段:&Swi*のクラスにはKVOがなく、監視される側 が自分から通知できるように公開するため? • !&func __conversion()&によりラップされた値を透過的に 使用することができる&!&Swi*&1.0でなくなりました!
  28. 28. ObservableProperty.swi1 public final class ObservableProperty<T> { public func values() -> ColdSignal<T> // setするとvalues()の戻り値のColdSignalのsubscriberに通知される public var value: T // not public: ミス or 他からの変換? init(_ value: T) /** public func __conversion() -> T public func __conversion() -> Signal<T> */ } extension ObservableProperty: SinkType { public func put(value: T) }
  29. 29. ObservablePropertyの使用例 public class Hoge { let name: ObservableProperty<String> = ObservableProperty("nanashi") } public func printName(name: String) { println(name) } let hoge = Hoge() printName(hoge.name.value) // => nanashi hoge.name.values().subscribe(next: { name in println("(name): name was changed!") }) // => nanashi: name was changed! : 最初に現在値が通知される hoge.name.value = "ikesyo" // or hoge.name.put("ikesyo") // by SinkType // => ikesyo: name was changed!
  30. 30. !!Promise!! • 単一の値を生成(resolve)する遅延タスクを表現する • ProducerのようなEvent-Streamではないので複数の値を通知 したりはしない • 値は-public let signal: Signal<T?>-でSignalとして参 照できる • resolve前:-nil,-resolve後:-生成された値-を通知
  31. 31. Ac#on • RACCommandの置き換え • インプットからアウトプットを返すアクション(主にUI用:&メイ ンスレッドで動作)の実行・結果を提供する • 個別のアクション実行の結果はColdSignal<T>で返す • 結果のチェックは戻り値がColdSignalなのでsubscribeで
  32. 32. Ac#on.swi* public final class Action<Input, Output> { // RACCommand.executionSignals相当は現時点でなし public let executing: ColdSignal<Bool> // 実行中かどうか public let enabled: ColdSignal<Bool> // 有効かどうか public let values: HotSignal<Output> // 成功結果 public let errors: HotSignal<NSError> // 失敗結果 // アクションとなるクロージャを渡す public init(enabledIf: HotSignal<Bool>, execute: Input -> ColdSignal<Output>) public func execute(input: Input) -> ColdSignal<Output> // アクションの実行 }
  33. 33. おまけ
  34. 34. Xcode&6.1,&iOS&7/8両対応で Swi$版のReac)veCocoaを組み込む 方法を共有します。
  35. 35. 1. git submodule add https://github.com/ ReactiveCocoa/ReactiveCocoa.git External/ ReactiveCocoa 2. (LlamaKitの微修正……) 3. LlamaKit.framework5/5Reac9veCocoa.framework5をビルドする 4. ビルドしたFrameworkをリンクする 5. Frameworkをアプリにコピーする 6. importして使うだけ!!
  36. 36. https://github.com/ikesyo/rac_tokyo_rac3_installation_sample
  37. 37. まとめ
  38. 38. !!RAC!3.0はまだ早い……!!
  39. 39. Q&A?
  40. 40. ありがとうございました!

×