Swift 组合 - 异步调用

Swift Combine - Async calls

我有以下功能:

    func getUserProfile() -> AnyPublisher<UserProfileDTO?, Error> {
    return Future { [unowned self] promise in
        do {
            if let data = KeychainWrapper.standard.data(forKey: profileKey) {
                let profileDTO = try PropertyListDecoder().decode(UserProfileDTO.self, from: data)
                setCurrentSession(profileDTO)
                promise(.success(profileDTO))
            }
            else {
                promise(.success(nil))
            }
        }
        catch {
            // Delete current UserProfile if cannot decode
            let _ = KeychainWrapper.standard.removeAllKeys()
            promise(.failure(error))
        }
    }.eraseToAnyPublisher()
}

    func connect(userProfile: UserProfileDTO) -> AnyPublisher<UserProfileDTO, Error> {
    return Future { promise in
        SBDMain.connect(withUserId: userProfile.email) { (user, error) in
            if let error = error {
                promise(.failure(error))
            }
            else {
                promise(.success(userProfile))
            }
        }
    }.eraseToAnyPublisher()
}

我想做的是先调用 getUserProfile() 方法,如果 return 值不为零,则调用 connect() 方法。但是,如果 getUserProfile() 有 nil 响应,则不需要调用 connect() 并且它应该只是 return nil 响应。这两种方法都需要从 autoLoginUser() 方法中调用。 我现在遇到的问题是弄清楚如何在不编写太多嵌套语句的情况下以干净的方式 swift 执行此操作。

我尝试使用 flatMaps,但它没有按我预期的方式运行。非常感谢任何帮助。

我目前一直在研究的一个解决方案是这样的。但这并不完全有效。

    func autoLoginUser2() -> AnyPublisher<UserProfile?,Error> {
    getUserProfile()
        .tryMap { [unowned self] in
            if let currentProfile = [=12=] {
                return connect(userProfile: currentProfile)
                    .tryMap {
                        //Map from UserProfileDTO --> UserProfile
                        return UserProfileDTOMapper.map([=12=])
                        
                    }
            }
            return nil
        }.eraseToAnyPublisher()
}

通过对使用的类型和错误类型进行一些调整,这应该可以工作。首先你要求提供配置文件,然后你强制打开配置文件,如果它是零,你会抛出一个错误,该错误将作为失败发送到接收器。 如果配置文件存在,则调用连接。

getUserProfile()
.tryMap { userDTO -> UserProfileDTO in
    if let id = userDTO {
        return id
    }
    throw MyError.noProfileDT
}
.flatMap { id in
    connect(id)
}
.sink {
    //.....
}

如果将连接签名更改为 return 可选配置文件:

func connect(userProfile: UserProfileDTO) -> AnyPublisher<UserProfileDTO?, Error>

你可以这样做:

getUserProfile()
    .flatMap { userProfile -> AnyPublisher<UserProfileDTO?, Error> in
            
        if let userProfile = userProfile {
            return connect(userProfile: userProfile)
                .eraseToAnyPublisher()
        } else {
            return Just<UserProfileDTO?>(nil)
                .setFailureType(to: Error.self)
                .eraseToAnyPublisher()
        }
    }
    //.sink etc.

如果您不需要发布者发出 nil,您可以保留连接签名并使用 compactMap:

getUserProfile()
    .compactMap { [=11=] }
    .flatMap {
        connect(userProfile: [=11=])
            .eraseToAnyPublisher()
    }