Reputation: 23
I'm having trouble disposing of one of the operations in a chained list of operations. I'm trying to use Reactive Cocoa to provide a stream of locations while the user is logged in and has granted access to location services.
I'm still pretty new to the world of Reactive Cocoa and functional reactive programming in general, but here's what I've got so far:
@weakify(self);
// signal of BOOL
RACSignal *loggedInSignal = [[self loggedInSignal] distinctUntilChanged];
// signal of CLAuthorizationStatus
RACSignal *currentStateSignal = [[self currentAuthorizationSignal] distinctUntilChanged];
// defer creation of the location stream signal until subscribed to
RACSignal *locationSignal = [RACSignal defer:^RACSignal *
{
@strongify(self);
// To get an uninterrupted stream of locations, just retry the stream if an error occurs
return [[self locationsSignal] retry];
}];
RACSignal *locationServicesDisabledSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber)
{
@strongify(self);
[self locationServicesDisabled];
[subscriber sendCompleted];
return nil;
}];
self.disposable = [[[[[[loggedInSignal map:^id(NSNumber *loggedIn)
{
return [loggedIn boolValue] ? currentStateSignal : [RACSignal empty];
}]
switchToLatest]
map:^id(NSNumber *currentStatus)
{
if (CLAuthorizationStatusCanBeginMonitoring([currentStatus intValue]))
{
return locationSignal;
}
return locationServicesDisabledSignal;
}]
switchToLatest]
subscribeNext:^(CLLocation *location)
{
@strongify(self);
CLLocationCoordinate2D coord = location.coordinate;
[self.output receivedNewLocationWithLatitude:coord.latitude
longitude:coord.longitude
accuracy:location.horizontalAccuracy
timestamp:location.timestamp];
}]
asScopedDisposable];
This works as expected when the authorisation status changes, starting/stopping the location stream. But when logging out, the location stream continues to provide locations. It's as if authStateSignal
is not disposed of, adjusting the authorisation status will still start/stop the stream, even though loggedInSignal had returned NO
as its last value.
I tried this:
(previous code as above)...
return locationServicesDisabledSignal;
}]
switchToLatest]
takeUntil:[loggedInSignal skip:1]]
subscribeNext:^(CLLocation *location)
{
(etc)...
after the second switchToLatest
to stop that signal after the next log in/out, but besides feeling kind of "hack-ish", it stopped the location stream events after 1 log in/out, to never start them again. What is the proper method of handling this scenario?
Also, it feels like I'm using locationServicesDisabledSignal
in the wrong way. I want a method to be called when authStateSignal
returns a status indicating the location services are disabled, so that I can react accordingly, but putting the method call inside a [RACSignal createSignal:]
block that way doesn't feel very Reactive Cocoa-ey. Any other tips on how to do any of what I'm trying the Reactive Cocoa way would be greatly appreciated too.
Upvotes: 1
Views: 244
Reputation: 3063
The reason it isn't working out for you is that when you return a [RACSignal empty];
values will nor propogate through your chain because [RACSignal empty];
does not send any next events. So Try this:
// signal of BOOL
RACSignal *loggedInSignal = [[self loggedInSignal] distinctUntilChanged];
// signal of CLAuthorizationStatus
RACSignal *currentStateSignal = [[self currentAuthorizationSignal] distinctUntilChanged];
// defer creation of the location stream signal until subscribed to
RACSignal *locationSignal = [RACSignal defer:^RACSignal *
{
@strongify(self);
// To get an uninterrupted stream of locations, just retry the stream if an error occurs
return [[self locationsSignal] retry];
}];
RACSignal *locationServicesDisabledSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber)
{
@strongify(self);
[self locationServicesDisabled];
[subscriber sendCompleted];
return nil;
}];
self.disposable = [[[RACSignal combineLatest:@[loggedInSignal, currentStateSignal]] flattenMap: ^RACStream * (RACTuple *tuple){
NSNumber* loggedIn = [tuple first];
NSNumber* currentStatus = [tuple second];
if([loggedIn boolValue] && CLAuthorizationStatusCanBeginMonitoring([currentStatus intValue])){
return locationSignal
}
else {
return locationServicesDisabledSignal
}
} ]
subscribeNext:^(CLLocation *location)
{
@strongify(self);
CLLocationCoordinate2D coord = location.coordinate;
[self.output receivedNewLocationWithLatitude:coord.latitude
longitude:coord.longitude
accuracy:location.horizontalAccuracy
timestamp:location.timestamp];
}]
Upvotes: 0