250x250
반응형
05-12 01:08
Today
Total
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Notice
Recent Posts
Recent Comments
Link
Archives
관리 메뉴

Bill Kim's Life...

[RxSwift] Traits(특성) : Single, Completable, Maybe 본문

CS(컴퓨터 과학)/RxSwift

[RxSwift] Traits(특성) : Single, Completable, Maybe

billnjoyce 2020. 10. 9. 11:28
728x90
반응형
RxSwift의 Traits의 기본 개념 및 특징에 대해서 살펴봅니다.

 

 

#. 개발 환경

  • Xcode 11.x 이상
  • Swift 5.x 이상
  • RxSwift 5.x 이상

 

 


 

 

Traits?

 

Swift는 애플리케이션의 정확성과 안정성 향상시키고 Rx를 보다 직관적이고 직접적인 경험으로 사용하는데 사용할수 있는 강력한 유형 시스템을 갖추고 있습니다. Traits는 모든 경계에서 사용할수 있는 원시 Observable과 비교할때 인터페이스 경계에서 observable 프로퍼티를 전달하고 보장하며, 문법적으로도 더 쉽고 구체적인 사용 사례를 타켓팅하는데 도움이 됩니다.(한마디로 코드적으로 간단하고 쉽게 Rx의 흐름을 파악할 수 있도록 도와주는 특성을 가진 객체들입니다.)

 

Traits은 struct 형태로 읽기전용 Observable sequence property와 함께 랩핑 되어있습니다.

struct Single<Element> {
    let source: Observable<Element>
}

struct Driver<Element> {
    let source: Observable<Element>
}

observable sequence에 대한 빌더 패턴 구현의 일종이라고 생각할수 있습니다. Trait이 만들어지면, asObservable()을 호출하면 그것을 흔한 observable seqeunce로 다시 변환합니다.

 

그렇다면 Traits의 종류가 무엇이 있는지 살펴보겠습니다.

 

 

 

 


 

 

Single

 

Single은 Observable의 변형으로 일련의 요소를 방출하는 대신 항상 단일 요소 또는 오류를 방출하도록 보장합니다.

Single의 큰 특징은 아래와 같습니다.

 

  • 정확히 하나의 요소 또는 error를 방출합니다.
  • 부수작용을 공유하지 않습니다.

따라서 Single의 사용 예로 보며 응답, 오류만 반환할수 있는 HTTP 요청을 수행하는데 사용되지만 단일요소를 사용하여 무한 스트림 요소가 아닌 단일 요소만 관리하는 경우에 사용할 수 있습니다.

 

 

 

 

Single을 만드는것은 Observable을 만드는것과 비슷합니다. 아래의 간단한 예제를 살펴 봅니다.

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() }
    }
}

// Alamofire 라이브러리 사용 예제
extension NetworkManager {
    func request<T: BaseResponseModelable>(_ url: Alamofire.URLConvertible, method: Alamofire.HTTPMethod, parameters: Alamofire.Parameters?, headers: Alamofire.HTTPHeaders?) -> Single<T> {
        return Single<T>.create { (single) -> Disposable in
            let request = self.sendRequest(url, method: method, parameters: parameters, headers: headers, handler: { (result: Result<T>) in
                switch result {
                case .success(let value):
                    single(.success(value))
                case .failure(let error):
                    single(.error(error))
                }
            })
            return Disposables.create {
                request.cancel()
            }
        }
    }
}

 

위의 함수는 아래와 같은 코드처럼 사용할 수 있습니다.

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)

 

또는 HTTP 요청 성공 시와 실패에 따른 처리를 subscribe(onSuccess:onError:) 을 통하여 아래처럼 사용할 수 있습니다.

getRepo("ReactiveX/RxSwift")
    .subscribe(onSuccess: { json in
                   print("JSON: ", json)
               },
               onError: { error in
                   print("Error: ", error)
               })
    .disposed(by: disposeBag)

subscription은 SingleEvent 타입의 요소를 포함하는 .success, .error일 수 있는 SingleEvent열거를 제공합니다. 첫번째 이벤트 이후에는 더이상 이벤트가 발생하지 않습니다.

 

또한 원본 Observable sequence에asSingle()를 사용하여 Single로 변경하여 사용이 가능합니다.

 

주의 사항

Stream에서 Single을 사용한다면 Single로 시작해야 합니다. Observable로 시작해서 중간에 asSingle로 바꿔 Single을 엮는다거나 하면 문제가 발생합니다. 이는 Single은 좁은 범위에서의 Observable 이기 때문입니다. Observable은 다량의 next 이벤트를 발행할 수 있으며, complete 또한 발행할 수 있습니다. 하지만 Single은 complete 이벤트를 발행할 수 없습니다. Single의 이벤트인 success 자체가 next, complete 두 개의 속성을 다 포함하고 있기 때문입니다.

 

 

 


 

 

 

Completable

 

Completable은 변화무쌍한 Observable 입니다. complete 하거나, error 를 방출하고, 아무 요소도 방출하지 않는것을 보장합니다.

  • 제로 요소 방출
  • 완료 이벤트 또는 에러 방출
  • 부수작용을 공유하지 않습니다.

Completable은 완료에 따른 요소에 신경쓰지 않은 경우 사용하면 유용합니다. 요소를 내보낼수 없는 경우 Observable를 사용하여 비교할수 있습니다.

 

 

 

그렇다면 Completable 생성 방법을 살펴보겠습니다.

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 {}
    }
}

 

생성한 위의 함수를 사용한 예제를 살펴보면 아래와 같습니다.

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)
    
    
    
cacheLocally()
    .subscribe(onCompleted: {
                   print("Completed with no error")
               },
               onError: { error in
                   print("Completed with an error: \(error.localizedDescription)")
               })
    .disposed(by: disposeBag)

구독은 CompletableEvent의 열거형을 제공합니다. 열거형은 .completed -> 오류없이 완료된 작업 또는 .error중 하나 일 수 있습니다. 첫번째 이벤트를 넘어서 더이상의 이벤트가 발생하지 않습니다.

 

 

 

 


 

 

 

Maybe

 

Maybe는 Single과 Completable사이에 있는 Observable의 변형입니다.

Maybe의 큰 특징은 아래와 같습니다.

 

  • 완료된 이벤트, 싱글 이벤트 또는 오류를 방출합니다.
  • 부수작용을 공유하지 않습니다.

Maybe를 요소를 방출하는 연사자 모델을 위해 사용할수 있습니다. 그러나 요소들을 방출하는것이 꼭 필요해야하는것은 아닙니다.

 

 

 

 

Maybe의 생성 또한  Observable 생성하는 것과 비슷합니다. 

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

        // OR

        maybe(.completed)

        // OR

        maybe(.error(error))

        return Disposables.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)

 

마찬가지로 subscribe(onSuccess:onError:onCompleted:)를 사용하면 아래와 같습니다.

generateString()
    .subscribe(onSuccess: { element in
                   print("Completed with element \(element)")
               },
               onError: { error in
                   print("Completed with an error \(error.localizedDescription)")
               },
               onCompleted: {
                   print("Completed with no element")
               })
    .disposed(by: disposeBag)

Observable sequence를 .asMaybe()를 사용하여 변환가능합니다.

 

 

 

 


 

 

RxCocoa's Traits

 

RxSwift에서는 특별하게 RxCocoa 컴포넌트에서 특별한 Trait를 제공합니다.

그 예로 Driver, Singnal, ControlProperty, ControlEvent 등이 있습니다.

 

해당 RxCocoa에서의 Traits는 추후 강좌에서 설명을 할 수 있도록 하겠습니다.

 

 

 

 


 

 

 

이상으로 RxSwift의 Traits의 기본 개념 및 특징에 대해서 살펴보았습니다.

 

 

 

감사합니다.

 

 

 

 


[참고 자료(References)]

 

[1] RxSwift: ReactiveX for Swift : github.com/ReactiveX/RxSwiftgithub.com/ReactiveX/RxSwift/blob/master/Documentation/GettingStarted.md

[2] Traits (formerly Units) : github.com/ReactiveX/RxSwift/blob/main/Documentation/Traits.md

[3] RxSwift. Traits 정리하기 : devmjun.github.io/archive/Traits

[4] [ReactiveX][RxSwift]Single Trait - 장단점 : minsone.github.io/programming/rxswift-single-traits 

[5] RxSwift Traits(특성) : brunch.co.kr/@tilltue/33

[6] iOS. RxSwift Traits에 관하여 알아봅시다. 어렵지 않아요... : mrgamza.tistory.com/647

[7] RxSwift Traits : medium.com/swift-india/rxswift-traits-5240965c4f12

[8] [RxSwift] Single Trait - wrapping network request : ntomios.tistory.com/11

[9] Traits : ntomios.tistory.com/11dukhovich.net/rxswift/traits/

[10] [RxSwift] Observable이란 - (2) : jinnify.tistory.com/50

728x90
반응형
Comments