[SwiftUI] CryptoApp: Sorting Feature

Junyoung Park·2022년 11월 3일
0

SwiftUI

목록 보기
86/136
post-thumbnail

Sorting List data with MVVM and animations | SwiftUI Crypto App #17

CryptoApp: Sorting Feature

구현 목표

  • 데이터 정렬 기능 구현

구현 태스크

  • 정렬 옵션 추가
  • 옵션 퍼블리셔에 따른 정렬 데이터 매핑 함수 구현
  • 정렬 옵션 선택 UI

핵심 코드

    enum SortOption {
        case rank, rankReversed, holdings, holdingsReversed, price, priceReversed
    }
  • 정렬 옵션 이넘을 통해 관리
    @Published var sortOption: SortOption = .holdings
  • 뷰 모델의 정렬 옵션을 퍼블리셔화
  • 퍼블리셔를 구독함으로써 정렬 조건이 바뀔 때마다 실제 데이터 정렬 가능
        $searchText
            .combineLatest(coinDataService.$allCoins, $sortOption)
            .debounce(for: .seconds(0.5), scheduler: DispatchQueue.main)
            .map(filterAndSortCoins)
            .sink { [weak self] coinModels in
                self?.allCoins = coinModels
            }
            .store(in: &cancellables)
  • combineLatest를 통해 정렬 옵션 퍼블리셔를 함께 관리
  • filterAndSortCoins를 통해 정렬된 코인 데이터 리턴
private func filterAndSortCoins(text: String, coins: [CoinModel], sort: SortOption) -> [CoinModel] {
        var updatedCoins = filterCoins(text: text, coins: coins)
        sortCoins(sort: sort, coins: &updatedCoins)
        return updatedCoins
    }
    
    private func sortCoins(sort: SortOption, coins: inout [CoinModel]) {
        switch sortOption {
        case .rank, .holdings:
            coins.sort(by: {$0.rank < $1.rank})
        case .rankReversed, .holdingsReversed:
            coins.sort(by: {$0.rank > $1.rank})
        case .price:
            coins.sort(by: {$0.currentPrice > $1.currentPrice})
        case .priceReversed:
            coins.sort(by: {$0.currentPrice < $1.currentPrice})
        }
    }
    
    private func sortPortfoiloCoinsIfNeeded(coins: [CoinModel]) -> [CoinModel] {
        switch sortOption {
        case .holdings:
            return coins.sorted(by: {$0.currentHoldingsValue > $1.currentHoldingsValue })
        case .holdingsReversed:
            return coins.sorted(by: {$0.currentHoldingsValue < $1.currentHoldingsValue })
        default: return coins
        }
    }
  • 필터링된 코인을 다시 정렬 옵션에 따라 정렬
  • 포트폴리오 코인은 allCoins와 연관되어 있기 때문에 별도로 관리
        $allCoins
            .combineLatest(portfolioDataService.$savedEntities)
            .map(mapAllCoinsToPortfolioCoins)
            .sink { [weak self] coinModels in
                guard let self = self else { return }
                self.portfolioCoins = self.sortPortfoiloCoinsIfNeeded(coins: coinModels)
            }
            .store(in: &cancellables)
  • 퍼블리셔 호출 관계에 따라 allCoins이 변경되기 때문에 해당 구독 코드 호출
  • sortPortfolioIfNeeded 함수 단에서 현재 뷰 모델 내 전역으로 관리하고 있는 sortOption을 통해 정렬된 포트폴리오 코인 데이터 모델 리턴
HStack(spacing: 4) {
                Text("Coin")
                Image(systemName: "chevron.down")
                    .opacity((viewModel.sortOption == .rank || viewModel.sortOption == .rankReversed) ? 1.0 : 0.0)
                    .rotationEffect(Angle(degrees: viewModel.sortOption == .rank ? 0 : 180))
            }
            .onTapGesture {
                withAnimation(.default) {
                    viewModel.sortOption = viewModel.sortOption == .rank ? .rankReversed : .rank
                }
            }
  • 현재 선택된 정렬 조건을 보여주는 UI
  • 선택에 따라 애니메이션 효과

구현 화면

profile
JUST DO IT

0개의 댓글