-
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
Subscribing to two signals but one only executes once #1176
Comments
I have thought of this same problem before but my approach to thinking of it was a little different. I thought it would be cleaner to subscribe to the cache only and then let the cache do a network request if needed. The cache would then subscribe to the network call and update itself. That way new data from the network would end up in the cache first. That way the UI, ViewController, or ViewModel would only have to know how to communicate with a cache. |
|
Thanks for your feedback @KyleLeneau. The approach I'm taking is the way it is because I'm using MMRecord to perform the network request and update the cache for me (it updates CoreData behind the scenes), which is all wrapped in it's own signal. The "illustrated scenario" above doesn't really give the full picture. What I really have going is a method in a data manager that returns a signal that my ViewModel subscribes to. That method instantiates the cache and network signals, combines them into a single signal (by using doSignal:count:followedBy:) and returns that signal back to the caller. So really that method is acting like the "cache" in your situation but it's just breaking apart the actual act of retrieving from cache and performing a network request/saving to cache. The goal behind this method is for my ViewModel to subscribe to it, get an initial set of data to populate a table, then immediately fire off a network request to get the latest data. Later after a certain action occurs (refresh the table view) the signal will react by grabbing new data and pushing it onto the signal. @Coneko I will take a look now to see if I can get that to work! |
@Coneko |
Great! I'll go ahead and close this but feel free to open it again if something else related to this comes up. Of course if something unrelated to this comes up you can always open a new issue. |
Actually I just tested it and it doesn't work, and it's probably because I have a fundamental misunderstanding of RAC. Maybe you could help. I've expanded the example below: Here we setup the signal using
Then immediately following I setup a binding on self.messages. It's triggered by self.refresh's value changing. When it does I map the signal.
At this point the signal is subscribed to and it's triggered right away. The console produces:
To demonstrate the issue I invoked the signal again by setting the refresh value to
Which didn't produce the results I expected. What I got was an initial return from cache then the network request.
At this point I only want the network signal to be triggered and only produce:
This works correctly with my |
Because signals in RAC are generally cold: it means they don't do anything until you subscribe to them, and they do what they do every time you subscribe to them. They're stateless by design. You just have to combine them differently, for example: RAC(self, messages) = [[[RACObserve(self, refresh) mapReplace:cachedSignal]
takeUntilReplacement:[RACObserve(self, refresh) mapReplace:networkSignal]]
swithToLatest]; |
That doesn't seem to work for me. The response I'm getting is:
It seems that the cached signal is getting immediately replaced by the network signal. Also any subsequent refreshes doesn't cause anything else to happen, no network or cached request, nothing. Also this approach seems to couple the caller very tightly to the data access implementation. Is it not necessarily possible (or advised) to have a signal that wraps two signals and knows under what conditions to invoke one, the other, or both? Consider this is a method in some data manager:
Then my viewController (for sake of clarity) will invoke this method to obtain a new signal and subscribe to it however it pleases:
Is this type of approach wrong in the RAC world? I just seems much cleaner from the highest level to be able to subscribe to a single signal and leave the implementation details of knowing when to use a caching signal vs. a networking signal to something lower. |
Yes sorry, what I wrote there was wrong, it should have been: RACSignal *refreshFromCacheSignal = [[RACObserve(self, refresh)
map:^(id value) {
return cachedSignal;
}]
switchToLatest];
RACSignal *refreshFromNetworkSignal = [[RACObserve(self, refresh)
map:^(id value) {
return networkSignal;
}]
switchToLatest];
RAC(self, messages) = [refreshFromCacheSignal
takeUntilReplacement:refreshFromNetworkSignal]; Basically the If you want to display the cached data immediately and have refreshes get the network data bypassing the cache it's much cleaner: RACSignal *refreshFromNetworkSignal = [[RACObserve(self, refresh)
map:^(id value) {
return networkSignal;
}]
switchToLatest];
RAC(self, messages) = [cachedSignal
takeUntilReplacement:refreshFromNetworkSignal]; not because it's necessarily a cleaner way of doing it generally speaking, but because
Generally cold signals are best, so we don't like introducing state in our signals. I would prefer @KyleLeneau's approach if you wanted to abstract data access. |
I see! This really helped shed light on the underlying concepts of RAC. I've taken the approach of letting my view model deal with observing a property on itself (self.refresh):
Then in my view controller:
Thanks for your help @Coneko and @KyleLeneau! |
No problem, glad I could be of help! |
I'm trying to understand the best way to implement the following scenario:
Let's say I have two signals, one that returns cached data and one that returns data from the network. What I'm looking to do is subscribe to a signal that 'next's cached data immediately followed by 'next'ing data from the network request. Then any subsequent subscriptions on the signal are only sent data from the network since the cached data is no longer relevant.
I've taken a stab at this but I'd love feedback on whether there's a more declarative approach to knowing that the cached data signal has been executed once.
This illustrates the scenario:
And this is the implementation of
doSignal:count:followedBy:
The text was updated successfully, but these errors were encountered: