Published 2022. 12. 28. 16:47

Traits란 기존의 Observable의 wrapper 구조체로, 더 제한적인 기능을 수행하는 Observable이다. 

public typealias Single<Element> = PrimitiveSequence<SingleTrait, Element>
public typealias Completable = PrimitiveSequence<CompletableTrait, Swift.Never>
public struct PrimitiveSequence<Trait, Element> {
    let source: Observable<Element>

    init(raw: Observable<Element>) {
        self.source = raw
    }
}

즉, Observable보다 좁은 범위의 기능을 수행하기에,

Traits를 사용하지 않고 Observable로 커버가 가능하지만, 코드가 더 명확해진다는 장점이 있다. 

 

 

Single

기존 Observable에서는 next, completed, error 이벤트가 있는 반면, 

Single에는 success, failure 2가지 이벤트 밖에 존재하지 않는다. 

 

또한, 이름에서 알 수 있듯이 하나의 이벤트만 방출하고 종료한다.

  • success는 일반 Observable의 next + completed와 같은 역할이고 
  • failureerror이벤트와 같은 역할이다
public static func create(subscribe: @escaping (@escaping SingleObserver) -> Disposable) -> Single<Element> {
    let source = Observable<Element>.create { observer in
        return subscribe { event in
            switch event {
            case .success(let element): // success 이벤트 
                observer.on(.next(element)) 
                observer.on(.completed)
            case .failure(let error): // failure 이벤트
                observer.on(.error(error))
            }
        }
    }
    
    return PrimitiveSequence(raw: source)
}

위의 코드를 보면 success이벤트는 next후에 바로 completed 이벤트가 방출되는 것을 알 수 있는데, 

따라서, 하나의 이벤트만 방출하고 종료하게 된다.

또한, failure시에는 error이벤트로 인해, Observable이 종료가 된다.

 

생성은 Observable과 별반 다를 바 없지만, 

func single() -> Single<String> {
    return Single.create { single in
        
        single(.success("1"))
        single(.failure(MyError.someError))
        
        return Disposables.create()
        
    }
}

기존 Observable의 경우에는 .onNext()  와 같은 형태로 데이터를 전달하였다면, 

Single에서는 (.success()) 와 같은 형태로 전달하는데, 

이러한 이유 역시 Single의 Observable의 wrapper 구조체이기 때문이다. 

 

Single은 API Request와 같이 하나의 데이터 혹은 에러만 존재할 때 유용하게 쓰인다.

static func featchAllData(_ url: String) -> Single<Data> {
    
    return Single<Data>.create { single in
        URLSession.shared.dataTask(with: URL(string: url)!) { data, res, err in
            if let err = err {
                single(.failure(err))
                return
            }

            guard let data = data else {
                let httpsResponse = res as! HTTPURLResponse
                single(.failure(NSError(domain: "no data",
                                           code: httpsResponse.statusCode,
                                           userInfo: nil)))
                return
            }
            
            single(.success(data))
        }.resume()
        
        return Disposables.create()
    }
}

 

Observable을 asSingle()이라는 메서드를 통해 Single타입으로 변환하는 경우,

success 이벤트는 next 후에 completed를 전달하는 이벤트 이기 때문에, 

completed이벤트 전 시점에 next이벤트가 없다면 에러가 발생하게 된다.

 

 

Completable

Completablecompleted, error의 2가지 타입의 이벤트가 존재한다.

또한, 아무 요소도 방출하지 않는 것을 보장한다. 

public static func create(subscribe: @escaping (@escaping CompletableObserver) -> Disposable) -> PrimitiveSequence<Trait, Element> {
    let source = Observable<Element>.create { observer in
        return subscribe { event in
            switch event {
            case .error(let error):
                observer.on(.error(error))
            case .completed:
                observer.on(.completed)
            }
        }
    }
    
    return PrimitiveSequence(raw: source)
}

 

또한, 요소를 방출하지 않기 때문에 타입을 명시하지 않는다.

func completable() -> Completable {
    return Single.create { completable in
        
        completable(.completed)
        completable(.error(MyError.someError))
        
        return Disposables.create()
        
    }
}

 이는, 성공 혹은 실패 여부만 알고 싶은 경우 유용하게 사용된다.

 

 

Maybe

MaybeCompletable과 Single의 중간 특성을 가진다. 

따라서 success, completed, error 3가지 타입의 이벤트가 존재하며, 각각의 이벤트는 Completable, Single과 동일한 동작을 한다. 

public static func create(subscribe: @escaping (@escaping MaybeObserver) -> Disposable) -> PrimitiveSequence<Trait, Element> {
    let source = Observable<Element>.create { observer in
        return subscribe { event in
            switch event {
            case .success(let element):
                observer.on(.next(element))
                observer.on(.completed)
            case .error(let error):
                observer.on(.error(error))
            case .completed:
                observer.on(.completed)
            }
        }
    }
    
    return PrimitiveSequence(raw: source)
}

 

방출하는 요소가 있기 때문에, 제네릭 타입으로 방출할 요소의 타입을 지정해주어야 한다.

func maybe() -> Maybe<String> {
    return Single.create { maybe in
        
        maybe(.success("1"))
        maybe(.completed)
        maybe(.error(MyError.someError))
        
        return Disposables.create()
        
    }
}

 

Single의 경우 

기존의 Observable에서 .asSingle()메서드를 통해 Single로 변경할 경우, 

completed이벤트 이전에 next이벤트가 없을때 에러가 발생하였지만, 

Maybe의 경우는 

completed이벤트가 따로 존재하기 때문에 에러가 발생하지 않는다.

 

 

Reference

https://www.notion.so/Wallaby-RxSwift-72194669a39a4557baa69c672268af38

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

'iOS > RxSwift' 카테고리의 다른 글

[RxSwift] Operator(4) - Share  (0) 2023.07.24
[RxSwift] Operator(3) - Combining  (0) 2023.01.08
[RxSwift] Operator(2) - Transforming  (2) 2022.12.27
[RxSwift] Operator(1) - Filtering  (1) 2022.12.25
[RxSwift] Subjects  (0) 2022.12.01
복사했습니다!