swift - Swift Combine:如何从发布者列表中创建单个发布者?
问题描述
使用 Apple 的新组合框架,我想从列表中的每个元素发出多个请求。然后我想从减少所有响应中得到一个单一的结果。基本上,我想从发布者列表转到拥有响应列表的单个发布者。
我曾尝试制作出版商列表,但我不知道如何将该列表简化为单个出版商。而且我尝试制作一个包含列表的发布者,但我无法平面映射发布者列表。
请看“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 框架对其进行测试:
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
])
}
}
推荐阅读
- django - 如何为我的视图添加信号方法?
- java - 如何包括正在进行的黄瓜测试,但不执行它们
- curl - 在 jenkins-pipeline 中进行奇怪调用的 Curl 命令
- java - Tess4J 4.0.0 java.lang.UnsatisfiedLinkError:找不到指定的模块
- sql-server - 主键索引
- css - 透视在 Mozilla Firefox 上不起作用?
- r - 如何强制 R 更新 png 文件创建日期?
- django - Geodjango 创建一个新的 SRID
- android - 返回应用程序时更改状态
- bash - 当函数包含其他回声时,从 bash 函数(<4.3)返回字符串的最安全方法