'Using Combine Publisher with AsyncSequence

I'm currently migrating code that was using Combine Publisher to an AsyncSequence. I previously used this alongside @Published search query that user could type in and now trying to "combine" that search term with AsyncSequence based data source such as following (using values to convert the search query to AsyncSequence as well). However, I'm only seeing the flatMap code being executed once initially.

@MainActor
class FantasyPremierLeagueViewModel: ObservableObject {
    @Published var playerList = [Player]()
    @Published var query: String = ""
    
    private let repository: FantasyPremierLeagueRepository
    init(repository: FantasyPremierLeagueRepository) {
        self.repository = repository
        
        Task {
            let playerStream = asyncStream(for: repository.playerListNative)
            
            let filteredPlayerStream = $query
                .debounce(for: 0.5, scheduler: DispatchQueue.main)
                .values
                .flatMap { query in
                    playerStream
                        .map { $0.filter { uery.isEmpty || $0.name.contains(query) } }
                }
                .map { $0.sorted { $0.points > $1.points } }
                
            
            for try await data in filteredPlayerStream {
                self.playerList = data
            }
        }
    }
}

Code pushed to branch and can also be viewed in https://github.com/joreilly/FantasyPremierLeague/blob/kmp_native_coroutines/ios/FantasyPremierLeague/FantasyPremierLeague/ViewModel.swift



Solution 1:[1]

Ok, looks like this can be done in a much cleaner way using combineLatest() from new Swift Async Algorithms package (https://github.com/apple/swift-async-algorithms).

Task {
    let playerStream = asyncStream(for: repository.playerListNative)
        .map { $0.sorted { $0.points > $1.points } }

    for try await (players, searchQuery) in combineLatest(playerStream, $query.values) {
        self.playerList = players
            .filter { searchQuery.isEmpty || $0.name.localizedCaseInsensitiveContains(query) }
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 John O'Reilly