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

如何用Combine和Swift直接返回嵌套字典内数组的Publisher

解决方案:让Combine网络层直接返回嵌套数组的Publisher

要实现直接返回AnyPublisher<[Entity], Error>,无需在sink中二次处理,你可以通过在数据流中提前提取嵌套数组并处理可能的错误来实现,以下是具体步骤:

1. 修正NetworkController的代码错误

你当前的get方法中有重复的return URLSession.shared.dataTaskPublisher调用,先修正这个问题:

func get<T>(type: T.Type, url: URL, headers: Headers) -> AnyPublisher<T, Error> where T : Decodable {
    var urlRequest = URLRequest(url: url)
    
    headers.forEach { key, value in
        if let value = value as? String {
            urlRequest.setValue(value, forHTTPHeaderField: key)
        }
    }
    
    return URLSession.shared.dataTaskPublisher(for: urlRequest)
        .map(\.data)
        .decode(type: T.self, decoder: JSONDecoder())
        .eraseToAnyPublisher()
}

2. 自定义网络错误类型(可选但推荐)

为了更清晰地处理"缺失entities数组"这类边界情况,定义一个自定义错误:

enum NetworkError: Error {
    case missingEntities
    // 可根据需求扩展其他错误类型,比如invalidResponse等
}

3. 修改EntitiesLogicController的getEntities方法

让它直接返回[Entity]的Publisher,通过tryMap在数据流中提取并验证数组:

func getEntities(named: String, count: Int) -> AnyPublisher<[Entity], Error> {
    let endpoint = Endpoint.companies(named: named, count: count)
    
    return networkController.get(type: Entities.self, url: endpoint.url, headers: endpoint.headers)
        .tryMap { entitiesResponse in
            // 安全解包entities数组,若为nil则抛出自定义错误
            guard let entities = entitiesResponse.entities else {
                throw NetworkError.missingEntities
            }
            return entities
        }
        .eraseToAnyPublisher()
}

如果你能确定API返回的entities永远不为nil,可以简化为return entitiesResponse.entities!,但推荐用tryMap+错误抛出的方式处理边界情况。

4. 简化ApiCaller的getEntities方法

现在可以直接接收[Entity]类型的数据流,无需在sink中二次处理:

func getEntities(named: String) {
    entitiesLogicController?.getEntities(named: named, count: 20)
        .sink(receiveCompletion: { [weak self] completion in
            switch completion {
            case .failure(let error):
                print("Error retrieving Entity, ERROR: \(error)")
            case .finished:
                print("Data successfully downloaded")
            }
        }, receiveValue: { [weak self] entities in
            self?.entities = entities
        })
        .store(in: &subscriptions)
}

进阶:封装通用的数组提取方法(可选)

如果你的API有很多类似的嵌套数组结构,可以给NetworkController扩展一个通用方法,方便复用:

extension NetworkController {
    func getNestedArray<Root, Element>(
        rootType: Root.Type,
        arrayKeyPath: KeyPath<Root, [Element]?>,
        url: URL,
        headers: Headers
    ) -> AnyPublisher<[Element], Error> where Root: Decodable, Element: Decodable {
        return get(type: rootType, url: url, headers: headers)
            .tryMap { root in
                guard let array = root[keyPath: arrayKeyPath] else {
                    throw NetworkError.missingArray
                }
                return array
            }
            .eraseToAnyPublisher()
    }
    
    // 补充对应的错误类型
    enum NetworkError: Error {
        case missingArray
        case missingEntities
    }
}

使用时只需传入根类型和数组的KeyPath:

func getEntities(named: String, count: Int) -> AnyPublisher<[Entity], Error> {
    let endpoint = Endpoint.companies(named: named, count: count)
    
    return networkController.getNestedArray(
        rootType: Entities.self,
        arrayKeyPath: \.entities,
        url: endpoint.url,
        headers: endpoint.headers
    )
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 10:57:33