• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
var RAC3 = ReactiveCocoa + Swift
 

var RAC3 = ReactiveCocoa + Swift

on

  • 265 views

2014-07-26(土)に開催された「ReactiveCocoa勉強会関西【Cocoa勉強会関西特別編】」の発表資料です。

2014-07-26(土)に開催された「ReactiveCocoa勉強会関西【Cocoa勉強会関西特別編】」の発表資料です。

https://atnd.org/events/53540

Statistics

Views

Total Views
265
Views on SlideShare
260
Embed Views
5

Actions

Likes
0
Downloads
1
Comments
0

1 Embed 5

https://twitter.com 5

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    var RAC3 = ReactiveCocoa + Swift var RAC3 = ReactiveCocoa + Swift Presentation Transcript

    • var$RAC3$=$Reac,veCocoa$+$Swi2 @ikesyo #rac_kansai Reac%veCocoa勉強会関西)2014.07.26)Sat)@はてな京都オフィス
    • @ikesyo いけしょー/池田翔 京都でフリーランスのiOS/Android エンジニアしています 甘いもの大好き!! お仕事待ってます!
    • Reac%veCocoaのコミッター(Contributor)やってます
    • 今日はSwi$ベースになった Reac%veCocoa)3.0のご紹介
    • ※!注意 2014%07%25,)Xcode)6)beta)4時点の内容に基づきます APIやクラス名など今後変更される可能性は大いにあります
    • RAC$3.0
    • 元々はこれ h"ps://github.com/Reac3veCocoa/ Reac3veCocoa/pull/966
    • _人人人人人人人_ > 突然のSwi$ <  ̄Y^Y^Y^Y^Y^Y ̄
    • だけではなく 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
    • h"ps://github.com/jspahrsummers/ RxSwi8 を経て
    • The$Great$Swi,ening$(a.k.a.$the$ new$3.0) h"ps://github.com/Reac3veCocoa/ Reac3veCocoa/pull/1382
    • New$Concepts • Generics)Support • Producer • Consumer • Signal • SignalingProperty • Promise • Action
    • Generics(Support 型パラメータは当然サポート public struct Producer<T> {} public final class Consumer<T>: Sink {} extension NSNotificationCenter { public func rac_notifications (name: String? = nil, object: AnyObject? = nil) -> Signal<NSNotification?> } extension NSURLSession { public func rac_dataProducerWithRequest (request: NSURLRequest) -> Producer<(NSData, NSURLResponse)> }
    • Producer • Cold&RACSignal:&Event&Stream • Consumerをアタッチしてイベントをproduceする • Consumer毎に受け取るイベントやタイミングは異なる • ConsumerがDisposableを持っていて、Producer生成時の クロージャではDisposableを返さない • consumer.disposable.addDisposable(...)
    • Producer.swi, public struct Producer<T> { public init(produce: Consumer<T> -> ()) // +[RACSignal createSignal:] public static func empty() -> Producer<T> // +[RACSignal empty] public static func single(value: T) -> Producer<T> // +[RACSignal return:] public static func error(error: NSError) -> Producer<T> // +[RACSignal error:] public static func never() -> Producer<T> // +[RACSignal never] // -[RACSignal subscribe:] public func produce(consumer: Consumer<T>) -> Disposable public func produce(consumer: Event<T> -> ()) -> Disposable // -[RACSignal subscribeNext:error:completed:] public func produce( next: T -> (), error: NSError -> (), completed: () -> () ) -> Disposable }
    • Producerの生成例 extension RACSignal { public func asProducer() -> Producer<AnyObject?> { return Producer { consumer in let next = { (obj: AnyObject?) -> () in consumer.put(.Next(Box(obj))) } let error = { (maybeError: NSError?) -> () in let nsError = maybeError.orDefault(RACError.Empty.error) consumer.put(.Error(nsError)) } let completed = { consumer.put(.Completed) } let disposable: RACDisposable? = self.subscribeNext(next, error: error, completed: completed) consumer.disposable.addDisposable(disposable) } } }
    • Consumer • like:'id<RACSubscriber> subscriber • SinkプロトコルのputにEvent<T>でラップした値を渡す • public func put(event: Event<T>) {} • 従来の'-sendNext:'などの代わり • Producerのproduceメソッドに'Event<T> -> ()'のクロー ジャをConsumerとして渡せる
    • Consumer.swi, public final class Consumer<T>: Sink { public typealias Element = Event<T> public let disposable = CompositeDisposable() public init<S: Sink where S.Element == Event<T>>(_ sink: S) public convenience init(put: Event<T> -> ()) public convenience init( next: T -> () = emptyNext, error: NSError -> () = emptyError, completed: () -> () = emptyCompleted) public func put(event: Event<T>) }
    • Consumerの使用例 extension Producer { public func asDeferredRACSignal<U: AnyObject>(evidence: Producer<T> -> Producer<U?>) -> RACSignal { return RACSignal.createSignal { subscriber in let selfDisposable = evidence(self).produce { event in switch event { case let .Next(obj): subscriber.sendNext(obj) case let .Error(error): subscriber.sendError(error) case let .Completed: subscriber.sendCompleted() } } return RACDisposable { selfDisposable.dispose() } } } }
    • 補足1 • evidenceという型チェックにより、特定の型パラメータを持 つProducerでのみメソッドを呼べるように制限 • Objec've)CとのブリッジなのでStructやEnumは渡せず、 AnyObjectでクラスオブジェクトだけに制限 • 使い方:,引数に,identityという関数を渡すだけ
    • 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 }
    • 補足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
    • Signal • Hot%RACSignal:%Behaviour%in%other%FRP • CompletedやErrorによる終了はなく、常に現在の値を持つ • T -> ()%のクロージャがオブサーバー%SinkOf<T>%となって observeする • ProducerのConsumerと異なり、全てのオブザーバーが同 じイベントを同じタイミングで受け取る • observeを開始した時点で現在値が通知される
    • Signal.swi* public final class Signal<T> { public var current: T public init(initialValue: T, generator: SinkOf<T> -> ()) public class func constant(value: T) -> Signal<T> public func observe<S: Sink where S.Element == T> (observer: S) -> Disposable public func observe(observer: T -> ()) -> Disposable }
    • Signalの生成例 extension NSNotificationCenter { public func rac_notifications (name: String? = nil, object: AnyObject? = nil) -> Signal<NSNotification?> { let disposable = ScopedDisposable(SerialDisposable()) return Signal(initialValue: nil) { sink in let observer = self.addObserverForName(name, object: object, queue: nil) { notification in sink.put(notification) } disposable.innerDisposable.innerDisposable = ActionDisposable { self.removeObserver(observer) } return () } } }
    • Signal/Observer(SinkOf<T>)の使用例 extension Signal { /// Creates a "hot" 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 send the observable's current /// value, then all changes thereafter. The signal will never complete or /// error, so it must be disposed manually. public func asInfiniteRACSignal<U: AnyObject>(evidence: Signal<T> -> Signal<U?>) -> RACSignal { return RACSignal.createSignal { subscriber in evidence(self).observe { value in subscriber.sendNext(value) } return nil } } }
    • SignalingProperty • 値の変更をSignalとして通知することができるプロパティ用 オブジェクト • KVOの代替手段:&Swi*のクラスにはKVOがなく、監視される側 が自分から通知できるように公開する • func __conversion()&によりラップされた値を透過的に使 用することができる
    • SignalingProperty.swi1 public final class SignalingProperty<T>: Sink { public typealias Element = T public let signal: Signal<T> // setするとsignalのオブザーバーに通知される public var value: T public init(_ defaultValue: T) public func __conversion() -> T public func __conversion() -> Signal<T> public func put(value: T) }
    • SignalingPropertyの使用例 public class Hoge { var name: SignalingProperty<String> = SignalingProperty("") } public func printName(name: String) { println(name) } let hoge = Hoge() // Stringが必要なのでSignalingProperty<String>から現在の値が取り出される printName(hoge.name) hoge.name.signal.observe { name in println("(name): name was changed!") } hoge.name.put("ikesyo")
    • Promise • 単一の値を生成(resolve)する遅延タスクを表現する • ProducerのようなEvent-Streamではないので複数の値を通知 したりはしない • 値は-public let signal: Signal<T?>-でSignalとして参 照できる • resolve前:-nil,-resolve後:-生成された値-を通知
    • Promise.swi* public final class Promise<T> { public let signal: Signal<T?> // 生成時のクロージャの引数の`sink`に`put`することでresolveされる。 // 二度目以降の`put`は無視される。 public init(action: SinkOf<T> -> ()) public func start() -> Promise<T> public func await() -> T // Promiseのチェーン public func then<U>(action: T -> Promise<U>) -> Promise<U> }
    • Promiseの使用例 public final class Signal<T> { public func firstPassingTest(pred: T -> Bool) -> Promise<T> { return Promise { sink in self.take(1).observe { value in if pred(value) { sink.put(value) } } return () } } }
    • Ac#on • RACCommandの置き換え • インプットからアウトプットを返すアクション(主にUI用:&デフ ォルトでメインスレッドで動作)の実行・結果を提供する • 個別のアクション実行の結果はPromise<Result<T>で返す • 結果のチェックはSignal<Result<T>?>なのでobserveで • 実行結果の値があるのでアクションの合成(チェーン)が可能
    • Ac#on.swi* public final class Action<Input, Output> { public typealias ExecutionSignal = Signal<Result<Output>?> // RACCommand.executionSignalsと同様 public let executions: Signal<ExecutionSignal?> public var results: Signal<Result<Output>?> // 実行結果 public var executing: Signal<Bool> // 実行中かどうか public let enabled: Signal<Bool> // 有効かどうか public var values: Signal<Output?> // 成功結果 public var errors: Signal<NSError?> // 失敗結果 // アクションとなるクロージャを渡す public init(enabledIf: Signal<Bool>, scheduler: Scheduler = MainScheduler(), execute: Input -> Promise<Result<Output>>) public func execute(input: Input) -> ExecutionSignal // アクションの実行 public func then<NewOutput> // アクションの合成 (action: Action<Output, NewOutput>) -> Action<Input, NewOutput> }
    • Q&A?
    • ありがとうございました!