快速组合 : How to create a single publisher from a list of publishers?

标签 swift combine

我想使用 Apple 的新 Combine 框架从列表中的每个元素发出多个请求。然后我想要一个减少所有响应的单一结果。基本上,我想从发布者列表转到拥有响应列表的单个发布者。

我尝试制作一个发布商列表,但我不知道如何将该列表缩减为单个发布商。我试过制作一个包含列表的发布者,但我无法平面映射发布者列表。

请看“createIngredients”函数

func createIngredient(ingredient: Ingredient) -> AnyPublisher<CreateIngredientMutation.Data, Error> {
    return apollo.performPub(mutation: CreateIngredientMutation(name: ingredient.name, optionalProduct: ingredient.productId, quantity: ingredient.quantity, unit: ingredient.unit))
            .eraseToAnyPublisher()
}

func createIngredients(ingredients: [Ingredient]) -> AnyPublisher<[CreateIngredientMutation.Data], Error> {
    // first attempt
    let results = ingredients
            .map(createIngredient)
    // results = [AnyPublisher<CreateIngredientMutation.Data, Error>]

    // second attempt
    return Publishers.Just(ingredients)
            .eraseToAnyPublisher()
            .flatMap { (list: [Ingredient]) -> Publisher<[CreateIngredientMutation.Data], Error> in
                return list.map(createIngredient) // [AnyPublisher<CreateIngredientMutation.Data, Error>]
            }
}

我不确定如何获取发布者数组并将其转换为包含数组的发布者。

类型“[AnyPublisher]”的结果值不符合闭包结果类型“Publisher”

最佳答案

本质上,在您的特定情况下,您会看到类似这样的内容:

func createIngredients(ingredients: [Ingredient]) -> AnyPublisher<[CreateIngredientMutation.Data], Error> {
    Publishers.MergeMany(ingredients.map(createIngredient(ingredient:)))
        .collect()
        .eraseToAnyPublisher()
}

这“收集”了上游发布者生成的所有元素,并且——一旦它们全部完成——生成一个包含所有结果的数组并最终完成自身。

请记住,如果其中一个上游发布者失败——或产生多个结果——元素的数量可能与订阅者的数量不匹配,因此您可能需要额外的运营商来缓解这种情况,具体取决于您的情况。

更通用的答案,您可以使用 EntwineTest framework 对其进行测试:

import XCTest
import Combine
import EntwineTest

final class MyTests: XCTestCase {
    
    func testCreateArrayFromArrayOfPublishers() {

        typealias SimplePublisher = Just<Int>

        // we'll create our 'list of publishers' here. Each publisher emits a single
        // Int and then completes successfully – using the `Just` publisher.
        let publishers: [SimplePublisher] = [
            SimplePublisher(1),
            SimplePublisher(2),
            SimplePublisher(3),
        ]

        // we'll turn our array of publishers into a single merged publisher
        let publisherOfPublishers = Publishers.MergeMany(publishers)

        // Then we `collect` all the individual publisher elements results into
        // a single array
        let finalPublisher = publisherOfPublishers.collect()

        // Let's test what we expect to happen, will happen.
        // We'll create a scheduler to run our test on
        let testScheduler = TestScheduler()

        // Then we'll start a test. Our test will subscribe to our publisher
        // at a virtual time of 200, and cancel the subscription at 900
        let testableSubscriber = testScheduler.start { finalPublisher }

        // we're expecting that, immediately upon subscription, our results will
        // arrive. This is because we're using `just` type publishers which
        // dispatch their contents as soon as they're subscribed to
        XCTAssertEqual(testableSubscriber.recordedOutput, [
            (200, .subscription),            // we're expecting to subscribe at 200
            (200, .input([1, 2, 3])),        // then receive an array of results immediately
            (200, .completion(.finished)),   // the `collect` operator finishes immediately after completion
        ])
    }
}

关于快速组合 : How to create a single publisher from a list of publishers?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56782078/

相关文章:

Swift 组合 block 运算符

swift - 根据观察到的变量的变化更新@Published 变量

xcode - @Published ObservedObjects SwiftUI 更新未发生

ios - 如何使用 NSUserDefaults 在 Swift 中存储自定义类数组?

ios - 在 iOS7 Swift 项目中包含来自 Swift 源的 Pod

swift - 用于获取 PassthroughSubject 发布者值的单元测试

swift - 从回调创建发布者

swift - 快速将 CGPointo 转换为 Int

ios - 无法在 iOS 中使用 SwiftyJson 将 JSON 数组或 JSON 对象分配给 JSON

ios - AVCaptureVideoPreviewLayer 从视频切换并以静态图像模式捕获图像时的延迟