diff --git a/Example/PaginationViewController.swift b/Example/PaginationViewController.swift index 4890aff..785a3aa 100644 --- a/Example/PaginationViewController.swift +++ b/Example/PaginationViewController.swift @@ -63,6 +63,7 @@ final class PaginationViewModel { private let nearBottomObserver: Signal.Observer private let retryObserver: Signal.Observer + private let stateProperty: Property let movies: Property<[Movie]> let errors: Property let refreshing: Property @@ -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(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.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 } diff --git a/Example/ViewController.swift b/Example/ViewController.swift index a2b8812..e1627fc 100644 --- a/Example/ViewController.swift +++ b/Example/ViewController.swift @@ -42,6 +42,7 @@ class ViewController: UIViewController { } final class ViewModel { + private let state: Property let counter: Property init(increment: Signal, decrement: Signal) { @@ -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) } } diff --git a/ReactiveFeedback.xcodeproj/project.pbxproj b/ReactiveFeedback.xcodeproj/project.pbxproj index 78c2a33..5c62dfa 100644 --- a/ReactiveFeedback.xcodeproj/project.pbxproj +++ b/ReactiveFeedback.xcodeproj/project.pbxproj @@ -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, ); }; }; @@ -98,6 +100,7 @@ 25E1D2371F56091A00D90192 /* PaginationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaginationViewController.swift; sourceTree = ""; }; 5898B6D01F97ADDD005EEAEC /* SystemTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SystemTests.swift; sourceTree = ""; }; 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 = ""; }; 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 = ""; }; 9AFA21251F9511A5001DBF7C /* ReactiveFeedback.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ReactiveFeedback.podspec; sourceTree = ""; }; @@ -150,6 +153,7 @@ children = ( A95097E70D3CBFF05FA7B8CC /* Feedback.swift */, A9509880213192F0D80EC2B3 /* SignalProducer+System.swift */, + 9AD5D42C1F97375E00E6AE5A /* Property+System.swift */, 25CC87B11F92855300A6EBFC /* Info.plist */, ); path = ReactiveFeedback; @@ -359,6 +363,7 @@ files = ( A9509BE4551098F4A5503820 /* Feedback.swift in Sources */, A950943401765BB90FA846B2 /* SignalProducer+System.swift in Sources */, + 9AD5D42D1F97375E00E6AE5A /* Property+System.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ReactiveFeedback/Property+System.swift b/ReactiveFeedback/Property+System.swift new file mode 100644 index 0000000..aabbf44 --- /dev/null +++ b/ReactiveFeedback/Property+System.swift @@ -0,0 +1,28 @@ +import ReactiveSwift + +extension Property { + public convenience init( + initial: Value, + scheduler: Scheduler = QueueScheduler.main, + reduce: @escaping (Value, Event) -> Value, + feedbacks: [Feedback] + ) { + 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( + initial: Value, + scheduler: Scheduler = QueueScheduler.main, + reduce: @escaping (Value, Event) -> Value, + feedbacks: Feedback... + ) { + self.init(initial: initial, scheduler: scheduler, reduce: reduce, feedbacks: feedbacks) + } +} diff --git a/ReactiveFeedback/SignalProducer+System.swift b/ReactiveFeedback/SignalProducer+System.swift index 4a8b6fb..76eae7a 100644 --- a/ReactiveFeedback/SignalProducer+System.swift +++ b/ReactiveFeedback/SignalProducer+System.swift @@ -10,7 +10,7 @@ extension SignalProducer where Error == NoError { feedbacks: [Feedback] ) -> SignalProducer { return SignalProducer.deferred { - let (state, observer) = Signal.pipe() + let (state, stateObserver) = Signal.pipe() let events = feedbacks.map { feedback in return feedback.events(scheduler, state) @@ -19,7 +19,7 @@ extension SignalProducer where Error == NoError { return SignalProducer(Signal.merge(events)) .scan(initial, reduce) .prefix(value: initial) - .on(value: observer.send(value:)) + .on(value: stateObserver.send(value:)) } } @@ -30,11 +30,8 @@ extension SignalProducer where Error == NoError { ) -> SignalProducer { return system(initial: initial, reduce: reduce, feedbacks: feedbacks) } -} -extension SignalProducerProtocol { - static func deferred(_ signalProducerFactory: @escaping () -> SignalProducer) -> SignalProducer { - return SignalProducer(value: ()) - .flatMap(.merge, signalProducerFactory) + private static func deferred(_ producer: @escaping () -> SignalProducer) -> SignalProducer { + return SignalProducer { $1 += producer().start($0) } } }