John O'Reilly
John O'Reilly

Reputation: 10330

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

Upvotes: 1

Views: 2533

Answers (1)

John O'Reilly
John O'Reilly

Reputation: 10330

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) }
    }
}

Upvotes: 1

Related Questions