Skip to content

Commit

Permalink
Conveniences for creating state machine as Property. (#11)
Browse files Browse the repository at this point in the history
* Initialise a state machine as `Property`.

* Add back `deferred`.
  • Loading branch information
andersio authored Oct 19, 2017
1 parent 5f6c7f2 commit 289a5be
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 27 deletions.
23 changes: 8 additions & 15 deletions Example/PaginationViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ final class PaginationViewModel {
private let nearBottomObserver: Signal<Void, NoError>.Observer
private let retryObserver: Signal<Void, NoError>.Observer

private let stateProperty: Property<State>
let movies: Property<[Movie]>
let errors: Property<NSError?>
let refreshing: Property<Bool>
Expand All @@ -88,24 +89,16 @@ final class PaginationViewModel {
Feedbacks.retryFeedback(for: retrySignal),
Feedbacks.retryPagingFeedback()
]
let initialState = State.initial
let stateProducer = SignalProducer.system(initial: initialState,
reduce: State.reduce,
feedbacks: feedbacks)
.observe(on: QueueScheduler.main)

let stateProperty = Property<State>(initial: initialState, then: stateProducer)
self.stateProperty = Property(initial: State.initial,
reduce: State.reduce,
feedbacks: feedbacks)

self.movies = Property<[Movie]>.init(initial: [], then: stateProperty.signal.filterMap {
$0.newMovies
})
self.movies = Property<[Movie]>(initial: [],
then: stateProperty.producer.filterMap { $0.newMovies })

self.errors = Property<NSError?>.init(initial: nil, then: stateProperty.producer.map {
$0.lastError
})
self.refreshing = stateProperty.map {
$0.isRefreshing
}
self.errors = stateProperty.map { $0.lastError }
self.refreshing = stateProperty.map { $0.isRefreshing }
self.nearBottomObserver = nearBottomObserver
self.retryObserver = retryObserver
}
Expand Down
10 changes: 5 additions & 5 deletions Example/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class ViewController: UIViewController {
}

final class ViewModel {
private let state: Property<Int>
let counter: Property<String>

init(increment: Signal<Void, NoError>, decrement: Signal<Void, NoError>) {
Expand All @@ -56,12 +57,11 @@ final class ViewModel {
return decrement.map { _ in Event.decrement }
}

let state = SignalProducer.system(initial: 0,
reduce: IncrementReducer.reduce,
feedbacks: incrementFeedback, decrementFeedback)
.map(String.init)
self.state = Property(initial: 0,
reduce: IncrementReducer.reduce,
feedbacks: incrementFeedback, decrementFeedback)

self.counter = Property(initial: "", then: state)
self.counter = state.map(String.init)

}
}
Expand Down
5 changes: 5 additions & 0 deletions ReactiveFeedback.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
5898B6D11F97ADDD005EEAEC /* SystemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5898B6D01F97ADDD005EEAEC /* SystemTests.swift */; };
9A4CCB0B1F95D5CA00ACF758 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A4CCB0C1F95D5CA00ACF758 /* Nimble.framework */; };
9A4CCB0D1F95D5D500ACF758 /* Nimble.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9A4CCB0C1F95D5CA00ACF758 /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
9AD5D42D1F97375E00E6AE5A /* Property+System.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD5D42C1F97375E00E6AE5A /* Property+System.swift */; };
9AE181B91F95A71B00A07551 /* ReactiveFeedbackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE181B81F95A71B00A07551 /* ReactiveFeedbackTests.swift */; };
9AE181BB1F95A71B00A07551 /* ReactiveFeedback.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 25CC87AE1F92855300A6EBFC /* ReactiveFeedback.framework */; };
9AE181C21F95A77500A07551 /* ReactiveSwift.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9AFA212A1F95135B001DBF7C /* ReactiveSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
9AE181C31F95A77500A07551 /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9AFA212B1F95135B001DBF7C /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
Expand Down Expand Up @@ -98,6 +100,7 @@
25E1D2371F56091A00D90192 /* PaginationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaginationViewController.swift; sourceTree = "<group>"; };
5898B6D01F97ADDD005EEAEC /* SystemTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SystemTests.swift; sourceTree = "<group>"; };
9A4CCB0C1F95D5CA00ACF758 /* Nimble.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; };
9AD5D42C1F97375E00E6AE5A /* Property+System.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Property+System.swift"; sourceTree = "<group>"; };
9AE181B61F95A71B00A07551 /* ReactiveFeedbackTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveFeedbackTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
9AE181BA1F95A71B00A07551 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
9AFA21251F9511A5001DBF7C /* ReactiveFeedback.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ReactiveFeedback.podspec; sourceTree = "<group>"; };
Expand Down Expand Up @@ -150,6 +153,7 @@
children = (
A95097E70D3CBFF05FA7B8CC /* Feedback.swift */,
A9509880213192F0D80EC2B3 /* SignalProducer+System.swift */,
9AD5D42C1F97375E00E6AE5A /* Property+System.swift */,
25CC87B11F92855300A6EBFC /* Info.plist */,
);
path = ReactiveFeedback;
Expand Down Expand Up @@ -359,6 +363,7 @@
files = (
A9509BE4551098F4A5503820 /* Feedback.swift in Sources */,
A950943401765BB90FA846B2 /* SignalProducer+System.swift in Sources */,
9AD5D42D1F97375E00E6AE5A /* Property+System.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
28 changes: 28 additions & 0 deletions ReactiveFeedback/Property+System.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import ReactiveSwift

extension Property {
public convenience init<Event>(
initial: Value,
scheduler: Scheduler = QueueScheduler.main,
reduce: @escaping (Value, Event) -> Value,
feedbacks: [Feedback<Value, Event>]
) {
let state = MutableProperty(initial)
state <~ SignalProducer.system(initial: initial,
scheduler: scheduler,
reduce: reduce,
feedbacks: feedbacks)
.skip(first: 1)

self.init(capturing: state)
}

public convenience init<Event>(
initial: Value,
scheduler: Scheduler = QueueScheduler.main,
reduce: @escaping (Value, Event) -> Value,
feedbacks: Feedback<Value, Event>...
) {
self.init(initial: initial, scheduler: scheduler, reduce: reduce, feedbacks: feedbacks)
}
}
11 changes: 4 additions & 7 deletions ReactiveFeedback/SignalProducer+System.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ extension SignalProducer where Error == NoError {
feedbacks: [Feedback<Value, Event>]
) -> SignalProducer<Value, NoError> {
return SignalProducer.deferred {
let (state, observer) = Signal<Value, NoError>.pipe()
let (state, stateObserver) = Signal<Value, NoError>.pipe()

let events = feedbacks.map { feedback in
return feedback.events(scheduler, state)
Expand All @@ -19,7 +19,7 @@ extension SignalProducer where Error == NoError {
return SignalProducer<Event, NoError>(Signal.merge(events))
.scan(initial, reduce)
.prefix(value: initial)
.on(value: observer.send(value:))
.on(value: stateObserver.send(value:))
}
}

Expand All @@ -30,11 +30,8 @@ extension SignalProducer where Error == NoError {
) -> SignalProducer<Value, Error> {
return system(initial: initial, reduce: reduce, feedbacks: feedbacks)
}
}

extension SignalProducerProtocol {
static func deferred(_ signalProducerFactory: @escaping () -> SignalProducer<Value, Error>) -> SignalProducer<Value, Error> {
return SignalProducer<Void, Error>(value: ())
.flatMap(.merge, signalProducerFactory)
private static func deferred(_ producer: @escaping () -> SignalProducer<Value, Error>) -> SignalProducer<Value, Error> {
return SignalProducer { $1 += producer().start($0) }
}
}

0 comments on commit 289a5be

Please sign in to comment.