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

Swift Combine中如何依次转换Publisher并单独处理各步骤错误?

解决Swift Combine序列执行的类型匹配与错误处理问题

针对你的需求,我们可以通过调整Publisher的类型匹配和错误处理逻辑来实现预期的序列流程,以下是具体方案:

核心思路梳理

  1. Login错误处理:登录失败时处理错误,直接终止后续流程;登录成功则继续执行updateImage
  2. UpdateImage错误跳过:无论updateImage成功或失败(即使后续修改为可能抛出错误的类型),都继续执行getProfile
  3. 最终结果处理:在sink中统一处理getProfile的结果与错误

完整实现代码

import Combine

// 假设的类型定义
struct Token: Equatable { let value: String }
struct Status: Equatable { let success: Bool }
struct Profile: Equatable { let name: String }
enum CustomError: Error { case loginFailed, profileFetchFailed }

// 模拟原函数
func login() -> Future<Token, Error> {
    Future { promise in
        // 模拟登录逻辑,这里可替换为实际请求
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            promise(.success(Token(value: "mock_token")))
            // 可测试失败场景:promise(.failure(CustomError.loginFailed))
        }
    }
}

func updateImage() -> Future<Status, Never> {
    Future { promise in
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            promise(.success(Status(success: true)))
        }
    }
}

func getProfile() -> Future<Profile, Error> {
    Future { promise in
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            promise(.success(Profile(name: "Mock User")))
            // 可测试失败场景:promise(.failure(CustomError.profileFetchFailed))
        }
    }
}

// 执行序列
var cancellables = Set<AnyCancellable>()

login()
    .catch { loginError in
        // 处理登录错误:日志、弹窗等操作
        print("登录失败:\(loginError)")
        // 返回Empty Publisher终止后续流程,类型需与上游匹配(Output: Token, Error: Error)
        return Empty<Token, Error>(completeImmediately: true)
    }
    .flatMap { token in
        // 处理登录成功的token:如保存到本地
        print("登录成功,Token:\(token.value)")
        // 先执行updateImage,无论结果如何都继续调用getProfile
        return updateImage()
            // 针对updateImage可能的错误(即使当前是Never,预留后续扩展)
            .catch { _ in
                // 跳过错误,返回Empty终止当前分支,继续执行then后的逻辑
                return Empty<Status, Never>(completeImmediately: true)
            }
            // 忽略updateImage的输出,执行getProfile
            .then(getProfile())
    }
    .sink(receiveCompletion: { completion in
        switch completion {
        case .finished:
            print("流程执行完成")
        case .failure(let profileError):
            print("获取用户信息失败:\(profileError)")
        }
    }, receiveValue: { profile in
        print("获取到用户信息:\(profile.name)")
    })
    .store(in: &cancellables)

关键细节说明

  1. Empty Publisher的作用

    • 在登录错误的catch块中,返回Empty<Token, Error>会立即发送.finished事件,终止整个后续流程,不会将错误传递到sink
    • 在updateImage的catch块中(预留扩展用),返回Empty<Status, Never>会跳过错误,让.then操作符继续执行getProfile
  2. 类型匹配解决

    • 所有catch块返回的Publisher必须与上游的Output和Error类型严格匹配,否则会出现编译错误
    • 使用.then操作符可以忽略前序Publisher的输出,直接执行下一个Publisher,完美适配“跳过updateImage结果,执行getProfile”的需求
  3. 流程控制逻辑

    • 登录失败:处理错误后终止流程,sink会收到.finished事件
    • 登录成功:先执行updateImage,无论成功与否,都会执行getProfile,最终sink处理getProfile的结果或错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 04:52:13