2024.04.16 - [◽️ Programming/iOS] - iOS Result 타입 (예외처리)
이전에 한번 알아보았던 Result Type에 대해서 다시 한번 짚고 넘어가보자 정의를 한번 다시 정리하고 이번에 Modern CollectionView를 만들었던 영화 정보를 가져오는 방식에 적용해보려고 한다!
먼저 Result 타입은 Swift5.0에서 새로 추가된 에러처리 방법이다. 에러가 발생하는 경우 에러를 따로 외부로 던지는 것이 아니라 리턴 타입을 리절트 타입으로 구현해서 함수 실행의 성공과 실패를 담아 리턴한다.
이를 통해서 do catch 를 사용해서 에러를 처리하는 방식보다 훨씬 가독성과 유지보수성을 향상시킬 수 있고 비동기 작업에서 오류 발생할 가능성이 적다.
정의는 이정도로 알아보고 이제 이전에 영화 정보 API를 가져와 컬렉션 뷰에 적용했던 방식에서 한번 알아보자
import Foundation
import RxSwift
class ViewModel {
let disposeBag = DisposeBag()
private let tvNetwork: TVNetwork
private let movieNetwork: MovieNetwork
init() {
let provider = NetworkProvider()
tvNetwork = provider.makeTVNetwork()
movieNetwork = provider.makeMovieNetwork()
}
struct Input {
let tvTrigger: Observable<Void>
let movieTrigger: Observable<Void>
}
struct Output {
let tvList: Observable<[TV]>
let movieResult: Observable<Result<MovieResult, Error>>
}
func transform(input: Input) -> Output {
let tvList = input.tvTrigger.flatMapLatest {[unowned self] _ -> Observable<[TV]> in
return self.tvNetwork.getTopRatedList().map{ $0.results }
}
let movieResult = input.movieTrigger.flatMapLatest { [unowned self] _ -> Observable<Result<MovieResult, Error>> in
return Observable.combineLatest(self.movieNetwork.getUpcomingList(), self.movieNetwork.getPopularList(), self.movieNetwork.getNowPlayingList()) { upcoming, popular, nowPlaying -> Result<MovieResult, Error> in
.success(MovieResult(upcomming: upcoming, popular: popular, nowPlaying: nowPlaying))
}.catch { error in
return Observable.just(.failure(error))
}
}
return Output(tvList: tvList, movieResult: movieResult)
}
}
RxSwift를 활용해 OutPut으로 영화 버튼을 클릭해 영화 정보를 가져오는 시점에서 API 정보를 제대로 가져오는지, 오류가 있다면 앱이 종료되지 않고 에러 메세지를 남길 수 있도록 Result 타입을 활용해 구현해보았다.
struct Output {
let tvList: Observable<[TV]>
let movieResult: Observable<Result<MovieResult, Error>>
}
먼저 아웃풋 부분에 영화 정보를 클릭하게 되면 MovieResult 의 값을 가져와 데이터를 뿌려주는데 해당 값이 오류가 있다면 Error를 표현할 수 있도록 구현했다.
let movieResult = input.movieTrigger.flatMapLatest { [unowned self] _ -> Observable<Result<MovieResult, Error>> in
return Observable.combineLatest(self.movieNetwork.getUpcomingList(), self.movieNetwork.getPopularList(), self.movieNetwork.getNowPlayingList()) { upcoming, popular, nowPlaying -> Result<MovieResult, Error> in
.success(MovieResult(upcomming: upcoming, popular: popular, nowPlaying: nowPlaying))
}.catch { error in
return Observable.just(.failure(error))
}
}
이렇게 리턴 값에 리절트 타입을 넣어줌으로써 정상적으로 데이터를 받게된다면 .success 에 값이 들어가고 오류가 있다면 .just를 활용해 오류 값을 넣어주면 된다.
output.movieResult.bind { [weak self] movieResult in
print("Movie Result \\(movieResult)")
switch movieResult {
case .success(let movieResult):
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
let bigImageList = movieResult.nowPlaying.results.map { movie in
return Item.bigImage(movie)
}
let bannerSection = Section.banner
snapshot.appendSections([bannerSection])
snapshot.appendItems(bigImageList, toSection: bannerSection)
let horizontalSection = Section.horizontal("Popular Movies")
let normalList = movieResult.popular.results.map { movie in
return Item.normal(Content(movie: movie))
}
snapshot.appendSections([horizontalSection])
snapshot.appendItems(normalList, toSection: horizontalSection)
let upcomingSection = Section.vertical("UpComing Moives")
let upcomingList = movieResult.upcomming.results.map { movie in
return Item.list(movie)
}
snapshot.appendSections([upcomingSection])
snapshot.appendItems(upcomingList, toSection: upcomingSection)
self?.dataSource?.apply(snapshot)
case .failure(let error):
print(error)
}
그 다음 VM에서 Result Type으로 output을 구성해주었기 때문에 VC에서 데이터를 바인드 할 때 해당 값을 그대로 사용하여 성공적으로 데이터가 전달되었다면 snapshot을 통해 diffabledatasource를 받아 정상적으로 구현이 가능해지며,
.failure를 통해 값이 정상적으로 받아지지 않는다면 error를 호출할 수 있게 된다.
이토록 Result 타입을 통해 에러를 관리해주면 훨씬 수월하고 가독성있게 에러를 관리 해줄 수 있다.!
오늘은 여기까지!
'◽️ Programming > T I L' 카테고리의 다른 글
PHAsset에 대해서 알아보자 (0) | 2024.08.13 |
---|---|
[Combine] assign과 sink의 차이점에 대해서 알아보자 (0) | 2024.08.07 |
SwiftData 에 대해서 알아보자 (0) | 2024.08.03 |
ModernCollectionView 구현하기 ( CompositionalLayout , DiffableDataSource , RxSwift ) (0) | 2024.08.01 |
Hashable 을 사용하는 이유가 뭘까 (0) | 2024.07.25 |