您如何编写以“Reactive Cocoa”方式获取集合?

时间:2013-06-24 17:28:00

标签: objective-c github functional-programming reactive-cocoa

我正在构建的客户端正在使用Reactive CocoaOctokit,到目前为止,它已经进展顺利。但是现在我正处于一个我想要获取存储库集合的地步,并且无法绕过这样的“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功能的亮点都非常感谢!

2 个答案:

答案 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通知)。

有几点需要注意:

  1. 最好尽可能避免subscribeNext:。使用它在功能范例之外的步骤(如同doNext:doError:一样,但它们有时也是有用的工具)。一般来说,你想要考虑如何将信号转换成符合你想要的信号。

  2. 如果您想将一项或多项工作链接在一起,通常需要使用flattenMap:。更一般地说,您想要开始考虑信号信号 - 发送代表其他工作的其他信号的信号。

  3. 您经常希望等待尽可能长时间将工作移回主线程。

  4. 在思考问题时,有时候有必要先写出每个信号来考虑a)你拥有什么,b)你想要什么,以及c)如何从一个到另一个。

  5. 编辑:已更新,以便在下面发表@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;