ios reactive cocoa pipeline
TRANSCRIPT
Рак мозга или мозг рака
Раком можно все
1023
A for Architecture
•Application Layer
•Transport Layer
•Service Layer
•Model Layer
•ViewModel Layer
•View Layer
• Application Layer
•Transport Layer
•Service Layer
•Model Layer
•ViewModel Layer
•View Layer
Persistence Notifications Runtime
• Application Layer
•Transport Layer
•Service Layer
•Model Layer
•ViewModel Layer
•View Layer
NSNotification LocationUpdate
•Application Layer
• Transport Layer
•Service Layer
•Model Layer
•ViewModel Layer
•View Layer
Networking Networking policies
•Application Layer
• Transport Layer
•Service Layer
•Model Layer
•ViewModel Layer
•View Layer
HTTP.Request HTTP.Response HTTP.Error
•Application Layer
•Transport Layer
• Service Layer
•Model Layer
•ViewModel Layer
•View Layer
API Request Persistence Request Application Service wrapper
•Application Layer
•Transport Layer
• Service Layer
•Model Layer
•ViewModel Layer
•View Layer
Update request Value Object Service Object
•Application Layer
•Transport Layer
•Service Layer
• Model Layer
•ViewModel Layer
•View Layer
Business logic Logic state
•Application Layer
•Transport Layer
•Service Layer
• Model Layer
•ViewModel Layer
•View Layer
Model Object
•Application Layer
•Transport Layer
•Service Layer
•Model Layer
• ViewModel Layer
•View Layer
Application State Presentation Logic
•Application Layer
•Transport Layer
•Service Layer
•Model Layer
• ViewModel Layer
•View Layer
Bindings Presentables
•Application Layer
•Transport Layer
•Service Layer
•Model Layer
•ViewModel Layer
• View LayerPresentation UI State
•Application Layer
•Transport Layer
•Service Layer
•Model Layer
•ViewModel Layer
• View LayerPresenters UIObjects Foundation Types
Data Flow
1024
Data + Data Request Flow
Transport Layer
Transport Layer
Transport Layer + Errors
Transport Layer + Binding
And now?
RAC• Signal
• SignalProducer
• Action
• Property
• Result
RAC• Signal
• SignalProducer
• Action
• Property
• Result
• Pipeline
• Future Task
• Future Builder
• Reactive State
• Enum<Value | Error>
RAC• Signal
• SignalProducer
• Action
• Property
• Result
• NSNotificationCenter
• startWithCompletion:
• startWithInput:Completions
• value + KVO
• completions(result, error)
RAC + Transforms• Signal -> Signal - Operator
• Signal.map(transform)
• Signal.retry(3)
• Signal -> Signal -> Signal - pipeline
- retry(map(signal, transform), 3)
✓ signal.map(transform).retry(3)
RAC + (flat)map
Transport Layer + RAC
Transport Layer + RAC
let requestAction = Action<URLRequest, URLResponse, Error> { .../*future task*/ } let http = Action<Request, Response, Error> { request in return Result<Request, Error>(value: request) .flatMap(serialize) //Result<JSON, Error> .flatMap(requestBuilder(forConfiguration: configuration)) //Result<URLRequest, Error> .map(SignalProducer.init) //SignalProducer<URLRequest, Error> .flatMap(.Latest, transform: requestAction.apply) //SignalProducer<URLResponse, Error> .attemptMap(deserialize) //SignalProducer<Response Error }
Заметьте - эта штука вполне может быть общей вне зависимости от входных и выходных данных засчет generic
Build Your Own RAC
extension SignalType { /// Returns a signal that will yield an array of values when `self` completes. @warn_unused_result(message="Did you forget to call `observe` on the signal?") public func collect() -> Signal<[Value], Error> { return self .reduce(CollectState()) { $0.append($1) } .map { $0.values } } }
Transport Layer + RAC
let httpWithPolicy = Action<Request, Response, Error> { request in http.apply(request) .retry(3) .suspendOn(enterBackgroundSignal: background, enterForeground: foreground) }
Service Layer
Service Layer
Service Layer + RAC
let service = Action<Number, Image, Error> { number in let image = cacheService.inquire .apply(number) .concat { xkcdImageHTTP.apply(number) }
let description = xkcdDescriptionHTTP.apply(number)
return combineLatest(image, description).map { ImageWithDescription(image: $0, description: $1) } }
Model Layer
Model Layer
Model Layer + RAC
struct State { let number: Int let value: AnyProperty<ImageWithDescription?>
init(number: Int) { self.number = number self.value = AnyProperty(value: nil, producer: service.apply(number)) } }
let state = MutableProperty(State(number: 100)) let increment = { number.value = State(number: number.value.number + 1) }
let exposedState = AnyProperty(state)
View Model Layer
View Model Layer
VM Layer + RAC let image = model.exposedState .flatMap { $0.value.image } .map { $0 ?? imagePlaceholder }
let description = model.exposedState .flatMap { $0.value.description } .map { $0 ?? "Loading..." }
let rightButtonAction = model.increment
let present = { presenters in presenters.rightButtonAction <~ rightButtonAction presenters.description <~ description.producer presenters.image <~ description.image }
View Layer
View + RAC
class View { //{ self.imageView.image = $0 } let image: Presenter<UIImage> = self.imageView.imagePresenter //{ self.descriptionLabel.value = $0 } let description: Presenter<String> = self.descriptionLabel.textPresenter //{ self.button.clickedSignal.observe($0) } let rightButton: Presenter<() -> ()> = self.button.clickActionPresenter }
Hm…
Benefits?• Consistency
• Observability
• Testability
• Encapsulation
• Completeness
Examples?Popup Action
Navigation Property + Actions
UIKit Sinks + Presentables
Login Form Action + Operators
What’s next?
• RXMarbles (http://rxmarbles.com/)
• Reactive Cocoa sources - all here
Questions?