I have the following scenario - I am using ReactiveSwift's Action
to trigger a network request in my app. This network request is potentially expensive due to the processing that is done on it's response. So, when a caller tries to apply the Action I would like to do the following:
- Determine if the Action is already in progress
- If it is, return a SignalProducer that observes the results of the in progress Action
- If it is not, return a SignalProducer that will apply the action when started
Ideally the solution would be thread safe, as callers may try to apply the Action
from different threads.
Now I've managed to cobble together something that works using examples of caching in ReactiveSwift, but I'm almost certain I'm doing something wrong particularly in how I'm having to reset my MutableProperty
to nil
when the Action
completes. Note that I'm also using static variables to ensure my multiple instances of the UseCase
can't bypass my intended behaviour. Also, my example signals Never
output but in the real world they may:
class UseCase {
private static let sharedAction = Action<Void, Never, AnyError> {
return SignalProducer.empty.delay(10, on: QueueScheduler.main).on(completed: {
print("Done")
UseCase.sharedProducer.value = nil
})
}
private static let sharedProducer = MutableProperty<SignalProducer<Never, AnyError>?>(nil)
func sync() -> SignalProducer<Never, AnyError> {
let result = UseCase.sharedProducer.modify { value -> Result<SignalProducer<Never, AnyError>, NoError> in
if let inProgress = value {
print("Using in progress")
return Result(value: inProgress)
} else {
print("Starting new")
let producer = UseCase.sharedAction.apply().flatMapError { error -> SignalProducer<Never, AnyError> in
switch error {
case .disabled: return SignalProducer.empty
case .producerFailed(let error): return SignalProducer(error: error)
}
}.replayLazily(upTo: 1)
value = producer
return Result(value: producer)
}
}
guard let producer = result.value else {
fatalError("Unexpectedly found nil producer")
}
return producer
}
}