
기존에 RxSwift ErrorTracker 를 통해 공통으로 요청에대한 에러를 처리하는 방법에 대해 정리해놓은것이 있다.
ErrorTracker를 선언해놓고, .trackError하는 방식으로 다음 코드와 같다 (ErrorTracker 정리에 상세히 정리되어 있음)
apiService.exampleLogin() // RxSwift Observable 요청
.trackActivity(self.activity) // RIBs 사용하는 경우 presenter.activity
.trackError(self.errorTracker) // RIBs 사용하는 경우 presenter.errorTracker
.subscribe(onNext: { [weak self] _ in
}).disposed(by: disposeBag)
장점은 여러 요청에 대해 한번에 대응이 가능하다는 것이다.
예를 들면 error가 났을 경우 error message에 대한 Alert만 보여주면 되는 경우 다음과 같이 대응하여 일괄로 처리가 가능하다.
// Way #1.
errorTracker.asObservable()
.subscribe(onNext: { [weak self] in
print($0.message)
// self?.openAlert($0.message)
}).disposed(by: disposeBag)
// Way #2.
errorTracker.asObservable()
.flatMap { [unowned self] in self.openAlert(msg: $0.message) }
.subscribe()
.disposed(by: disposeBag)
// openAlert 함수가 정의되어 있다고 가정
하지만 특정 요청의 경우 이 요청에 대한 에러가 났을 경우 Custom한 후속처리를 요구 할 수 있다.
이때 .catchError를 통해 대응했던 코드를 정리하고자 한다.
RIBs 정리 포스팅에서 SampleInteractor의 코드에서
ReactorKit를 활용하기 위해 Async라는, 말그대로 비동기 응답에 대한 Model를 만들어서 대응하는 코드가 있다.
extension SampleInteractor: Reactor {
func mutate(action: Action) -> Observable<Mutation> {
switch action {
case .requestLogin:
let reset = Observable.just(Mutation.setLoginResult(Async.uninitialized))
let update = apiService.requestLogin()
.asObservable()
.trackActivity(presenter.activity)
.map { _ in Mutation.setLoginResult(Async.success(Void())) }
.catchError { Observable.just(Mutation.setLoginResult(Async.error($0 as? CustomedError))) }
return Observable.concat(update, reset)
}
func reduce(state: State, mutation: Mutation) -> State {
var state = state
switch mutation {
case let .setLoginResult(result):
state.result = result
}
return state
}
}
Async Model 에 대한 코드는 다음과 같다.
import Foundation
enum State {
case uninitialized
case loading
case error
case success
}
private let asyncError = CustomedError(message: "async error")
enum Async<T>: Equatable {
case uninitialized
case loading
case error(CustomedError?)
case success(T)
var state: State {
switch self {
case .uninitialized: return .uninitialized
case .loading: return .loading
case .error: return .error
case .success: return .success
}
}
func successValue() throws -> T {
switch self {
case .success(let value): return value
default: throw asyncError
}
}
func errorValue() throws -> CustomedError {
switch self {
case .error(let error):
guard let error = error else { throw asyncError }
return error
default: throw asyncError
}
}
static func == (lhs: Async<T>, rhs: Async<T>) -> Bool {
return lhs.state == rhs.state
}
}
UIViewController 에서는 성공, 실패(에러)를 이런식으로 받아서 처리한다.
reactor.state.map { $0.result }
.distinctUntilChanged()
.filter { $0.state == State.success }
.map { try $0.successValue() }
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] _ in
// 성공처리
}).disposed(by: disposeBag)
reactor.state.map { $0.result }
.distinctUntilChanged()
.filter { $0.state == State.error }
.map { try $0.errorValue() }
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] error in
// 에러처리
print(error.message)
}).disposed(by: disposeBag)'iOS_RxSwift' 카테고리의 다른 글
| RxSwift 를 통한 UITableView 사용법에 대한 정리 (0) | 2022.02.01 |
|---|---|
| Subject, Relay 에 대한 활용 및 이해도 정리 (0) | 2022.01.24 |
| RxSwift ErrorTracker GitHub 코드 정리 (0) | 2022.01.11 |
| return Single<> 비동기 처리 (0) | 2022.01.11 |
| RxSwift ActivityIndicator GitHub 코드 정리 (0) | 2022.01.11 |