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

rny*_*rom 10 functional-programming github objective-c 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");
 }];
Run Code Online (Sandbox Code Playgroud)

我最初假设-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
Run Code Online (Sandbox Code Playgroud)

当然,这是有效的,但它似乎没有遵循RAC启用的功能原则.我真的想在这里坚持惯例.任何关于RAC/Octokit功能的亮点都非常感谢!

mdi*_*iep 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];
Run Code Online (Sandbox Code Playgroud)

self.repositories每当从客户端更新存储库时,现在将更改(并触发KVO通知).

有几点需要注意:

  1. subscribeNext:尽可能避免.使用它的功能模式以外的步骤(如做doNext:doError:,但他们也是有帮助的工具,在次).一般来说,您想要考虑如何将信号转换为满足您需求的信号.

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

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

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

编辑:更新以解决@JustinSpahrSummers的评论如下.

  • 很好的答案,虽然我建议在这里使用`-map:`和`-switchToLatest`,以便只使用最新客户端的结果.否则,您可能会遇到以下客户端的结果到达新客户端的情况,然后您的`存储库`属性错误. (2认同)