A typical way to chain AnyPublisher is to use Combine operators like 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
}
}
}
Is there a more elegant way to use async/await, so the code can be similar like
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()
}
}
To use async/await
we need to convert Publisher
into AsyncStream
of values, so get rid of errors.
So, it is possible to be like (tested with 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)
}
}
}
}
and usage like
struct ContentView: View {
@StateObject var vm = ViewModel()
var body: some View {
YourViewHere()
.task {
await vm.collectPosts()
}
}
}