I have one signal that basically what it does is requesting for a configuration using NSRULSession. When I do a subscribeNext it does the request perfectly fine, however for the second time this request is not necessary anymore. How could I avoid it?
Your signal will do its work each time it is subscribed to unless you do something explicit to prevent that. It sounds like what you want here is the replayLast operator. This operator will cache the last emitted value of your signal and emit it when your signal is subscribed to again instead of redoing the initial work.
Read up on the 'replay' operators here:
http://spin.atomicobject.com/2014/06/29/replay-replaylast-replaylazily/
One time signal could be made via take: operator. You just need to pass an argument for the amount of times required to perform a signal. After such amount of executions this gateway will be closed completely and no more data will be passed in the subscribeNext: block. In your case this amount would be equal 1.
RACSignal *requestConfigurationSignal = ...
[[requestSignal
take:1]
subscribeNext:^(id value){
NSLog(#"Request in progress")
}]
Use a property and an action whose values are bound to that property. Then trigger the action only as needed to refresh the property's value.
Related
How can a buffered replay subject be implemented in ReactiveSwift?
I've looked at replayLazily(upTo:) operator of SignalProducer, and also the pipe() function of the Signal type, however I can't see a straightforward way of creating something equivalent to Rx ReplaySubject.
This brings up the following questions as well:
ReactiveSwift implements Subject with Signal.pipe(), however you can't specify a buffer for the pipe the same way you can for a Rx ReplaySubject. Are there any workarounds?
replayLazily(upTo:) operator is missing from the Signal type. I guess this is not so bad since you can create a SignalProducer from a Signal. But why does Signal not have the same operator?
Has anyone encountered this problem before? Or am I missing something?
Any help would be much appreciated.
The Signal docs say:
An observer of a Signal will see the exact same sequence of events as all other observers. In other words, events will be sent to all observers at the same time.
This is in contrast to producers, which create a new signal each time they are started, which means it's possible for each observer to see different events.
The buffering scenario requires each observer to receive a list of current values in the buffer upon subscription, and other observers should not receive these values each time a new observer is added. Therefore, each observer needs their own signal, and that means the buffering mechanism must be implemented as a producer which can create a new signal for each subscriber.
There's a good discussion from 2016 when replayLazily was added that hopefully clarifies the thinking behind the operator and why it absolutely can't be a part of Signal.
In my class, there are some values that may be observed by other part of the app. These values is can be read and written to database.
I have a collection that retains some RACSubjects.
When an object need to observe a value V1, I'll create a RACSignal for it, name it S1, and later if any other object is also interested in V1, I'll give it V1 too, so that when S1 changed, I can call [RACSubject sendNext:] to notify objects that is interested in it.
But there is a problem, I don't know when to release S1, since I don't know how to get notified when there is no object subscribing to S1.
Is there any way to do this?
I'm mentally rewording part of your question to the following (my changes italicized), because I think the original phrasing had some typos:
When an object need to observe a value V1, I'll create a RACSubject for it, name it S1, and later if any other object is also interested in V1, I'll give it S1 too, so that when V1 changed, I can call [RACSubject sendNext:] to notify objects that is interested in it.
If this is an incorrect interpretation, ignore this answer.
If you aren't explicitly retaining a signal yourself, ReactiveCocoa will automatically reclaim it when it runs out of subscribers. The relevant excerpt:
A created signal is automatically added to a global set of active signals.
The signal will wait for a single pass of the main run loop, and then remove itself from the active set if it has no subscribers. Unless the signal was retained somehow, it would deallocate at this point.
If something did subscribe in that run loop iteration, the signal stays in the set.
Later, when all the subscribers are gone, step 2 is triggered again.
But there's one problem: this doesn't apply to RACSubjects. They aren't added to the global set of active signals.
However, there's a... workaround you can apply in order to get this nice auto-retain behavior.
RACSignal *autoretainedSignal = [subject map:^(id x) { return x; }]
As long as you only subscribe to the autoretainedSignal, not the underlying RACSubject, you can take advantage of the auto-retain behavior that normal signals get.
So how do you hold onto it without retaining it? If you only have one signal that you're interesting in caching/sharing, you can just store it in a weak property. If there are multiple properties that you're dynamically managing, an NSMapTable with weak storage is your friend.
You'll need to hold onto both the underlying subject (so you can send events on it) and the derived signal (so you can provide it to subscribers) weakly. As long as the derived signal has subscribers, it will keep its underlying subject alive, and as soon as it runs out of subscribers both it and its subject will be deallocated.
I found an idea for you :)
#property (nonatomic,assign) NSInteger countOfSubscribers = 0;
later in your code
RACSignal *s; // here is your target signal
[[s rac_signalForSelector:#selector(subscribeNext:)]
subscribeNext:^(id x) {
self.countOfSubscribers++;
}];
[[[s rac_signalForSelector:#selector(subscribeNext:)] rac_willDeallocSignal]
subscribeNext:^(id x) {
self.countOfSubscribers--;
}];
Main idea is to subscribe to 'subscribeNext:' method, and when original subscriber will be removed - rac_willDeallocSignal
will called.
It's not completely tested snippet, but i hope this can help you to find right direction.
I'm pretty new to FRP and I'm facing a problem:
I subscribe to an observable that triggers subscribeNext every second.
In the subscribeNext's block, I zip observables that execute asynchronous operations and in zip's completed block I perform an action with the result.
let signal: RACSignal
let asynchOperations: [RACSignal]
var val: AnyObject?
// subscribeNext is trigered every second
signal.subscribeNext {
let asynchOperations = // several RACSignal
// Perform asynchronous operations
RACSignal.zip(asynchOperations).subscribeNext({
val = $0
}, completed: {
// perform actions with `val`
})
}
I would like to stop the triggering of subscribeNext for signal (that is normally triggered every second) until completed (from the zip) has been reached.
Any suggestion?
It sounds like you want an RACCommand.
A command is an object that can perform asynchronous operations, but only have one instance of its operation running at a time. As soon as you tell a command to start execute:ing, it will become "disabled," and will automatically become enabled again when the operation completes.
(You can also make a command that's enabled based on other criteria than just "am I executing right now," but it doesn't sound like you need that here.)
Once you have that, you could derive a signal that "gates" the interval signal (for example, if:then:else: on the command's enabled signal toggling between RACSignal.empty and your actual signal -- I do this enough that I have a helper for it), or you can just check the canExecute property before invoking execute: in your subscription block.
Note: you're doing a slightly weird thing with your inner subscription there -- capturing the value and then dealing with the value on the completed block.
If you're doing that because it's more explicit, and you know that the signal will only send one value but you feel the need to encode that directly, then that's fine. I don't think it's standard, though -- if you have a signal that will only send one value, that's something that unfortunately can't be represented at the type level, but is nonetheless an assumption that you can make in your code (or at least, I find myself comfortable with that assumption. To each their own).
But if you're doing it for timing reasons, or because you actually only want the last value sent from the signal, you can use takeLast:1 instead to get a signal that will always send exactly one value right at the moment that the inner signal completes, and then only subscribe in the next block.
Slight word of warning: RACCommands are meant to be used from the main thread to back UI updates; if you want to use a command on a background thread you'll need to be explicit about the scheduler to deliver your signals on (check the docs for more details).
Another completely different approach to getting similar behavior is temporal recursion: perform your operation, then when it's complete, schedule the operation to occur again one second later, instead of having an ongoing timer.
This is slightly different as you'll always wait one second between operations, whereas in the current one you could be waiting anywhere between zero and one seconds, but if that's a not a problem then this is a much simpler solution than using an RACCommand.
ReactiveCocoa's delay: method makes this sort of ad-hoc scheduling very convenient -- no manual NSTimer wrangling here.
I have the following code below that loops through an array. I need to check if the finish or fail selector has been called iterating to the next object in my dataArray.
for (id object in dataArray) {
[client setDidFinishSelector:#selector(getDataFinish:)];
[client setDidFailSelector:#selector(getDataFail:)];
[client getData:object];
}
In my getDataFinish method I assign values and I am trying to keep it in order. If I use the above method, the values can get out of order since the client response time can be different for each request..
I see two possible solutions, depending on what you're actually trying to do. It sounds like you're making calls to the internet, so yes you will get varied response time (or no response at all). Because of this, I would recommend using NSNotification. See this answer for more information about that.
Another option is making a flag in your code (AKA a BOOL) that you set to YES when your method has completed. Again, if you're making calls to the web I would not recommend this method as you are setting yourself up for an infinite loop if the user has no service and the BOOL never changes.
If you are still having trouble let me know and I can provide a more detailed answer.
I am registering to receive updates from a CMMotionManager like so:
motionManager.startDeviceMotionUpdatesToQueue(deviceMotionQueue) {
[unowned self] (deviceMotion, error) -> Void in
// ... handle data ...
}
where deviceMotionQueue is an NSOperationQueue with the highest quality of service, i.e. the highest possible update rate:
self.deviceMotionQueue.qualityOfService = NSQualityOfService.UserInteractive
This means that I am getting updates often. Like really often. So I was wondering: what happens if I don't handle one update fast enough? If the update interval is shorter than the execution time of 'handle data'? Will the motion manager drop some information? Or will it queue up and after a while become run out of memory? Or is this not feasable at all?
It's hard to know what the internal CoreMotion implementation will do, and given that what it does is an "implementation detail", even if you could discern its current behavior, you wouldn't want to rely on that behavior moving forward.
I think the common solution to this is to do the minimum amount of work in the motion update handler, and then manage the work/rate-limiting/etc yourself. So, for instance, if you wanted to drop interstitial updates that arrived while you were processing the last update, you could have the update handler that you pass into CoreMotion do nothing but (safely) add a copy of deviceMotion to a mutable array, and then enqueue the "real" handler on a different queue. The real handler might then have a decision tree like:
if the array is empty, return immediately
otherwise (safely) take the last element, clear all elements from the array, and do the work based on the last element
This would have the effect of letting you take only the most recent reading, but also to have knowledge of how many updates were missed, and, if it's useful, what those missed updates were. Depending on your app, it might be useful to batch process the missed events as a group.
But the takeaway is this: if you want to be sure about how a system like this behaves, you have to manage it yourself.