본문 바로가기
Project/SwiftUI 블록와이드

[SwiftUI Project] 코인 검색하기 Filtering with Combine

by iOS_woo 2022. 9. 17.

유저가 입력한 문자로 데이터를 필터링하여 보여줍니다.

 

API에서 받은 코인 리스트 중에 유저가 원하는 키워드를 갖고 있는 코인만 보여주는 기능입니다. 

 

HomeViewModel.swift 에 filtering  함수를 작성해줍니다. 

import Foundation
import Combine

class HomeViewModel: ObservableObject {
    
    @Published var allCoins: [CoinModel] = []
    @Published var searchCoins: [CoinModel] = []  <- 필터링 된 리스트를 받을 어레이
    @Published var portfolioCoins: [CoinModel] = []
    
    @Published var searchText: String = ""
    
    private let dataService = CoinDataService()
    private var cancellables = Set<AnyCancellable>()
    
    init() {
        addSubscribers()
    }
    
    func addSubscribers() {
        
        // AllCoins Update
        dataService.$allCoins
            .sink { [weak self] (returnedCoins) in
                self?.allCoins = returnedCoins
            }
            .store(in: &cancellables)
        
        // Search Filtering
        $searchText // Subcriber
            .combineLatest(dataService.$allCoins) // Subcriber
            .debounce(for: .seconds(0.3), scheduler: DispatchQueue.main)
                // .debounce는 유저가 아무리 빠르게 타이핑해도 0.3초에 한번씩만 수행되도록 한다. 만약 설정하지 않는다면 유저가 빠르게 10자를 타이핑할 시 10번을 새롭게 수행하게 된다. 때문에 큰 데이터라면 좋지 않다.
            .map(filterCoins)
            .sink { [weak self] (returnCoins) in
                self?.searchCoins = returnCoins
            }
            .store(in: &cancellables)
    }
    
    private func filterCoins(text: String, coins: [CoinModel]) -> [CoinModel] {
        
        // text가 비어있지 않을 때만 계속 진행, 비어있다면 coins를 리턴하라
        guard !text.isEmpty else {
            return coins
        }
        
        // 입력된 문자를 소문자로 변환
        let lowercasedText = text.lowercased()
        
        // StartingCoins를 순회하며 조건값으로 필터링하고 리턴하라
        return coins.filter { (coin) -> Bool in
            
            // 요소의 name, symbol, id에 lowercasedText를 포함한 것을 반환하라
            return coin.name.lowercased().contains(lowercasedText) || // || 는 or "혹은"이라는 뜻
            coin.symbol.lowercased().contains(lowercasedText) ||
            coin.id.lowercased().contains(lowercasedText)
        }
    }
}

흐름을 정리한다면 

1. 두가지 Subscriber를 통해 유저가 입력할 텍스트필드의 내용과 코인리스트를 가져옵니다. 

2. debounce를 작성하여 혹시나 발생할 수 있는 유저의 과입력(?)을 방지해줍니다. 

3. map(filterCoins)를 통해 filterCoins의 조건으로 코인 리스트를 필터링합니다. 

4. sink로 searchCoins 어레이에 최종 전달! 

 

 

이번 필터링을 구현할 때의 공부는 배운게 많고 유익했습니다. 

 

이로써 검색화면의 기능들이 거의 다 구현된 것 같아서 기쁘네요! ^^ 

댓글