Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stop publishing when there are no subscribers and auto start when there are subscribers

How would I implement a RACSignal that would stop publishing when there are no subscribers to it and auto start when there are subscribers?

Here is a scenario:

Let us say I have a currentLocationSignal in the AppDelegate. My LocationViewController would subscribe to the currentLocationSignal when view loads and unsubscribe (dispose) when view unloads. Since it takes few seconds to get the current location, I would like to always subscribe to the currentLocationSignal when the app opens (and auto unsubscribe after few seconds), so by the time I arrive to LocationViewController I would get an accurate location. So there can be more then one subscribers to the signal. When the first subscriber listens, it needs to start calling startUpdatingLocation and when there are no subscribers it needs to call stopUpdatingLocation.

like image 301
prabir Avatar asked Feb 08 '13 09:02

prabir


People also ask

How do you publish to subscriptions feed and notify subscribers?

When uploading a video, keep the box next to “Publish to Subscriptions feed and notify subscribers” on the “Advanced settings” tab checked. Check how often you've posted in the last 24 hours. Viewers can get a maximum of 3 new video notifications from each channel in a 24 hour period.

Why my views are increasing but not subscribers?

When a channel is getting lots of views but not many subscribers, this usually means those viewers are not finding your content very engaging and therefore don't want to see more of it. Here are some of my suggestions after a brief look at your channel: Your videos are very low quality - only 240p resolution.

Does YouTube remove subscribers automatically?

Every three or four months, YouTube will do a massive purge of subscribers, or they will fix a problem with subscriber counts. Last December YouTube acknowledged they had an issue removing spam accounts, which led to a huge subscriber killing spree. We ourselves lost over 700 subscribers in that single purge.


1 Answers

Good question! Normally, you'd use RACMulticastConnection for use cases like this, but, because you want the signal to be able to reactivate later, a connection isn't suitable on its own.

The simplest answer is probably to mimic how a connection works, but with the specific behaviors you want. Basically, we'll keep track of how many subscribers there are at any given time, and start/stop updating the location based on that number.

Let's start by adding a locationSubject property. The subject needs to be a RACReplaySubject, because we always want new subscribers to get the most recently sent location immediately. Implementing updates with that subject is easy enough:

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
    [self.locationSubject sendNext:locations.lastObject];
}

Then, we want to implement the signal that tracks and increments/decrements the subscriber count. This works by using a numberOfLocationSubscribers integer property:

- (RACSignal *)currentLocationSignal {
    return [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
        @synchronized (self) {
            if (self.numberOfLocationSubscribers == 0) {
                [self.locationManager startUpdatingLocation];
            }

            ++self.numberOfLocationSubscribers;
        }

        [self.locationSubject subscribe:subscriber];

        return [RACDisposable disposableWithBlock:^{
            @synchronized (self) {
                --self.numberOfLocationSubscribers;
                if (self.numberOfLocationSubscribers == 0) {
                    [self.locationManager stopUpdatingLocation];
                }
            }
        }];
    }];
}

In the above code, the +createSignal: block is invoked every time a new subscriber is added to the returned signal. When that happens:

  1. We check to see if the number of subscribers is currently zero. If so, the just-added subscriber is the first one, so we need to enable (or re-enable) location updates.
  2. We hook the subscriber directly up to our locationSubject, so the values from the latter are automatically fed into the former.
  3. Then, at some future time, when the subscription is disposed of, we decrement the count and stop location updates if appropriate.

Now, all that's left is subscribing to the currentLocationSignal on startup, and automatically unsubscribing after a few seconds:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Use a capacity of 1 because we only ever care about the latest
    // location.
    self.locationSubject = [RACReplaySubject replaySubjectWithCapacity:1];

    [[self.currentLocationSignal
        takeUntil:[RACSignal interval:3]]
        subscribeCompleted:^{
            // We don't actually need to do anything here, but we need
            // a subscription to keep the location updating going for the
            // time specified.
        }];

    return YES;
}

This subscribes to self.currentLocationSignal immediately, and then automatically disposes of that subscription when the +interval: signal sends its first value.

Interestingly, -[RACMulticastConnection autoconnect] used to behave like -currentLocationSignal above, but that behavior was changed because it makes side effects wildly unpredictable. This use case should be safe, but there are other times (like when making a network request or running a shell command) when automatic reconnection would be horrible.

like image 68
Justin Spahr-Summers Avatar answered Oct 15 '22 22:10

Justin Spahr-Summers