Reputation: 1479
This question stems from this GitHub issue for the book Functional Reactive Pixels by Ash Furrow.
The example below is similar to the book in that it bridges non-RAC code and also sends the entire list of photos.
RACCommand *fetchPhotosCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input __unused) {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[[MyAPIClient sharedClient] fetchPhotosWithBlock:^(NSArray *photos, NSError *error) {
if (photos == nil) {
[subscriber sendError:error];
}
else {
[subscriber sendNext:photos];
[subscriber sendCompleted];
}
}];
return [RACDisposable disposableWithBlock:^{
// Perform cleanup, cancel request, etc
}];
}];
}];
I'd like to know how best to send individual items (photos in this case), but more importantly, how the command should be subscribed to and the response handled, including pagination.
Typically I would do this...
RAC(self, photos) = [fetchPhotosCommand.executionSignals flatten];
...which obviously doesn't work when sending individual items. The book says this:
We’re sending over a completed data set instead of a stream of single values over time. It would be “more reactive” if we instead sent a stream of individual photo models that could be concatenated later on. This would also help with pagination, but we’re not going to address this pattern because it’s a little more advanced. Check out octokit for an example of this concatenation.
So, I'm aware that some concatenation is needed on the subscribing end but don't follow what's happening in octokit.
In summary:
How should I be sending the individual items? Something like this?
[photos enumerateObjectsUsingBlock:^(Photo *photo, NSUInteger idx __unused, BOOL *stop __unused) {
[subscriber sendNext:photo];
}];
[sendCompleted];
return [RACDisposable disposableWithBlock:^{
// Also do something here with `stop`?
}];
How should I handle the individual items on the subscribing end, so that self.photos
is still an array of all of the photos that were sent individually?
How does pagination play a part in this? Obviously we wouldn't want to add items to self.photos
that were already present.
Upvotes: 2
Views: 724
Reputation: 6308
How should I be sending the individual items? Something like this?
Your example is correct. If you want to send individual Photo objects, you find some way to iterate through your list of Photos and send the subscriber each Photo as a separate value.
How should I handle the individual items on the subscribing end, so that self.photos is still an array of all of the photos that were sent individually?
This sort of contradicts your desire to send them individually. If you want to collect them into an array in your subscriber, you can either send the Photos in batches as arrays, or you can collect the individual Photo values and build an array from them. (There's actually a built-in operator in ReactiveCocoa that will collect values into an array for you, called -[RACSignal collect]
.)
How does pagination play a part in this? Obviously we wouldn't want to add items to self.photos that were already present.
I'm not sure how to help you with this. -enqueueRequest:resultClass:fetchAllPages:
creates signals that paginate through results for you, following this general approach:
Link
HTTP header, whose value is a URL that contains the next page of results.Link
header was present in the HTTP response, the -enqueueRequest:resultClass:fetchAllPages:
method is recursively invoked to obtain a signal for the next page's URL, and that signal is concatenated onto the end of the current signal.As @jspahrsummers points out, this lets you lazily traverse through the results. As your code reaches the end of each page, it will subscribe to the concatenated signal for the next page, resulting in another fetch for data. But it relies on OctoKit and GitHub API infrastructure. Hopefully this explanation helps you understand how you can modify your code to support a model similar to OctoKit's.
Upvotes: 4