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)
.tryMap({try handleURLResponse(output: $0, url: url)})
.retry(3)
.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)
}
구현 화면