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

时间:2023-01-02 22:00:50

The client I'm building is using Reactive Cocoa with Octokit and so far it has been going very well. However now I'm at a point where I want to fetch a collection of repositories and am having trouble wrapping my head around doing this the "RAC way"

我正在建立的客户正在使用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");
 }];

I originally assumed that -subscribeNext: would pass an NSArray, but now understand that it sends the message every "next" object returned, which in this case is an OCTRepository.

我最初假设-subscribeNext:会传递一个NSArray,但现在明白它会返回每个返回的“next”对象的消息,在这种情况下是一个OCTRepository。

Now I could do something like this:

现在我可以这样做:

NSMutableArray *repos = [NSMutableArray array];
// most of that code above
subscribeNext:^(OCTRepository *fetchedRepo) {
    [repos addObject:fetchedRepo];
}
// the rest of the code above

Sure, this works, but it doesn't seem to follow the functional principles that RAC enables. I'm really trying to stick to conventions here. Any light on capabilities of RAC/Octokit are greatly appreciated!

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

2 个解决方案

#1


14  

It largely depends on what you want to do with the repositories afterward. It seems like you want to do something once you have all the repositories, so I'll set up an example that does that.

这在很大程度上取决于您之后要对存储库执行的操作。一旦你拥有了所有的存储库,你似乎想要做一些事情,所以我将建立一个这样做的例子。

// 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];

Now self.repositories will change (and fire a KVO notification) whenever the repositories are updated from the client.

现在,只要从客户端更新存储库,self.repositories就会更改(并触发KVO通知)。

A couple things to note about this:

有几点需要注意:

  1. It's best to avoid subscribeNext: whenever possible. Using it steps outside of the functional paradigm (as do doNext: and doError:, but they're also helpful tools at times). In general, you want to think about how you can transform the signal into something that does what you want.

    最好避免subscribeNext:尽可能。使用它在功能范例之外的步骤(doNext:和doError :,但它们有时也是有用的工具)。一般来说,您想要考虑如何将信号转换为满足您需求的信号。

  2. If you want to chain one or more pieces of work together, you often want to use flattenMap:. More generally, you want to start thinking about signals of signals--signals that send other signals that represent the other work.

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

  3. You often want to wait as long as possible to move work back to the main thread.

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

  4. When thinking through a problem, it's sometimes valuable to start by writing out each individual signal to think about a) what you have, b) what you want, and c) how to get from one to the other.

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

EDIT: Updated to address @JustinSpahrSummers' comment below.

编辑:更新以解决下面@JustinSpahrSummers的评论。

#2


2  

There is a -collect operator that should do exactly what you're looking for.

有一个-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;

#1


14  

It largely depends on what you want to do with the repositories afterward. It seems like you want to do something once you have all the repositories, so I'll set up an example that does that.

这在很大程度上取决于您之后要对存储库执行的操作。一旦你拥有了所有的存储库,你似乎想要做一些事情,所以我将建立一个这样做的例子。

// 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];

Now self.repositories will change (and fire a KVO notification) whenever the repositories are updated from the client.

现在,只要从客户端更新存储库,self.repositories就会更改(并触发KVO通知)。

A couple things to note about this:

有几点需要注意:

  1. It's best to avoid subscribeNext: whenever possible. Using it steps outside of the functional paradigm (as do doNext: and doError:, but they're also helpful tools at times). In general, you want to think about how you can transform the signal into something that does what you want.

    最好避免subscribeNext:尽可能。使用它在功能范例之外的步骤(doNext:和doError :,但它们有时也是有用的工具)。一般来说,您想要考虑如何将信号转换为满足您需求的信号。

  2. If you want to chain one or more pieces of work together, you often want to use flattenMap:. More generally, you want to start thinking about signals of signals--signals that send other signals that represent the other work.

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

  3. You often want to wait as long as possible to move work back to the main thread.

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

  4. When thinking through a problem, it's sometimes valuable to start by writing out each individual signal to think about a) what you have, b) what you want, and c) how to get from one to the other.

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

EDIT: Updated to address @JustinSpahrSummers' comment below.

编辑:更新以解决下面@JustinSpahrSummers的评论。

#2


2  

There is a -collect operator that should do exactly what you're looking for.

有一个-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;