[SwiftUI] CryptoApp: Additional Optimizations

Junyoung Park·2022년 11월 5일
0

SwiftUI

목록 보기
94/136
post-thumbnail

Adding final optimizations | SwiftUI Crypto App #25

CryptoApp: Additional Optimizations

구현 목표

  • SwiftUI가 제공하는 멀티 디바이스 환경에 대한 옵티마이제이션 적용

구현 태스크

  • 아이패드 디바이스 지원
  • 메인/백그라운드 태스크 스레드 최적화
  • 빈 뷰의 경우 가이드라인을 통한 더 나은 UX 제공

핵심 코드

var body: some Scene {
        WindowGroup {
            ZStack {
                NavigationView {
                    HomeView()
                        .toolbar(.hidden)
                }
                .navigationViewStyle(.stack)
                .environmentObject(viewModel)
                ZStack {
                    if showLaunchView {
                        LaunchView(showLaunchView: $showLaunchView)
                            .transition(.move(edge: .leading))
                    }
                }
                .zIndex(2.0)
            }
        }
    }
  • SwiftUI가 제공하는 강력한 멀티 디바이스 지원 기능에 더불어 아이패드에서 아이폰 환경과 같은 뷰 환경을 제공하기 위해서는 네비게이션 스타일만 스택으로 강제하면 된다.
static func download(with url: URL) -> AnyPublisher<Data, Error> {
        return URLSession
            .shared
            .dataTaskPublisher(for: url)
//            .subscribe(on: DispatchQueue.global(qos: .default))
            .tryMap({try handleURLResponse(output: $0, url: url)})
            .retry(3)
//            .receive(on: DispatchQueue.main)
            .eraseToAnyPublisher()
    }
  • 기존의 비동기 네트워킹을 담당하는 함수
  • URLSession을 사용하는 디폴트 스레드가 background이기 때문에 receive로 두 번 선언할 필요가 없기 때문에 주석 처리
  • retry를 통한 네트워킹 실패 시 대처
func fetchCoins() {
        guard let url = URL(string: "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=1&sparkline=true&price_change_percentage=24h") else { return }
        coinSubscription = NetworkingManager
            .download(with: url)
            .decode(type: [CoinModel].self, decoder: JSONDecoder())
            .receive(on: DispatchQueue.main)
            .sink(receiveCompletion: NetworkingManager.handleCompletion,
                  receiveValue: { [weak self] coinModels in
                self?.allCoins = coinModels
                self?.coinSubscription?.cancel()
            })
    }
  • 위의 download 함수를 사용해 내려받은 데이터를 디코딩, 이후 UI를 그리기 위해 던져주는 퍼블리셔 부분
  • decode가 많은 리소스가 필요한 작업이기 때문에 download에서 메인 스레드로 받은 이후 decode를 하게 되면 연산량이 큼 → 각 데이터에 대한 적절한 decode가 끝난 뒤 receive롤 메인 스레드 태스크 변경
if showPortfolio {
                    ZStack(alignment: .top) {
                        if viewModel.portfolioCoins.isEmpty && viewModel.searchText.isEmpty {
                            portfolioEmptyView
                        } else {
                            portfolioCoinsList
                        }
                    }
                    .transition(.move(edge: .trailing))
                }
  • 홈 뷰에서 사용자가 추가한 코인 목록이 없을 경우 빈 화면보다 가이드라인 뷰를 제공하는 게 보다 좋은 UX
private var portfolioEmptyView: some View {
        Text("You haven't added any coins to your portfolio yet.\nClick + button to get started.")
            .font(.callout)
            .foregroundColor(Color.theme.accent)
            .fontWeight(.medium)
            .multilineTextAlignment(.center)
            .padding(50)
    }
  • 어떤 작업을 해야 할지 전달 가능

구현 화면

profile
JUST DO IT

0개의 댓글