[RxSwift #6] Traits(1) - RxSwift Traits

2022. 1. 7. 16:24ReactiveX

Traits는 RxSwift의 traits와 RxCocoa에 포함되어 있는 traits로 나뉜다. 이번 글에서는 먼저 RxSwift의 traits에 관해 공부한 내용을 작성할 예정이다:)

 

먼저 Traits가 무슨 뜻인지, 처음에 전혀 유추할 수 없었다. 한글 그대로의 뜻을 찾아봐도 '특성'이라는 해석인데... 전혀 감이 잡히지 않는다. 그래서 RxSwift에서의 traits는 대체 무슨 뜻일까? ReactiveX에 설명된 내용을 해석해보자면, 인터페이스의 경계를 넘어 옵저버블 시퀀스의 속성을 전달하고 보장할 뿐만 아니라 컨텍스트 의미를 제공하고, 원시 옵저버블과 비교할 때보다 구체적인 사용 사례를 대상으로 할 때 유용한 것이라고 한다. 

 

너무 복잡하니깐 한 줄로 정리하면 원시 Observable보다 rx를 더욱 직관적으로 사용할 수 있도록 하는 장치라고 이해할 수 있다. 따라서 이걸 사용하는 것은 의무까지는 아니고, Observable로도 구현할 수 있을 것이다.

 

RxSwift의 traits에는 Single, Completable, Maybe 세 가지가 있다. 지금부터 이것들을 하나씩 알아보자.

 

Single

먼저 Single이라는 trait에 대해 알아보자. 원시 Observable은 여러 요소들을 방출하고 completed 또는 error를 발생시킬 수 있다. 하지만 Single은 하나의 요소 또는 에러만을 방출할 수 있다. 즉 success 또는 error를 방출한다.

 

사용법은 다음과 같다. Create를 통해 Single을 생성하여 요청에 대한 응답을 방출하는 코드이다. 

func getRepo(_ repo: String) -> Single<[String: Any]> {
    return Single<[String: Any]>.create { single in
        let task = URLSession.shared.dataTask(with: URL(string: "https://api.github.com/repos/\(repo)")!) { data, _, error in
            if let error = error {
                single(.error(error))
                return
            }

            guard let data = data,
                  let json = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves),
                  let result = json as? [String: Any] else {
                single(.error(DataError.cantParseJSON))
                return
            }

            single(.success(result))
        }

        task.resume()

        return Disposables.create { task.cancel() }
    }
}

이와 같이 single은 단일 HTTP 응답을 방출하는 데에 많이 사용한다. 그렇다면 Single을 관찰하는 입장에서는 어떻게 사용하게 될까?

getRepo("ReactiveX/RxSwift")
    .subscribe { event in
        switch event {
            case .success(let json):
                print("JSON: ", json)
            case .error(let error):
                print("Error: ", error)
        }
    }
    .disposed(by: disposeBag)

Observable과 마찬가지로, subscribe를 통해 어떤 이벤트가 발생했는지 관찰할 수 있다. 

Observable을 Single로 바꾸고 싶을 때는 .asSingle()을 이용할 수 있다.

 

Completable

Completable은 Single과 달리 completed나 error만 발생시킬 수 있다. 요소는 방출하지 않는다. 

Completable도 역시 다음과 같이 create를 통해 생성된다.

func cacheLocally() -> Completable {
    return Completable.create { completable in
       // Store some data locally
       ...
       ...

       guard success else {
           completable(.error(CacheError.failedCaching))
           return Disposables.create {}
       }

       completable(.completed)
       return Disposables.create {}
    }
}

위의 예시와 같이 데이터의 저장할 때 등 성공과 실패 여부를 방출하고 싶을 때 사용할 수 있다.

 

Completable을 관찰하는 관찰자는 다음과 같이 작성된다.

cacheLocally()
    .subscribe { completable in
        switch completable {
            case .completed:
                print("Completed with no error")
            case .error(let error):
                print("Completed with an error: \(error.localizedDescription)")
        }
    }
    .disposed(by: disposeBag)

이 역시 subscribe를 통해서 관찰을 시작할 수 있다.

Observable을 Completable로 바꾸고 싶을 때는 .asCompletable()을 이용할 수 있다.

 

Maybe

Maybe는 위에 등장했던 Single과 Completable을 합쳐놓은 성향을 갖는다. Maybe가 동작하는 방법은 세 가지인데, 첫 번째는 단일 요소를 방출하는 것(success)이고, 두 번째는 요소 방출 없이 complete를 방출하는 것, 세 번째는 요소 방출 없이 error를 방출하는 것이다. 

 

Maybe를 생성하는 방법은 다음과 같다. 

func generateString() -> Maybe<String> {
    return Maybe<String>.create { maybe in
        maybe(.success("RxSwift"))

        // OR

        maybe(.completed)

        // OR

        maybe(.error(error))

        return Disposables.create {}
    }
}

이것도 역시 create를 이용해 생성하는 것을 볼 수 있다. 

 

관찰을 시작하는 법은 다음과 같다.

generateString()
    .subscribe { maybe in
        switch maybe {
            case .success(let element):
                print("Completed with element \(element)")
            case .completed:
                print("Completed with no element")
            case .error(let error):
                print("Completed with an error \(error.localizedDescription)")
        }
    }
    .disposed(by: disposeBag)

Observable을 Maybe로 바꾸고 싶을 때에는 .asMaybe()를 이용할 수 있다.

 

 

https://github.com/ReactiveX/RxSwift/blob/main/Documentation/Traits.md

 

 

GitHub - ReactiveX/RxSwift: Reactive Programming in Swift

Reactive Programming in Swift. Contribute to ReactiveX/RxSwift development by creating an account on GitHub.

github.com

'ReactiveX' 카테고리의 다른 글

[RxSwift #5] Driver란 무엇일까?  (0) 2022.01.01
[RxSwift #4] Subject 와 Relay  (2) 2021.09.22
[RxSwift #3] Scheduler  (0) 2021.09.20
[RxSwift #2] Observable에 대해  (0) 2021.09.19
[RxSwift #1] 자주 쓰이는 용어와 개념  (0) 2021.09.17