You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Combine实现Swift嵌套网络请求并行执行及错误处理

问题:Combine框架下实现元数据并行请求并处理失败场景

我需要实现这样的业务流程:

  1. 先调用API获取元数据列表
  2. 基于该列表并行调用API获取每个元数据对应的内容
  3. 整合所有结果,将元数据的identifier和type字段更新到对应的Content实例中,最终返回[Content]

需求要求:

  • 若第一个获取元数据的API调用失败,直接终止后续所有请求
  • 第二个批量请求中,单个元数据的请求可能失败,但如果全部请求都失败,则返回错误;如果有部分成功,就返回成功的内容数组

现有结构体定义:

struct MetaData:  Codable {
    var identifier: String?
    var type: String?
    var url: String?
}

struct Content: Codable {
    var contentId: String? // 来自metaData的identifier
    var type: String? // 来自metaData的type
    var title: String?
    var subTitle: String?
    var subscriptionValue: String?
}

已实现的请求方法:

func fetchMetaData() -> AnyPublisher<[MetaData], Error> {
    return Future<[MetaData], Error> { promise in
        //network call
        //if success {
            //promise(.success([MetaData]))
        //} else {
            //promise(.failure(Error()))
        //}
    }.eraseToAnyPublisher()
}

func fetchContents(metaData: MetaData) -> AnyPublisher<Content, Error> {
    return Future<Content, Error> { promise in
        //network call
        //if success {
            //promise(.success(Content))
        //} else {
            //promise(.failure(Error()))
        //}
    }.eraseToAnyPublisher()
}

刚接触Combine框架,请问如何组合这两个方法,完成上述需求并正确处理各种失败场景?


解决方案

我们可以利用Combine的flatMap、Publishers.MergeMany和collect等操作符,结合错误处理逻辑来实现需求,以下是完整实现代码:

import Combine

func fetchAndCombineContents() -> AnyPublisher<[Content], Error> {
    fetchMetaData()
        .flatMap { metaDataList -> AnyPublisher<[Content], Error> in
            // 为每个元数据创建对应的内容请求Publisher,并处理单个请求失败
            let contentPublishers = metaDataList.map { metaData in
                fetchContents(metaData: metaData)
                    // 将单个请求的失败转换为返回nil的成功,避免中断整个流
                    .catch { _ in Just(nil) }
                    .map { content -> Content? in
                        guard var content = content else { return nil }
                        // 将元数据的identifier和type更新到Content实例
                        content.contentId = metaData.identifier
                        content.type = metaData.type
                        return content
                    }
            }
            
            // 处理空元数据列表的情况
            guard !contentPublishers.isEmpty else {
                return Just([])
                    .setFailureType(to: Error.self)
                    .eraseToAnyPublisher()
            }
            
            // 并行合并所有内容请求,收集结果后处理
            return Publishers.MergeMany(contentPublishers)
                .collect() // 收集所有请求的结果到数组
                .map { contents in
                    // 过滤掉请求失败的nil项
                    contents.compactMap { $0 }
                }
                .tryMap { filteredContents in
                    // 检查是否所有请求都失败,若是则抛出错误
                    guard !filteredContents.isEmpty else {
                        throw NSError(domain: "ContentFetchError", code: -1, userInfo: [NSLocalizedDescriptionKey: "所有内容请求均失败"])
                    }
                    return filteredContents
                }
                .eraseToAnyPublisher()
        }
        .eraseToAnyPublisher()
}

关键逻辑说明:

  1. 元数据请求失败处理:
    fetchMetaData()的失败会直接向上传递,flatMap不会执行,后续所有请求都不会触发,完全符合第一个需求。

  2. 单个内容请求失败处理:
    通过.catch { _ in Just(nil) }将单个请求的失败转换为返回nil的成功,这样不会导致整个合并流中断,确保其他请求能正常完成。

  3. 元数据字段更新:
    在map操作中,将对应元数据的identifier和type赋值给Content实例,完成字段同步。

  4. 全部请求失败的处理:
    collect()收集所有结果后,用compactMap过滤掉nil项,再通过tryMap检查过滤后的数组是否为空。如果为空,抛出自定义错误;否则返回成功的内容数组,满足第二个需求。

  5. 空元数据列表处理:
    如果元数据列表为空,直接返回空数组,避免无意义的合并操作。

内容的提问来源于stack exchange,提问作者ReB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 19:39:23