-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RAC Principles: How best to start SignalProducer
only once?
#2924
Comments
Using Also, there's a race condition here, so you might want to use RAC's I'd also recommend splitting the actual API code into a separate method or class. That will make the code a little easier to read and separate the concerns a little better. struct API {
static func getUsers() -> SignalProducer<User, NSError> {
return SignalProducer<User, NSError> { observer, disposable in
let url = NSURL(string:"http://jsonplaceholder.typicode.com/users/1")!
let task = NSURLSession.sharedSession()
.dataTaskWithURL(url) { (data, response, error) in
if error != nil {
observer.sendFailed(NSError(domain:"", code:5, userInfo:nil))
} else {
let json = try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions())
let user = User(JSON: json)
print("Completed user request at \(NSDate())")
observer.sendNext(user)
observer.sendCompleted()
}
}
print("Started user request at \(NSDate())")
task.resume()
}
}
}
class UserManager {
static let sharedManager = UserManager()
private var requestProducer: Atomic<SignalProducer<User, NSError>?> = Atomic(nil)
func getUsers() -> SignalProducer<User, NSError> {
return requestProducer.modify { producer in
if let producer = producer {
print("There is already a request in progress so simply return a reference to its signal")
return producer
}
return API.getUsers()
.on(completed: {
self.requestProcuder.modifify { _ in nil }
})
.replayLazily()
}
}
} There still is state, but it's contained a little better. The RAC-iest way to do it would be to add a new operator that contains the state. extension SignalProducer {
func replayIfStarted() -> SignalProducer<Value, Error> {
let active: Atomic<SignalProducer<Value, Error>?> = nil
return active.modify { producer in
if let producer = producer {
return producer
}
return self
.on(completed: {
active.modify { _ in nil }
})
.replayLazily()
}
}
} I'd be sure to double check that against the RAC codebase, and also write some tests around the producer lifetime, to make sure I wrote it properly. |
Nice! And apologies for the code-barf on my part, I should have prefaced that block with a "this is not prod code" message 😄 I'll check this out when I'm in front of Xcode; appreciate all your insight. |
I'm going to close this. Feel free to reopen if you have questions after looking at it! |
👍 thanks Matt |
Here's another way to go about this problem: using final class UserManager {
/// The public API is as easy as observing this.
public let users: AnyProperty<[User]>
private let usersMutableProperty = MutableProperty<[User]>([])
init() {
self.users = AnyProperty(self.usersMutableProperty)
}
private func getUsers() -> SignalProducer<User, NSError> { }
public func requestUsers() {
/// This is not idea because multiple calls to this method would step onto one-another, just doing it like this for simplicity
self.usersMutableProperty <~ self.usersRequest()
}
} |
Hi - this came up just now during a discussion with a friend (cc @mgrebenets)
I have a
UserManager
which retrievesUser
objects from the network and wish to expose an interface on theUserManager
as follows:func getUsers() -> SignalProducer<User, ErrorType>
(I'm returning a
SignalProducer
as I want toflatMap
this network request with requests to other network resources).The catch is that I only want to
start
the network request wrapped up in theSignalProducer
if there's not a request currently in progress.My intuition to solve this went like this:
Buuuut the mutable state in the
requestSignal
feels really "Un-RAC". Would appreciate any pointers you could give on a solution which is more in line with the RAC principles.Cheers! 😀
The text was updated successfully, but these errors were encountered: