본문 바로가기

IOS

Swift - ReactorKit / ErrorHandling

https://moonggi-dev-story.tistory.com/49 글에 이어서 ReactorKit 에 대해 적도록 하겠습니다.

Http 통신중에 발생할수 있는 오류에 대해 ReactorKit으로 처리할수 있는 방법을 적겠습니다.

 

func mutate(action: Action) -> Observable<Mutation> {
        switch action {
            case .http:
                
                return Observable.concat([
                    Observable.just(Mutation.setLoading(true)),
                    setHttp().map{Mutation.setResult($0)},
                    Observable.just(Mutation.setLoading(false))
                ])
        }
    }

해당 프로세스는 로딩(true) -> Http 통신 완료후 -> 로딩(false) 와 같은 순서를 보이고 있고

Http 통신중에 오류가 발생하면 중간에 끊깁니다.

setHttp 상에서 오류를 처리하여 해당 프로세스를 진행 할수 있습니다. 어케보면 RxSwift 지식이 있다면 처리 할수 있는 문제입니다.


1. Mutate

enum Mutation {
	case setLoading(Bool)
	case setResult(String)
	case onError(ConnectError)
}
    func mutate(action: Action) -> Observable<Mutation> {
        switch action {
            case .http:
                
                return Observable.concat([
                    Observable.just(Mutation.setLoading(true)),
                    setHttp().map{Mutation.setResult($0)}.catchAndReturn(Mutation.onError(ConnectError.APIException)),
                    Observable.just(Mutation.setLoading(false))
                ])
        }
    }

setHttp 함수상에서 onError 가 발생하면 다음과 같이 동작하며, concat의 동작도 계속 이어갈수 있습니다

에러 발생 관련 메세지 받는방법은 State 선언하여 해당 변수를 구독 합니다.


2. State

struct State {
	var isLoading : Bool = false
	var text : String = ""
	var errorResult = ConnectError.DefaultError
}
func reduce(state: State, mutation: Mutation) -> State {
  switch mutation {
    case let .setLoading(val):
      var state = state
      state.isLoading = val
      return state
    case let.setResult(val):
      var state = state
      state.text = val
      return state
    case let.onError(error):
      var state = state
      state.errorResult = error
      return state
  }
}

3. View

reactor.state.map{
    $0.errorResult
}.distinctUntilChanged()
.filter{ $0 != .DefaultError }
.subscribe(onNext : {
    error in
    print("에러 발생 :\(error)")
}).disposed(by: disposeBag)

기본값인 DefaultError 를 제외하고 filter 상에서 걸리게 처리하였다.

reactorkit 특성상 구독을 걸어 놓으면 errorResult 값이 바뀌지 않아도 다른 state 값이 바뀌면 그전 값도 같이 내려오게 된다.

그래서 항상 distinctUntilChanged() 를 걸어놓는다.

하지만 로그인 alert 팝업이라던가, 똑같은 값이 두번 내려올경우에 구독을 못받을수가 있습니다.

해당 내용은 임의의 값을 추가하여 다음과 같이 처리 할수 있습니다.


4. ReactorValue

struct ReactorValue<T> {
    var revision : Int = 0
    var value : T?
}

5. State

struct State {
	var revision = 0
	var isLoading : Bool = false
	var text = ReactorValue<String>(revision: 0, value: nil)
	var errorResult = ReactorValue<ConnectError>(revision: 0, value: ConnectError.DefaultError)
}
func reduce(state: State, mutation: Mutation) -> State {
    switch mutation {
        case let .setLoading(val):
            var state = state
            state.isLoading = val
            return state
        case let.setResult(val):
            var state = state
            state.revision = state.revision + 1
            state.text = ReactorValue(revision: state.revision, value: val)
            return state
        case let.onError(error):
            var state = state
            state.revision = state.revision + 1
            state.errorResult = ReactorValue(revision: state.revision, value: error)
            return state
    }
}

6. View

reactor.state.map{ $0.text }
    .distinctUntilChanged {
        $0.revision == $1.revision
    }
    .subscribe(onNext : {
        value in
        self.resultTextView.text = "\(value.value)"
    }).disposed(by: disposeBag)

reactor.state.map{
    $0.errorResult
}.filter {
    $0.value != .DefaultError
}
.distinctUntilChanged {
    $0.revision == $1.revision
}
.subscribe(onNext : {
    error in
    print("에러 발생 :\(error.value)")
}).disposed(by: disposeBag)

다음과 같이 revision 값을 추가하여 실제 값을 reduce 할때만 추가해서 보냅니다.

view 에서 구독 받을땐 해당 기존 revision 값과 현재 값을 비교하여 틀릴 경우에만 값을 적용 합니다.

 

참고 : https://github.com/ReactorKit/ReactorKit/issues/34

 

Error Handling · Issue #34 · ReactorKit/ReactorKit

Any document about how to handle errors?

github.com

 

'IOS' 카테고리의 다른 글

Swift - Property Wrapper  (0) 2021.08.06
Swift - Reactive Programming (Rx)  (0) 2021.08.02
Swift - iOS 면접 질문 리스트  (0) 2021.07.18
Swift - UICollectionView + Paging  (0) 2021.07.13
Swift - SceneDelegate  (0) 2021.07.10