Danny의 iOS 컨닝페이퍼
article thumbnail
Published 2023. 3. 6. 23:45
[iOS/RxSwift] Traits에 대하여 RxSwift

Traits

Observable이 파생된 형태로 create시 제한적인 이벤트를 사용하고 하고 싶을 때 사용합니다.

 

간단히 말해 Observable 생성 시

이벤트 onNext, onError, onCompleted를 모두 처리하는 것이 아니라 필요한 이벤트만 사용함.

 

Traits도 엄연히 Observable이고

Traits을 통해 필요한 이벤트만 사용하여 코드를 조금 더 직관적이고 의도를 명확하게 해 주도록 도와줍니다.

 

종류

RxSwift에는 Single, Completable, Mabye

RxCocoa는 Driver, Signal

 

오늘은 RxSwift의 Single, Completable, Mabye만 알아보겠습니다.

 

Traits의 구현은

각각의 PrimitiveSequence의 create를 통해 Observable을 생성합니다.

설명이 너무 광범위해서... 자세한 내용은 정의를 한번 찾아보세요.

사용방법만 알아봅시다!

public typealias Single<Element> = PrimitiveSequence<SingleTrait, Element>
public typealias Completable = PrimitiveSequence<CompletableTrait, Swift.Never>
public typealias Maybe<Element> = PrimitiveSequence<MaybeTrait, Element>

// 이와같이 Traits은 Observable 타입을 감싸고 있는 Wrapper 구조체입니다.
// PrimitiveSequence == (Trait 타입, 요소)
public struct PrimitiveSequence<Trait, Element> {
    let source: Observable<Element>
}

 

 

Single

위에서 엄연히 Traits도 Observable이라고 했죠?

마찬가지로 생성 방식도 비슷합니다.

 

일단 비교를 위해 기존의 Observable을 생성해 보겠습니다.

func observable() -> Observable<Any> {
    return Observable.create { observer in
        observer.onNext("Operation")
        observer.onCompleted()
        observer.onError(SomeError.err)
        
        return Disposables.create()
    }
}

 

Single은 오직 성공 및 실패 이벤트만 다룹니다.

func singleObservable() -> Single<Any> {
    return Single.create { observer in
        observer(.success("Success Operation"))
        observer(.failure(SomeError.err))
        
        return Disposables.create()
    }
}

 

success와 failur의 이벤트는 기존의 Observable의 이벤트에서 이와 같은 역할을 같습니다.

.success() == onNext + onCompleted

.failure() == onError

 

또한 차이점으로 observer.onNext() 이런 접근이 아닌 observer(.success()) 이런식으로 사용되는 이유는

 

SingleObserver 경우

Traits은 Observable 타입을 감싸고 있는 Wrapper 구조체이기 때문에 실행으로 접근하여

이벤트를 Result로 성공, 실패로 접근하기 때문에 이와 같이 사용합니다.

(아닐수도...)

public typealias SingleEvent<Element> = Result<Element, Swift.Error>

 

생성을 해줬으니 구독(subscribe)을 해볼까요?

당연히 이벤트도 onSuccess, onFailure, onDisposed 만을 갖고 사용합니다.

singleObservable().subscribe { event in
    switch event {
    case .success(let data):
        print(data)
    case .failure(let error):
        print(error.localizedDescription)
    }
}.dispose()

 

일반적으로 Single은 성공, 실패로 이벤트를 받을 수 있으므로

API Request에서 주로 사용됩니다.

// 모델
struct ArticleResponse: Codable {
    let articles: [Article]
}

struct Article: Codable {
    let title: String
}

// Single Observable 생성
func getNews() -> Single<[Article]> {
    return Single.create { observer in
        
        let urlString = "https://newsapi.org/v2/everything?q=tesla&sortBy=publishedAt&apiKey=123456789"
        
        AF.request(urlString)
            .validate(statusCode: 200...299)
            .responseDecodable(of: ArticleResponse.self) { response in
                switch response.result {
                case .success(let data):
                    observer(.success(data.articles))
                case .failure(let error):
                    observer(.failure(error))
                }
            }
        return Disposables.create()
    }
}

 

구독(subscribe)

let disposbag = DisposeBag()

// switch문을 통해 이벤트를 호출하거나
getNews()
    .subscribe { event in
        switch event {
        case .success(let data):
            debugPrint(data)
        case .failure(let error):
            print("Failed Request: \(error)")
        }
    }.disposed(by: disposbag)

// 또는 subscribe(onSuccess:, onError:) 직접 처리
getNews()
    .subscribe(
        onSuccess: { data in
            debugPrint(data)
        },
        onFailure: { error in
            print("Failed Request: \(error)")
        })
    .disposed(by: disposbag)

 

 

Completable

성공 여부만 전달해주고 싶을 때 Completable를 사용합니다.

어떠한 element도 방출하지 않고 순수하게 오류의 발생 또는 성공 여부를 전달하는 목적입니다.

func completableObservable() -> Completable {
    return Completable.create { observer in
        observer(.completed)
        observer(.error(SomeError.err))
        return Disposables.create()
    }
}

 

간단한 예제를 살펴봅시다.

 

만약 스위치가 켜져 있을 때 어떠한 동작을 지정해주고 싶은 경우

var switchOn = true

func checkTheSwitch() -> Completable {
    return Completable.create { observer in
        
        // 스위치가 꺼져있으면 오류 발생
        guard switchOn else {
            observer(.error(SomeError.err))
            return Disposables.create()
        }
        observer(.completed)
        
        return Disposables.create()
    }
}

 

completed를 이벤트 통해 동작을 설정해 줄 수  있습니다.

checkTheSwitch().subscribe { event in
    switch event {
    case .completed:
        print("Completed with no error, Turn off switch")
        switchOn = false
        print(switchOn)
    case .error(let error):
        print(error)
    }
}.disposed(by: disposbag)

// Completed with no error, Turn off switch
// false

 

 

Mabye

Mabye는 Single과 Completable의 중간 특성을 갖고 있는 Observable입니다.

 

success, completed, error를 모두 배출도 가능하지만

// 1
func mabyeObservable() -> Maybe<Any> {
    return Maybe.create { observer in
        observer(.success("Mabye Observable"))
        observer(.completed)
        observer(.error(SomeError.err))
        
        return Disposables.create()
    }
}

 이와 같이 필요에 따라 생략도 가능합니다.

// 2
func mabyeObservable() -> Maybe<Any> {
    return Maybe.create { observer in
        observer(.completed)
        
        return Disposables.create()
    }
}

 

mabyeObservable()
    .subscribe { event in
        switch event {
        case.success(let element):
            print("Success: \(element)")
        case .completed:
            print("Complete")
        case .error(let error):
            print(error)
        }
    }.disposed(by: disposbag)

// 1
// Mabye Observable

// 2
// Complete

 

 

정리 및 참고

Traits의 이벤트 정리

 

Single : .success(), .failure()

Completable : .completed, .error()

Mabye : .success()?, .completed?, .error()?

 

 

참고

 

Observable 시퀀스를 as로(Single, Mabye) 변환 시 주의점이 있습니다.

 

만약 Observable을 asSingle로 변환할 때

Observable의 생성 시 onNext 뒤에는 onCompleted가 와줘야 합니다.

 

이유는 Single의 success는 onNext + onCompleted이 합쳐진 것이므로

항상 뒤에 같이 써주며 순서를 신경 써줘야 합니다. (아니면 오류 발생)

// 1. 사용 가능
func observable() -> Observable<Any> {
    return Observable.create { observer in
        observer.onNext("Operation")
        observer.onCompleted()
        
        observer.onError(SomeError.err)

        return Disposables.create()
    }
}
// 2. 오류 발생
func observable() -> Observable<Any> {
    return Observable.create { observer in
        observer.onNext("Operation")
        
        observer.onError(SomeError.err)
        observer.onCompleted()

        return Disposables.create()
    }
}

구독(subscribe)

observable().asSingle()
    .subscribe { event in
        switch event {
        case .success(let element):
            print(element)
        case .failure(let error):
            print(error)
        }
    }.disposed(by: disposbag)

// 1 
// Operation

// 2
// err

 

 

부족한 설명이지만, 조금은 이해 가셨나요?

틀린 내용이 있다면 언제든지 지적해 주시면 감사히 받겠습니다. 🫠
읽어주셔서 감사합니다 😃

 

 

반응형
profile

Danny의 iOS 컨닝페이퍼

@Danny's iOS

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!