我正在构建的客户端正在使用Reactive Cocoa和Octokit,到目前为止,它已经进展顺利。但是现在我正处于一个我想要获取存储库集合的地步,并且无法绕过这样的“RAC方式”
// fire this when an authenticated client is set
[[RACAbleWithStart([GHDataStore sharedStore], client)
filter:^BOOL (OCTClient *client) {
return client != nil && client.authenticated;
}]
subscribeNext:^(OCTClient *client) {
[[[client fetchUserRepositories] deliverOn:RACScheduler.mainThreadScheduler]
subscribeNext:^(OCTRepository *fetchedRepo) {
NSLog(@" Received new repo: %@",fetchedRepo.name);
}
error:^(NSError *error) {
NSLog(@"Error fetching repos: %@",error.localizedDescription);
}];
} completed:^{
NSLog(@"Completed fetching repos");
}];
我最初假设-subscribeNext:
会传递NSArray
,但现在明白它会返回每个返回的“next”对象的消息,在这种情况下是OCTRepository
。
现在我可以这样做:
NSMutableArray *repos = [NSMutableArray array];
// most of that code above
subscribeNext:^(OCTRepository *fetchedRepo) {
[repos addObject:fetchedRepo];
}
// the rest of the code above
当然,这有效,但它似乎没有遵循RAC启用的功能原则。我真的想在这里坚持惯例。任何关于RAC / Octokit功能的亮点都非常感谢!
答案 0 :(得分:14)
这在很大程度上取决于您之后要对存储库执行的操作。一旦你拥有了所有的存储库,你似乎想要做一些事情,所以我将建立一个这样做的例子。
// Watch for the client to change
RAC(self.repositories) = [[[[[RACAbleWithStart([GHDataStore sharedStore], client)
// Ignore clients that aren't authenticated
filter:^ BOOL (OCTClient *client) {
return client != nil && client.authenticated;
}]
// For each client, execute the block. Returns a signal that sends a signal
// to fetch the user repositories whenever a new client comes in. A signal of
// of signals is often used to do some work in response to some other work.
// Often times, you'd want to use `-flattenMap:`, but we're using `-map:` with
// `-switchToLatest` so the resultant signal will only send repositories for
// the most recent client.
map:^(OCTClient *client) {
// -collect will send a single value--an NSArray with all of the values
// that were send on the original signal.
return [[client fetchUserRepositories] collect];
}]
// Switch to the latest signal that was returned from the map block.
switchToLatest]
// Execute a block when an error occurs, but don't alter the values sent on
// the original signal.
doError:^(NSError *error) {
NSLog(@"Error fetching repos: %@",error.localizedDescription);
}]
deliverOn:RACScheduler.mainThreadScheduler];
每当从客户端更新存储库时,self.repositories
都会更改(并触发KVO通知)。
有几点需要注意:
最好尽可能避免subscribeNext:
。使用它在功能范例之外的步骤(如同doNext:
和doError:
一样,但它们有时也是有用的工具)。一般来说,你想要考虑如何将信号转换成符合你想要的信号。
如果您想将一项或多项工作链接在一起,通常需要使用flattenMap:
。更一般地说,您想要开始考虑信号信号 - 发送代表其他工作的其他信号的信号。
您经常希望等待尽可能长时间将工作移回主线程。
在思考问题时,有时候有必要先写出每个信号来考虑a)你拥有什么,b)你想要什么,以及c)如何从一个到另一个。
编辑:已更新,以便在下面发表@JustinSpahrSummers的评论。
答案 1 :(得分:2)
有一个-collect操作符应该完全符合您的要求。
// Collect all receiver's `next`s into a NSArray. nil values will be converted
// to NSNull.
//
// This corresponds to the `ToArray` method in Rx.
//
// Returns a signal which sends a single NSArray when the receiver completes
// successfully.
- (RACSignal *)collect;