如何在 SwiftUI 中使用 async/await 链接 AnyPublisher

Jac*_*cky 5 async-await swiftui combine

链接 AnyPublisher 的典型方法是使用诸如 flatMap 之类的组合运算符。


class MyService {
    func getUserList() -> AnyPublisher<[User], Error> {
        ....
    }

    func getPostList(user: User) -> AnyPublisher<[Post], Error> {
        ...
    }
}

class ViewModel: ObserableObject {
    let service = MyService()
    
    @published var post: [Post] = []
    
    func fetchAllPostFromLastUser() {
        service.getUserList().flatMap { [weak self] users in
            if let user = users.last {
                return self.service.getPosts(user: user)
            } else {
                return Fail(error:APIError.emptyUsers).eraseToAnyPublisher()
            }
        }
        .sink { result in
            
        }
    }
}

Run Code Online (Sandbox Code Playgroud)

有没有更优雅的方式来使用 async/await,所以代码可以类似于

class ViewModel: ObserableObject {
    let service = MyService()
    
    @published var post: [Post] = []
    
    func fetchAllPostFromLastUser() {
        let users = await service.getUserList().somethingMagicToConvertPublisherToAsync()
        let posts = await service.getPostList(user: user.first).somethingMagicToConvertPublisherToAsync()
    }
}

Run Code Online (Sandbox Code Playgroud)

Asp*_*eri 2

要使用async/await我们需要转换PublisherAsyncStream值,因此消除错误。

所以,有可能像(用 Xcode 13.4 测试)

@MainActor class ViewModel: ObserableObject {
    // ...

    func collectPosts() async {
        for await newUsers in service.getUserList().replaceError(with: []).values {
            for user in newUsers {
                for await newPost in service.getPost(user: user).replaceError(with: []).values {
                    post.append(contentsOf: newPost)
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

和用法就像

struct ContentView: View {
    @StateObject var vm = ViewModel()

    var body: some View {
        YourViewHere()
            .task {
                await vm.collectPosts()
            }
    }
}
Run Code Online (Sandbox Code Playgroud)