[Swift] RxSwift의 일반적인 사용법에 대한 자세한 설명

RxSwift는 ReactiveX API의 Swift 버전입니다. 비동기 및 이벤트 기반 코드를 처리하기 위한 Swift 이벤트 기반 라이브러리입니다.

GitHub: https://github.com/ReactiveX/RxSwift

1. 설치

먼저 RxSwift를 설치해야 합니다. CocoaPods, Carthage 또는 Swift Package Manager를 사용하여 설치할 수 있습니다. 다음은 CocoaPods를 사용하는 예입니다.

Podfile에 다음을 추가합니다.

pod 'RxSwift', '~> 6.0'
pod 'RxCocoa', '~> 6.0'

그런 다음 포드 설치를 실행합니다.
 

2. 기본 방법

먼저, 파일에서 RxSwift를 가져와야 합니다:

import RxSwift

1.Observable 관찰 가능 시퀀스

RxSwift의 핵심 구성요소는 Observable입니다. Observable은 Observer가 캡처하고 응답할 수 있는 이벤트를 내보냅니다.

다음은 Observable을 생성하는 방법의 예입니다.

let myObservable = Observable<String>.create { observer in
    observer.onNext("Hello, RxSwift!")
    observer.onCompleted()
    return Disposables.create()
}

2.관찰자 관찰자

Observer는 Observable에서 발생하는 이벤트를 수신하고 처리하는 엔터티입니다. Observer를 생성하고 Observable을 구독하기 위해 subscribe 메소드를 사용할 수 있습니다:

let myObserver = myObservable.subscribe { event in
    print(event)
}

그러면 "Hello, RxSwift!"가 콘솔에 인쇄됩니다.

3.폐기 와 봉투 버리기

Observable을 구독하면 구독 메소드는 Disposable 객체를 반환합니다. Observable이 더 이상 필요하지 않으면 메모리 누수를 방지하기 위해 Observable을 해제해야 합니다. dispose 메서드를 호출하거나 DisposeBag에 추가하여 해제할 수 있습니다.

let disposeBag = DisposeBag()

myObservable.subscribe { event in
    print(event)
}.disposed(by: disposeBag)

4.주제

주체는 Observable과 Observer 사이의 다리입니다. 둘 다 다른 Observable의 이벤트를 구독하고 이벤트를 생성할 수 있습니다.

RxSwift는 PublishSubject, BehaviorSubject, ReplaySubject 및 Variable을 포함한 여러 유형의 주제를 제공합니다.

let subject = PublishSubject<String>()

subject.onNext("Hello, RxSwift!")

let subscriptionOne = subject
    .subscribe(onNext: { string in
        print(string)
    })
    .disposed(by: disposeBag)

subject.onNext("Hello again, RxSwift!")

subscriptionOne.dispose()

let subscriptionTwo = subject
    .subscribe(onNext: { string in
        print(string)
    })
    .disposed(by: disposeBag)

subject.onNext("Hello again and again, RxSwift!")

그러면 "Hello again, RxSwift!"가 두 번 인쇄되고 "Hello again, RxSwift!"가 한 번 인쇄됩니다.

이는 RxSwift를 사용하는 기본적인 방법일 뿐입니다. RxSwift는 Observable에서 발생하는 이벤트를 처리하고 변환하는 데 사용할 수 있는 map, filter, Reduce, concat 등과 같은 많은 연산자를 제공합니다.
 

3. 운영자

1.지도

Observable이 방출한 각 요소에 변환 함수를 적용하여 변환된 새로운 Observable을 반환합니다.

let numbers = Observable.of(1, 2, 3)

numbers
    .map { $0 * 2 } // 将每个元素乘以 2
    .subscribe(onNext: { value in
        print(value) // 输出:2, 4, 6
    })
    .disposed(by: disposeBag)

이 예에서 'map' 연산자는 각 요소에 2를 곱합니다.

2.플랫맵

Observable이 방출한 각 요소에 변환 함수를 적용하고 새 Observable을 반환한 다음 이러한 Observable을 단일 Observable로 병합합니다.

let numbers = Observable.of(1, 2, 3)

numbers
    .flatMap { value in
        Observable.of(value, value * 2) // 将每个元素转换为两个元素的 Observable
    }
    .subscribe(onNext: { value in
        print(value) // 输出:1, 2, 2, 4, 3, 6
    })
    .disposed(by: disposeBag)

이 예에서 ` flatMap` 연산자는 각 요소를 두 요소로 구성된 Observable로 변환하고 이러한 Observable을 단일 Observable로 병합합니다.

3.컴팩트맵

Observable이 내보낸 각 요소에 변환 함수를 적용하고 변환 결과가 nil인 요소를 필터링한 후 새 Observable을 반환합니다.

let numbers = Observable.of(1, 2, 3, nil, 4, nil, 5)

numbers
    .compactMap { $0 } // 过滤掉为 nil 的元素
    .subscribe(onNext: { value in
        print(value) // 输出:1, 2, 3, 4, 5
    })
    .disposed(by: disposeBag)

이 예에서 `compactMap` 연산자는 nil 요소를 필터링합니다.

4.필터

특정 조건을 충족하지 않는 요소를 필터링하고, 조건을 충족하는 요소만 유지하고, 새로운 Observable을 반환합니다.

let numbers = Observable.of(1, 2, 3, 4, 5)

numbers
    .filter { $0 % 2 == 0 } // 过滤偶数
    .subscribe(onNext: { value in
        print(value) // 输出:2, 4
    })
    .disposed(by: disposeBag)

이 예에서 `filter` 연산자는 짝수 요소만 내보냅니다.

5.스캔

Observable이 내보낸 각 요소에 집계 함수를 적용하여 점진적으로 누적된 결과 시퀀스를 반환합니다.

let numbers = Observable.of(1, 2, 3, 4, 5)

numbers
    .scan(0) { accumulated, value in
        accumulated + value
    }
    .subscribe(onNext: { value in
        print(value) // 输出:1, 3, 6, 10, 15
    })
    .disposed(by: disposeBag)

이 예에서 'scan' 연산자는 각 요소를 누적하고 각 누적 결과를 내보냅니다.

6.감소

Observable이 방출한 각 요소에 집계 함수를 적용하여 최종 집계 결과를 반환합니다.

 let numbers = Observable.of(1, 2, 3, 4, 5)

numbers
    .reduce(0) { accumulated, value in
        accumulated + value
    }
    .subscribe(onNext: { value in
        print(value) // 输出:15
    })
    .disposed(by: disposeBag)

예제에서 `reduce` 연산자는 각 요소에 대해 누적 연산을 수행하고 최종적으로 누적된 결과를 출력합니다.

7.테이크

Observable에서 처음 n개의 요소를 가져와서 새 Observable을 반환합니다.

let numbers = Observable.of(1, 2, 3, 4, 5)

numbers
    .take(3) // 只发出前 3 个元素
    .subscribe(onNext: { value in
        print(value) // 输出:1, 2, 3
    })
    .disposed(by: disposeBag)

이 예에서 'take' 연산자는 처음 3개 요소만 내보냅니다.

8.테이크하는 동안

특정 조건이 더 이상 참이 아닐 때까지 Observable에서 요소를 가져와서 새 Observable을 반환합니다.

let numbers = Observable.of(1, 2, 3, 4, 5)

numbers
    .takeWhile { $0 < 4 } // 只发出小于 4 的元素
    .subscribe(onNext: { value in
        print(value) // 输出:1, 2, 3
    })
    .disposed(by: disposeBag)

이 예에서 'takeWhile' 연산자는 4보다 작은 요소만 내보냅니다.

9.건너뛰기

Observable의 처음 n개 요소를 건너뛰고 새 Observable을 반환합니다.

let numbers = Observable.of(1, 2, 3, 4, 5)

numbers
    .skip(2) // 跳过前 2 个元素
    .subscribe(onNext: { value in
        print(value) // 输出:3, 4, 5
    })
    .disposed(by: disposeBag)

이 예에서 'skip' 연산자는 처음 2개 요소를 건너뛰고 나머지 요소만 내보냅니다.

10. 건너뛰는 동안

조건이 더 이상 유지되지 않을 때까지 Observable의 요소를 건너뛰고 새 Observable을 반환합니다.

let numbers = Observable.of(1, 2, 3, 4, 5)

numbers
    .skipWhile { $0 < 3 } // 跳过小于 3 的元素
    .subscribe(onNext: { value in
        print(value) // 输出:3, 4, 5
    })
    .disposed(by: disposeBag)

예제에서 SkipWhile 연산자는 3개 미만의 요소를 건너뛰고 나머지 요소를 내보냅니다.

11.변경될 때까지 뚜렷한

연속적으로 반복되는 요소를 필터링하고, 반복되지 않는 첫 번째 요소만 유지하고, 새 Observable을 반환합니다.

let numbers = Observable.of(1, 1, 2, 2, 3, 3, 4, 4, 5)

numbers
    .distinctUntilChanged() // 只发出连续不重复的元素
    .subscribe(onNext: { value in
        print(value) // 输出:1, 2, 3, 4, 5
    })
    .disposed(by: disposeBag)

예제에서 independentUntilChanged 연산자는 연속적이고 반복되지 않는 요소만 내보냅니다.

12. 요소 무시

Observable이 방출하는 모든 요소를 ​​무시하고 Observable의 종료 이벤트에만 주의를 기울이십시오.

let numbers = Observable.of(1, 2, 3, 4, 5)

numbers
    .ignoreElements() // 忽略所有元素,只发出 `completed` 或 `error` 事件
    .subscribe(onCompleted: {
        print("Completed")
    })
    .disposed(by: disposeBag)

예제에서ignoreElements 연산자는 모든 요소를 ​​무시하고 완료된 이벤트만 내보냅니다.

13.요소에서

Observable이 내보낸 지정된 인덱스의 요소를 가져오고 새 Observable을 반환합니다.

let numbers = Observable.of(1, 2, 3, 4, 5)

numbers
    .elementAt(2) // 只发出索引为 2 的元素
    .subscribe(onNext: { value in
        print(value) // 输出:3
    })
    .disposed(by: disposeBag)

예제에서 elementAt 연산자는 인덱스 2의 요소만 내보냅니다.

14.toArray

Observable이 방출한 모든 요소를 ​​배열로 수집하고 새로운 Observable을 반환합니다.

let disposeBag = DisposeBag()

Observable.of(1, 2, 3, 4, 5)
    .toArray()
    .subscribe(onNext: { array in
        print(array) // 输出:[1, 2, 3, 4, 5]
    })
    .disposed(by: disposeBag)

위의 예에서 toArray 연산자는 Observable이 내보낸 모든 요소를 ​​배열로 수집하고 이 배열을 단일 이벤트로 다운스트림 구독자에게 내보냅니다. 이는 단일 이벤트 내의 여러 요소를 전체적으로 처리해야 하는 시나리오에 유용합니다.

15.병합

여러 Observable의 요소를 단일 Observable로 결합합니다.

let numbers1 = Observable.of(1, 2, 3)
let numbers2 = Observable.of(4, 5, 6)

Observable.merge(numbers1, numbers2) // 合并两个 Observable
    .subscribe(onNext: { value in
        print(value) // 输出:1, 2, 3, 4, 5, 6
    })
    .disposed(by: disposeBag)

예제에서 병합 연산자는 두 Observable의 요소를 단일 Observable로 병합합니다.

16.zip

여러 Observable의 요소를 일대일로 순서대로 결합합니다.

let numbers = Observable.of(1, 2, 3)
let letters = Observable.of("A", "B", "C")

Observable.zip(numbers, letters) // 按顺序一对一地组合两个 Observable 的元素
    .subscribe(onNext: { number, letter in
        print("\(number)\(letter)") // 输出:1A, 2B, 3C
    })
    .disposed(by: disposeBag)

예제에서 zip 연산자는 두 Observable의 요소를 일대일로 순서대로 결합합니다.

17.combine최신

여러 Observable의 최신 요소를 결합합니다.

let numbers = Observable.of(1, 2, 3)
let letters = Observable.of("A", "B", "C")

Observable.combineLatest(numbers, letters) // 组合两个 Observable 的最新元素
    .subscribe(onNext: { number, letter in
        print("\(number)\(letter)") // 输出:3A, 3B, 3C
    })
    .disposed(by: disposeBag)

예제에서 CombineLatest 연산자는 두 Observable의 최신 요소를 결합합니다.

18.스위치최신

Observable이 방출한 Observable을 단일 Observable로 변환하고 최신 Observable이 방출한 요소만 방출합니다.

let subject = BehaviorSubject(value: Observable.of(1, 2, 3))

subject
    .switchLatest() // 转换为单个 Observable,只发出最新的 Observable 发出的元素
    .subscribe(onNext: { value in
        print(value) // 输出:1, 2, 3
    })
    .disposed(by: disposeBag)

subject.onNext(Observable.of(4, 5, 6)) // 切换到新的 Observable

// 输出:4, 5, 6

이 예에서 switchLatest 연산자는 방출하는 Observable을 단일 Observable로 변환하고 최신 Observable에서 방출된 요소만 방출합니다.

19.와

여러 Observable에서 먼저 요소를 방출하고 다른 Observable을 무시하는 Observable을 선택합니다.

let numbers1 = Observable<Int>.interval(1, scheduler: MainScheduler.instance).take(3)
let numbers2 = Observable<Int>.interval(0.5, scheduler: MainScheduler.instance).take(5)

Observable.amb([numbers1, numbers2]) // 选择首先发出元素的 Observable
    .subscribe(onNext: { value in
        print(value) // 输出:0, 1, 2
    })
    .disposed(by: disposeBag)

예제에서 amb 연산자는 요소를 먼저 방출하는 Observable을 선택합니다.

20.캐치오류

Observable에서 발생하는 오류를 포착하고 새 Observable을 반환하거나 오류 처리 논리를 수행합니다.

enum CustomError: Error {
    case error
}

let observable = Observable<Int>.create { observer in
    observer.onNext(1)
    observer.onNext(2)
    observer.onError(CustomError.error)
    return Disposables.create()
}

observable
    .catchError { error in
        return Observable.just(3) // 捕获错误并返回新的 Observable 发出默认值 3
    }
    .subscribe(onNext: { value in
        print(value) // 输出结果: 1, 2, 3
    })
    .disposed(by: disposeBag)

예제에서 Observable은 1과 2를 방출한 후 오류가 발생하고 catchError 연산자를 사용하여 오류를 포착하고 기본값 3을 방출하는 새로운 Observable을 반환합니다.

21.재시도

오류가 발생했을 때 Observable을 다시 구독하려면 최대 재시도 횟수를 지정할 수 있습니다.

var count = 0

let observable = Observable<Int>.create { observer in
    if count < 3 {
        observer.onError(NSError(domain: "", code: 0, userInfo: nil))
        count += 1
    } else {
        observer.onNext(1)
        observer.onCompleted()
    }
    return Disposables.create()
}

observable
    .retry(2) // 最多重试 2 次
    .subscribe(onNext: { value in
        print(value) // 输出结果: 1
    }, onError: { error in
        print(error) // 不会产生错误
    })
    .disposed(by: disposeBag)

예시에서 Observable은 오류 발생 시 재시도 연산자를 사용하여 Observable을 다시 구독하고 최대 2번까지 재시도하므로 총 3번의 시도가 이루어지며 최종적으로 값 1이 성공적으로 방출됩니다.

22.반복요소

동일한 요소를 반복적으로 방출하는 Observable입니다.

Observable.repeatElement(1)
    .take(3) // 只取前 3 个元素
    .subscribe(onNext: { value in
        print(value) // 输出结果: 1, 1, 1
    })
    .disposed(by: disposeBag)

이 예에서는 반복 요소 연산자를 사용하여 요소 1을 반복적으로 내보내는 Observable을 만든 다음 take 연산자를 사용하여 처음 3개 요소만 가져옵니다.

23. 지연

Observable이 방출하는 요소를 지연시킵니다.

Observable.of(1, 2, 3)
    .delay(.seconds(1), scheduler: MainScheduler.instance) // 延迟 1 秒
    .subscribe(onNext: { value in
        print(value) // 输出结果: 1, 2, 3 (每个元素延迟 1 秒)
    })
    .disposed(by: disposeBag)

이 예에서 Observable이 방출한 요소는 구독자가 수신하기 전에 1초 동안 지연됩니다.

24.스로틀

지정된 시간 간격 동안 Observable의 첫 번째 요소만 방출되고 후속 요소는 무시됩니다.

Observable<Int>.from([1, 2, 3, 4, 5])
    .throttle(.milliseconds(500), scheduler: MainScheduler.instance) // 在 500 毫秒内只发出第一个元素
    .subscribe(onNext: { value in
        print(value) // 输出结果: 1, 3, 5 (忽略了 2 和 4)
    })
    .disposed(by: disposeBag)

이 예에서 스로틀 연산자는 500밀리초 내에 첫 번째 요소만 방출하므로 2와 4는 무시됩니다.

25.디바운스

Observable이 요소를 방출한 후 지정된 시간 간격 내에 새 요소가 없는 경우에만 요소를 방출합니다.

Observable<Int>.from([1, 2, 3, 4, 5])
    .debounce(.milliseconds(500), scheduler: MainScheduler.instance) // 在 500 毫秒内只发出最后一个元素
    .subscribe(onNext: { value in
        print(value) // 输出结果: 5 (忽略了 1、2、3、4)
    })
    .disposed(by: disposeBag)

예제에서 디바운스 연산자는 500밀리초 안에 마지막 요소만 내보내므로 1, 2, 3, 4는 무시됩니다.

26.타임아웃

Observable이 지정된 시간 내에 요소나 완료 이벤트를 발생시키지 않으면 시간 초과 오류가 생성됩니다.

 Observable<Int>.never()
    .timeout(.seconds(2), scheduler: MainScheduler.instance) // 超时时间为 2 秒
    .subscribe(onNext: { value in
        print(value) // 不会输出结果
    }, onError: { error in
        print(error) // 输出结果: RxError.timeout
    })
    .disposed(by: disposeBag)

예제에서는 timeout 연산자를 사용하여 timeout을 2초로 설정했는데, Observable은 무한히 비어 있는 Observable이므로 2초 후에 timeout 오류가 발생합니다.

27.시작

Observable이 방출하는 일련의 요소 앞에 지정된 요소를 삽입합니다.

let numbers = Observable.of(1, 2, 3)

numbers
    .startWith(0) // 在序列前插入元素 0
    .subscribe(onNext: { value in
        print(value) // 输出:0, 1, 2, 3
    })
    .disposed(by: disposeBag)

예제에서 startWith 연산자는 시퀀스 시작 부분에 요소 0을 삽입합니다.

28.끝나다

Observable이 내보낸 일련의 요소에 지정된 요소를 추가합니다.

Observable.of(1, 2, 3)
    .endWith(4) // 在 Observable 完成之前先发出结束元素 4
    .subscribe(onNext: { value in
        print(value) // 输出结果: 1, 2, 3, 4
    })
    .disposed(by: disposeBag)

예제에서 endWith 연산자는 Observable이 완료되기 전에 끝 요소 4를 방출합니다.

29.연결

여러 Observable을 순차적으로 연결하고 이전 Observable이 완료된 후에만 다음 Observable을 구독하세요.

let observable1 = Observable.of(1, 2)
let observable2 = Observable.of(3, 4)

Observable.concat([observable1, observable2])
    .subscribe(onNext: { value in
        print(value) // 输出结果: 1, 2, 3, 4
    })
    .disposed(by: disposeBag)

이 예에서는 두 Observable이 concat 연산자를 사용하여 순차적으로 연결되고, observable1은 1과 2를 방출하고, observable2는 3과 4를 방출합니다.

30.concatMap

Observable이 방출한 각 요소에 변환 함수를 적용하고, 새로운 Observable을 반환하고, 이러한 Observable을 순서대로 연결합니다.

let observable = Observable.of(1, 2, 3)

observable
    .concatMap { value in
        return Observable.of(value * 2, value * 3) // 将每个元素乘以 2 和 3,并按顺序连接输出
    }
    .subscribe(onNext: { value in
        print(value) // 输出结果: 2, 3, 4, 6, 6, 9
    })
    .disposed(by: disposeBag)

예제에서 concatMap 연산자는 각 요소에 2와 3을 곱하고 출력을 순차적으로 연결하는 데 사용됩니다.

31.스위치맵

switchMap 연산자는 소스 Observable의 각 요소를 새로운 Observable로 변환한 다음 이 새로운 Observable을 구독하고 이 새로운 Observable의 요소만 내보냅니다.

let subject = PublishSubject<String>()
let newSubject = PublishSubject<String>()

subject
    .switchMap { _ in newSubject }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

subject.onNext("Hello")
newSubject.onNext("World") // Outputs: World

위의 예에서 switchMap 연산자는 newSubject를 구독하고 해당 요소를 출력합니다.

32.구체화하다

Observable에서 발생하는 요소와 이벤트를 요소 유형이 Event인 Observable로 변환합니다.

let subject = PublishSubject<String>()

subject
    .materialize()
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

subject.onNext("Hello") // Outputs: next(Hello)
subject.onCompleted() // Outputs: completed

위의 예에서 구체화 연산자는 소스 Observable의 방출 및 알림을 요소로 변환합니다.

33.비물질화

Observable이 방출한 Event 요소를 Observable의 원래 요소 및 이벤트 유형으로 다시 변환합니다.

let subject = PublishSubject<Event<String>>()

subject
    .dematerialize()
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

subject.onNext(Event.next("Hello")) // Outputs: Hello
subject.onNext(Event.completed) // Nothing is printed

위의 예에서, dematerialize 연산자는 구체화된 요소를 Observable 소스의 방출로 복원합니다.

34.공유

공유 연산자는 소스 Observable을 공유 가능한 Observable로 변환합니다.

let source = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance).share()

source
    .subscribe(onNext: { print("Subscriber 1: \($0)") })
    .disposed(by: disposeBag)

DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    source
        .subscribe(onNext: { print("Subscriber 2: \($0)") })
        .disposed(by: disposeBag)
}

// Outputs:
// Subscriber 1: 0
// Subscriber 1: 1
// Subscriber 1: 2
// Subscriber 2: 2
// Subscriber 1: 3
// Subscriber 2: 3

위의 예에서 공유 연산자는 두 구독자가 동일한 Observable을 공유하도록 합니다.

35.공유리플레이

shareReplay 연산자를 사용하면 새 구독자가 마지막 n개 요소를 수신할 수 있습니다.

let source = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance).shareReplay(1)

source
    .subscribe(onNext: { print("Subscriber 1: \($0)") })
    .disposed(by: disposeBag)

DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    source
        .subscribe(onNext: { print("Subscriber 2: \($0)") })
        .disposed(by: disposeBag)
}

// Outputs:
// Subscriber 1: 0
// Subscriber 1: 1
// Subscriber 1: 2
// Subscriber 2: 1
// Subscriber 1: 3
// Subscriber 2: 3

위의 예에서 shareReplay 연산자를 사용하면 새 구독자가 가장 최근 요소를 받을 수 있습니다.

36. 출판

게시 연산자는 소스 Observable을 ConnectableObservable로 변환합니다. 연결 연산자가 호출될 때만 요소 방출을 시작합니다.

let source = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance).publish()

source
    .subscribe(onNext: { print("Subscriber 1: \($0)") })
    .disposed(by: disposeBag)

DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    source
        .subscribe(onNext: { print("Subscriber 2: \($0)") })
        .disposed(by: disposeBag)
}

source.connect()

// Outputs:
// Subscriber 1: 0
// Subscriber 2: 0
// Subscriber 1: 1
// Subscriber 2: 1

위의 예에서 게시 연산자는 소스 Observable을 ConnectableObservable로 바꾸고 connect가 호출된 후 요소를 방출하기 시작합니다.

37.멀티캐스트

멀티캐스트 연산자는 소스 Observable을 ConnectableObservable로 변환하고 주제를 중재자로 지정할 수 있도록 합니다. 연결 연산자가 호출될 때만 요소 방출을 시작합니다.

let subject = PublishSubject<Int>()
let source = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance).multicast(subject)

source
    .subscribe(onNext: { print("Subscriber 1: \($0)") })
    .disposed(by: disposeBag)

DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    source
        .subscribe(onNext: { print("Subscriber 2: \($0)") })
        .disposed(by: disposeBag)
}

source.connect()

// Outputs:
// Subscriber 1: 0
// Subscriber 2: 0
// Subscriber 1: 1
// Subscriber 2: 1

위의 예에서 멀티캐스트 연산자는 소스 Observable을 ConnectableObservable로 바꾸고 connect가 호출된 후 요소를 방출하기 시작합니다.

38.refCount

refCount 연산자는 ConnectableObservable을 일반 Observable로 변환합니다. 구독자 수가 0에서 1로 증가하면 요소를 방출하기 시작합니다. 구독자 수가 1에서 0으로 변경되면 요소 방출이 중지됩니다.

let source = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance).publish().refCount()

source
    .subscribe(onNext: { print("Subscriber 1: \($0)") })
    .disposed(by: disposeBag)

DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    source
        .subscribe(onNext: { print("Subscriber 2: \($0)") })
        .disposed(by: disposeBag)
}

// Outputs:
// Subscriber 1: 0
// Subscriber 1: 1
// Subscriber 1: 2
// Subscriber 2: 2
// Subscriber 1: 3
// Subscriber 2: 3

위의 예에서 refCount 연산자는 구독자 수가 0에서 1로 증가할 때 소스 Observable이 요소를 방출하기 시작하도록 합니다.

39.재생

재생 연산자는 소스 Observable을 ConnectableObservable로 변환하고 새 구독자가 이를 구독할 때 마지막 n 요소를 내보냅니다.

let source = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance).replay(3)

source
    .subscribe(onNext: { print("Subscriber 1: \($0)") })
    .disposed(by: disposeBag)

DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    source
        .subscribe(onNext: { print("Subscriber 2: \($0)") })
        .disposed(by: disposeBag)
}

source.connect()

// Outputs:
// Subscriber 1: 0
// Subscriber 1: 1
// Subscriber 2: 0
// Subscriber 2: 1
// Subscriber 1: 2
// Subscriber 2: 2

위의 예에서 재생 연산자는 새 구독자가 구독할 때 Observable 소스가 마지막 n 요소를 방출하도록 합니다.

40.샘플

Observable에서 주기적으로 샘플링하고 최신 요소를 내보냅니다.

let source = PublishSubject<String>()
let notifier = PublishSubject<Void>()

source
    .sample(notifier)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

source.onNext("Hello")
notifier.onNext(()) // Nothing is printed
source.onNext("World")
notifier.onNext(()) // Outputs: World

이 예제에서는 notifier.onNext(())가 처음에 호출되지 않으므로 source.onNext("Hello")가 실행된 후 notifier는 어떤 요소도 내보내지 않습니다. 따라서 샘플링된 신호 없이 샘플 연산자를 실행하면 요소가 방출되지 않으므로 콘솔에 아무 것도 출력되지 않습니다. 즉, 출력은 "아무것도 인쇄되지 않습니다"입니다.

notifier.onNext(())가 호출되고 알림자가 샘플링 신호를 방출한 후에만 샘플 운영자는 방출을 위해 소스 Observable 소스에서 최신 요소를 선택합니다. 따라서 notifier.onNext(())에 대한 두 번째 호출 후 샘플 연산자는 최신 요소 "World"를 선택하여 내보내고 콘솔은 "World"를 출력합니다.

41. 걸릴 때까지

다른 Observable이 요소를 방출하거나 완료할 때 원래 Observable의 요소 방출을 중지합니다.

takeUntil 연산자는 두 번째 Observable이 요소를 방출할 때까지 소스 Observable의 요소를 구독하고 방출합니다.

let source = PublishSubject<String>()
let stopper = PublishSubject<String>()

source
    .takeUntil(stopper)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

source.onNext("Hello") // Outputs: Hello
source.onNext("World") // Outputs: World
stopper.onNext("Enough")
source.onNext("!!!") // Nothing is printed

위의 예에서 takeUntil 연산자는 스토퍼가 요소를 내보내면 소스 요소의 출력을 중지합니다.

42. 배송까지

SkipUntil 연산자는 takeUntil 연산자와 반대입니다. 두 번째 Observable이 요소를 방출할 때까지 소스 Observable의 요소를 무시합니다.

let source = PublishSubject<String>()
let notifier = PublishSubject<String>()

source
    .skipUntil(notifier)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

source.onNext("Hello") // Nothing is printed
notifier.onNext("OK")
source.onNext("World") // Outputs: World

위의 예에서 SkipUntil 연산자는 알림이 내보낸 요소 앞에 오는 소스의 모든 요소를 ​​무시합니다.

43.마지막으로

takeLast 연산자는 Observable 소스의 마지막 n개 요소만 내보냅니다.

let source = PublishSubject<String>()

source
    .takeLast(2)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

source.onNext("1")
source.onNext("2")
source.onNext("3")
source.onCompleted() // Outputs: 2, 3

위의 예에서 takeLast 연산자는 마지막 두 요소만 출력합니다.

44.마지막 건너뛰기

SkipLast 연산자는 소스 Observable의 마지막 n 요소를 건너뜁니다.

let source = PublishSubject<String>()

source
    .skipLast(2)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

source.onNext("1") // Outputs: 1
source.onNext("2")
source.onNext("3")
source.onCompleted()

위의 예에서 SkipLast 연산자는 마지막 두 요소를 건너뛰고 첫 번째 요소만 출력합니다.

45.버퍼

버퍼 연산자는 주기적으로 Observable 소스에서 요소를 수집하고 이러한 요소를 배열로 내보냅니다.

let source = PublishSubject<String>()

source
    .buffer(timeSpan: .seconds(1), count: 2, scheduler: MainScheduler.instance)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

source.onNext("1")
source.onNext("2") // Outputs: ["1", "2"]
source.onNext("3")

46.창

`window` 연산자는 `buffer`와 유사하지만 배열 대신 Observable의 요소를 수집합니다.

let source = PublishSubject<String>()

source
    .window(timeSpan: .seconds(1), count: 2, scheduler: MainScheduler.instance)
    .flatMap { $0.toArray() }
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

source.onNext("1")
source.onNext("2") // Outputs: ["1", "2"]
source.onNext("3")
source.onNext("4") // Outputs: ["3", "4"]

위의 예에서 창 연산자는 두 요소를 모두 수집하여 배열로 내보냅니다.

47.반복

Observable의 요소를 반복적으로 구독하고 내보내면 반복 횟수를 지정할 수 있습니다.

Observable.of("Hello")
    .repeatElement(3)
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag) // Outputs: Hello, Hello, Hello

위의 예에서는 "Hello"가 세 번 반복됩니다.

48. 와

여러 Observable에서 먼저 요소를 방출하고 다른 Observable을 무시하는 Observable을 선택합니다.

let subject1 = PublishSubject<String>()
let subject2 = PublishSubject<String>()

Observable.amb([subject1, subject2])
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)

subject2.onNext("Hello from subject 2") // Outputs: Hello from subject 2
subject1.onNext("Hello from subject 1") // Nothing is printed

위의 예에서 amb 연산자는 요소를 먼저 방출하는 Observable을 선택합니다.

49.타임아웃

지정된 시간 간격이 경과한 후 소스 Observable이 어떤 요소도 방출하지 않으면 시간 제한 연산자는 오류를 방출합니다.

let subject = PublishSubject<String>()

subject
    .timeout(.seconds(2), scheduler: MainScheduler.instance)
    .subscribe(onNext: { print($0) }, onError: { print($0) })
    .disposed(by: disposeBag)

DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
    subject.onNext("Hello") // Outputs: The operation couldn’t be completed. (RxSwift.RxError error 5.)
}

위의 예에서 소스 Observable이 2초 동안 어떤 요소도 방출하지 않았기 때문에 시간 초과 연산자는 오류를 방출합니다.

50.디버그

디버그 연산자는 모든 소스 Observable 구독, 이벤트 및 상태를 콘솔에 인쇄합니다.

let subject = PublishSubject<String>()

subject
    .debug("Observable")
    .subscribe()
    .disposed(by: disposeBag)

subject.onNext("Hello") // Outputs: Observable -> subscribed, Observable -> Event next(Hello)
subject.onCompleted() // Outputs: Observable -> Event completed, Observable -> isDisposed

위의 예에서 디버그 연산자는 모든 소스 Observable의 구독, 이벤트 및 상태를 인쇄합니다.
 

4. 실제 적용

1. 양식 유효성 검사

전통적인 방법 :

등록 양식에서는 모든 필드(예: 사용자 이름, 비밀번호, 비밀번호 확인, 이메일 등)가 유효한 경우에만 사용자가 제출 버튼을 클릭할 수 있도록 하고 싶습니다. 기존 접근 방식에서는 필드가 변경될 때 호출되는 각 필드에 대한 함수를 작성해야 할 수도 있습니다. 그런 다음 각 기능에서 모든 필드를 다시 확인하고 제출 버튼을 활성화할지 여부를 결정합니다.

class SignUpViewController: UIViewController {
    @IBOutlet weak var usernameField: UITextField!
    @IBOutlet weak var passwordField: UITextField!
    @IBOutlet weak var confirmPasswordField: UITextField!
    @IBOutlet weak var signUpButton: UIButton!

    // 当文本字段中的文本更改时,调用此函数
    @IBAction func textChanged(_ sender: UITextField) {
        let isValid = validateForm()
        signUpButton.isEnabled = isValid // 根据表单是否有效来启用或禁用注册按钮
    }

    // 验证表单
    // 如果用户名、密码和确认密码都不为空,并且密码与确认密码匹配,则返回 true,否则返回 false
    func validateForm() -> Bool {
        guard let username = usernameField.text, !username.isEmpty,
              let password = passwordField.text, !password.isEmpty,
              let confirmPassword = confirmPasswordField.text, !confirmPassword.isEmpty,
              password == confirmPassword else {
            return false
        }
        return true
    }
}

RxSwift

RxSwift를 사용하여 이러한 필드를 Observable로 처리하고 CombineLatest 메소드를 사용하여 양식의 유효성을 나타내는 새로운 Observable을 생성할 수 있습니다. 그런 다음 이 Observable을 구독할 수 있으며 새 값이 방출되면 제출 버튼을 활성화하거나 비활성화할 수 있습니다. 이렇게 하면 코드가 더 깨끗해지고 유지 관리가 더 쉬워집니다.

import RxSwift
import RxCocoa

class RxSignUpViewController: UIViewController {
    @IBOutlet weak var usernameField: UITextField!
    @IBOutlet weak var passwordField: UITextField!
    @IBOutlet weak var confirmPasswordField: UITextField!
    @IBOutlet weak var signUpButton: UIButton!
    
    let disposeBag = DisposeBag() // 用于存储订阅
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 将用户名、密码和确认密码的文本组合成一个 Observable
        // 然后使用 map 操作符将这些文本转换为一个布尔值,表示表单是否有效
        // 最后,将这个布尔值绑定到注册按钮的 isEnabled 属性,这样当表单是否有效改变时,按钮的启用状态会自动更新
        Observable.combineLatest(
            usernameField.rx.text.orEmpty,
            passwordField.rx.text.orEmpty,
            confirmPasswordField.rx.text.orEmpty
        )
        .map { username, password, confirmPassword in
            return !username.isEmpty && !password.isEmpty && !confirmPassword.isEmpty && password == confirmPassword
        }
        .bind(to: signUpButton.rx.isEnabled)
        .disposed(by: disposeBag)
    }
}

2. 네트워크 요청

전통적인 방법 :

기존 네트워크 요청에서는 콜백이나 Promise/Future를 사용할 수 있습니다. 이렇게 하면 코드가 비동기식으로 유지되지만 여러 개의 연속적인 네트워크 요청을 수행해야 하거나 네트워크 요청을 취소해야 하는 경우 코드가 복잡해지고 유지 관리가 어려워질 수 있습니다.

class NetworkRequestViewController: UIViewController {
    func fetchData() {
        // 使用 URLSession 发起网络请求
        // 当请求完成时,我们会得到 data、response 和 error,并打印它们
        URLSession.shared.dataTask(with: URL(string: "https://example.com")!) { data, response, error in
            if let error = error {
                print("Error: \(error)")
            } else if let data = data {
                print("Data: \(data)")
            }
        }.resume()
    }
}

RxSwift

RxSwift를 사용하면 네트워크 요청을 Observable로 처리할 수 있으므로 RxSwift에서 제공하는 다양한 연산자(예: map, filter, concat 등)를 사용하여 네트워크 요청을 처리할 수 있습니다. 예를 들어 flatMap 연산자를 사용하여 연속적인 네트워크 요청을 할 수 있고, takeUntil 연산자를 사용하여 네트워크 요청을 취소할 수 있습니다.

import RxSwift
import RxCocoa

class RxNetworkRequestViewController: UIViewController {
    let disposeBag = DisposeBag() // 用于存储订阅
    
    func fetchData() {
        let url = URL(string: "https://example.com")!
        // 使用 URLSession 的 rx 扩展发起网络请求
        // 当请求完成时,我们会得到 data 或 error,并打印它们
        URLSession.shared.rx.data(request: URLRequest(url: url))
            .subscribe(onNext: { data in
                print("Data: \(data)")
            }, onError: { error in
                print("Error: \(error)")
            })
            .disposed(by: disposeBag)
    }
}

3. UI 업데이트

전통적인 방법 :

기존 접근 방식에서는 여러 위치에서 UI를 업데이트해야 할 수도 있습니다. 예를 들어 데이터가 변경되면 setter 메서드에서 UI를 업데이트해야 하거나 네트워크 요청의 콜백에서 UI를 업데이트해야 할 수 있습니다.

class UpdateUIViewController: UIViewController {
    @IBOutlet weak var label: UILabel!
    
    // 当数据更新时,我们会更新 label 的文本
    var data: String? {
        didSet {
            label.text = data
        }
    }
}

RxSwift

RxSwift를 사용하면 데이터를 Observable로 볼 수 있고, Observable을 구독할 수 있으며, 새 값을 내보낼 때 UI를 업데이트할 수 있습니다. 이러한 방식으로 UI 업데이트 로직이 더욱 중앙 집중화되고 유지 관리가 더 쉬워집니다.

import RxSwift
import RxCocoa

class RxUpdateUIViewController: UIViewController {
    @IBOutlet weak var label: UILabel!
    
    let disposeBag = DisposeBag() // 用于存储订阅
    let data = PublishSubject<String>() // 数据源
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 将数据绑定到 label 的 text 属性,这样当数据有新的值时,label 的文本会自动更新
        data
            .bind(to: label.rx.text)
            .disposed(by: disposeBag)
    }
}

4.검색

전통적인 방법 :

검색 상자를 구현하려는 경우 사용자가 텍스트를 입력하면 검색 결과를 가져오고 UI를 업데이트하도록 API를 요청합니다. RxSwift를 사용하지 않고 다음과 같이 할 수 있습니다:

class SearchViewController: UIViewController {
    @IBOutlet weak var searchBar: UISearchBar!
    @IBOutlet weak var tableView: UITableView!
    
    var searchResults: [String] = [] {
        didSet {
            tableView.reloadData()
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        searchBar.delegate = self
    }
}

extension SearchViewController: UISearchBarDelegate {
    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        // 这里我们模拟网络请求,实际使用中会调用实际的 API
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
            self?.searchResults = ["Result 1", "Result 2", "Result 3"]
        }
    }
}

이 예에서는 검색 상자의 프록시 메서드 searchBar(_:textDidChange:)가 호출될 때 네트워크 요청을 시뮬레이션합니다. 네트워크 요청이 완료되면 searchResults를 업데이트하여 tableView를 다시 로드합니다.

RxSwift

RxSwift를 사용하면 검색 상자의 텍스트 변경 사항을 Observable로 처리한 다음 이 Observable을 구독할 수 있고, 새 값을 내보낼 때 API를 요청하고 UI를 업데이트할 수 있습니다. 이것은 RxSwift를 사용하는 버전입니다:

import RxSwift
import RxCocoa

class RxSearchViewController: UIViewController {
    @IBOutlet weak var searchBar: UISearchBar!
    @IBOutlet weak var tableView: UITableView!
    
    let disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        searchBar.rx.text.orEmpty
            .debounce(.milliseconds(300), scheduler: MainScheduler.instance) // 防抖动
            .distinctUntilChanged() // 仅当新的值和前一个值不相同时才发出
            .flatMapLatest { query -> Observable<[String]> in
                return self.fetchSearchResults(query) // 获取搜索结果
            }
            .bind(to: tableView.rx.items(cellIdentifier: "Cell")) { index, model, cell in
                cell.textLabel?.text = model
            }
            .disposed(by: disposeBag)
    }
    
    func fetchSearchResults(_ query: String) -> Observable<[String]> {
        // 模拟网络请求
        return Observable.just(["Result 1", "Result 2", "Result 3"])
    }
}

이 예에서는 여러 RxSwift 연산자를 사용합니다. 디바운스 연산자는 사용자가 계속 입력하는 동안 네트워크 요청을 하는 것을 방지합니다. UniqueUntilChanged 연산자는 검색 상자의 텍스트가 실제로 변경된 경우에만 네트워크 요청을 보내도록 보장합니다. flatMapLatest 연산자를 사용하면 항상 최신 검색창 텍스트가 포함된 검색결과를 얻을 수 있습니다. 마지막으로 검색 결과가 변경되면 테이블 뷰가 자동으로 업데이트되도록 바인딩(to:) 메서드를 사용하여 검색 결과를 테이블 뷰에 바인딩합니다.
 

바인딩(to:) 메소드는 Observable 값을 위 예제의 UITableView와 같은 특정 객체에 바인딩합니다. 이를 통해 데이터 소스(Observable)가 새 요소를 내보낼 때 UITableView가 자동으로 업데이트되어 새 데이터를 표시할 수 있습니다.

이 예에서는 tableView.rx.items(cellIdentifier: "Cell")을 바인딩 대상으로 사용했습니다. 이는 RxSwift에서 제공하는 메서드로, 새 요소를 받을 때마다 UITableView를 업데이트하는 Observer를 반환합니다.

검색 결과를 tableView.rx.items(cellIdentifier: "Cell")에 바인딩할 때 각각의 새로운 검색 결과가 도착할 때 호출되는 클로저를 제공합니다.

tableView.rx.items(cellIdentifier: "Cell") { index, model, cell in
    cell.textLabel?.text = model
}

이 클로저 내부에는 세 가지 매개변수가 있습니다:

  • index: 현재 줄 번호인 현재 요소의 인덱스입니다.

  • 모델: 검색 결과의 값인 현재 요소의 값입니다.

  • cell: 현재 UITableViewCell, 이 셀에 데이터를 표시해야 합니다.

따라서 이 클로저의 기능은 UITableViewCell(셀)에 검색 결과(모델)를 표시하는 것입니다. 이러한 방식으로 검색 결과에 새 값이 있을 때마다 UITableView는 자동으로 업데이트되어 새 검색 결과를 표시합니다.

5. 사용자 입력 처리

이 예에서는 텍스트 필드에서 사용자 입력을 처리합니다. RxSwift를 사용하면 텍스트 필드의 text 속성을 구독하고 사용자가 새 텍스트를 입력할 때 자동으로 인쇄할 수 있습니다.

전통적인 방법 :

class UserInputViewController: UIViewController {
    @IBOutlet weak var textField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        textField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
    }

    @objc func textFieldDidChange(_ textField: UITextField) {
        if let text = textField.text {
            print("User input: \(text)")
        }
    }
}

RxSwift

import RxSwift
import RxCocoa

class RxUserInputViewController: UIViewController {
    @IBOutlet weak var textField: UITextField!
    
    let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()
        textField.rx.text.orEmpty
            .subscribe(onNext: { text in
                print("User input: \(text)")
            })
            .disposed(by: disposeBag)
    }
}

6. 타이밍 동작

이 예에서는 매초 "Timer Fire!"를 인쇄하는 타이머를 만듭니다. RxSwift에서는 간격 연산자를 사용하여 타이머를 생성할 수 있습니다.

전통적인 방법 :

class TimerViewController: UIViewController {
    var timer: Timer?

    override func viewDidLoad() {
        super.viewDidLoad()
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
            print("Timer fired!")
        }
    }

    deinit {
        timer?.invalidate()
    }
}

RxSwift

import RxSwift
import RxCocoa

class RxTimerViewController: UIViewController {
    let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()
        Observable<Int>.interval(RxTimeInterval.seconds(1), scheduler: MainScheduler.instance)
            .subscribe(onNext: { _ in
                print("Timer fired!")
            })
            .disposed(by: disposeBag)
    }
}

7. 오류 처리

이 예에서는 네트워크 요청 오류를 처리합니다. RxSwift에서 오류는 구독 시 처리할 수 있는 시퀀스 종료 이벤트로 처리됩니다.

전통적인 방법 :

class ErrorHandlingViewController: UIViewController {
    func fetchData(completion: @escaping (Data?, Error?) -> Void) {
        URLSession.shared.dataTask(with: URL(string: "https://example.com")!) { data, _, error in
            completion(data, error)
        }.resume()
    }

    func doSomething() {
        fetchData { data, error in
            if let error = error {
                print("Error: \(error)")
            } else if let data = data {
                print("Data: \(data)")
            }
        }
    }
}

RxSwift

import RxSwift
import RxCocoa

class RxErrorHandlingViewController: UIViewController {
    let disposeBag = DisposeBag()

    func fetchData() -> Observable<Data> {
        return URLSession.shared.rx.data(request: URLRequest(url: URL(string: "https://example.com")!))
    }

    func doSomething() {
        fetchData()
            .subscribe(onNext: { data in
                print("Data: \(data)")
            }, onError: { error in
                print("Error: \(error)")
            })
            .disposed(by: disposeBag)
    }
}

8. 수집업무

이 예에서는 컬렉션을 필터링하고 있습니다. RxSwift를 사용하면 배열을 Observable로 변환하고 맵 연산자를 사용하여 필터링할 수 있습니다.

전통적인 방법 :

class CollectionViewController: UIViewController {
    var items: [Int] = []

    func updateItems() {
        items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        print("Updated items: \(items)")
    }
    
    func filterItems() {
        items = items.filter { $0 % 2 == 0 }
        print("Filtered items: \(items)")
    }
}

RxSwift

import RxSwift

class RxCollectionViewController: UIViewController {
    let disposeBag = DisposeBag()
    let items = BehaviorSubject(value: [Int]())

    func updateItems() {
        items.onNext([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
    }

    func filterItems() {
        items.asObservable()
            .map { $0.filter { $0 % 2 == 0 } }
            .subscribe(onNext: { items in
                print("Filtered items: \(items)")
            })
            .disposed(by: disposeBag)
    }
}

9. 병렬로 여러 요청

이 예에서는 여러 네트워크 요청을 병렬로 완료합니다. RxSwift를 사용하면 URL 배열을 Observable로 변환하고 flatMap 및 toArray 연산자를 사용하여 병렬 요청을 할 수 있습니다.

전통적인 방법 :

class ParallelRequestsViewController: UIViewController {
    func fetchData(from urls: [URL], completion: @escaping ([Data]) -> Void) {
        let group = DispatchGroup()
        var results: [Data] = []

        for url in urls {
            group.enter()

            URLSession.shared.dataTask(with: url) { data, _, _ in
                if let data = data {
                    results.append(data)
                }
                group.leave()
            }.resume()
        }

        group.notify(queue: .main) {
            completion(results)
        }
    }
}

RxSwift

import RxSwift
import RxCocoa

class RxParallelRequestsViewController: UIViewController {
    let disposeBag = DisposeBag()

    func fetchData(from urls: [URL]) -> Observable<[Data]> {
        return Observable.from(urls)
            .flatMap { url in
                return URLSession.shared.rx.data(request: URLRequest(url: url))
            }
            .toArray()
    }
}

10.교환응답

이 예에서는 네트워크 요청의 응답을 받아 필요한 형식으로 변환합니다. RxSwift를 사용하면 변환을 위해 지도 연산자를 사용할 수 있습니다.

전통적인 방법 :

class TransformResponseViewController: UIViewController {
    func fetchData(completion: @escaping (String?) -> Void) {
        URLSession.shared.dataTask(with: URL(string: "https://jsonplaceholder.typicode.com/posts/1")!) { data, _, _ in
            if let data = data {
                let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
                completion(json?["title"] as? String)
            }
        }.resume()
    }
}

RxSwift

import RxSwift
import RxCocoa

class RxTransformResponseViewController: UIViewController {
    let disposeBag = DisposeBag()

    func fetchData() -> Observable<String> {
        let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1")!
        return URLSession.shared.rx.json(request: URLRequest(url: url))
            .map { json in
                let dictionary = json as? [String: Any]
                return dictionary?["title"] as? String ?? ""
            }
    }
}

11.이벤트 큐잉

이 예에서는 이벤트 큐에 이벤트를 추가하고 2초 후에 이벤트를 처리합니다. RxSwift에서는 이 기능을 달성하기 위해 버퍼 연산자를 사용할 수 있습니다.

전통적인 방법 :

class EventQueueViewController: UIViewController {
    var events: [String] = []
    var timer: Timer?
  
    func enqueue(event: String) {
        events.append(event)
        if timer == nil {
            timer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { _ in
                if !self.events.isEmpty {
                    print("Processing event: \(self.events.removeFirst())")
                } else {
                    self.timer?.invalidate()
                    self.timer = nil
                }
            }
        }
    }
}

RxSwift

import RxSwift
import RxCocoa

class RxEventQueueViewController: UIViewController {
    let disposeBag = DisposeBag()
    let eventSubject = PublishSubject<String>()
  
    override func viewDidLoad() {
        super.viewDidLoad()
        
        eventSubject
            .buffer(timeSpan: .seconds(2), count: 1, scheduler: MainScheduler.instance)
            .subscribe(onNext: { events in
                if let event = events.first {
                    print("Processing event: \(event)")
                }
            })
            .disposed(by: disposeBag)
    }
  
    func enqueue(event: String) {
        eventSubject.onNext(event)
    }
}

12. 여러 작업 결합

이 예에서는 여러 작업을 결합해야 합니다. 먼저 사용자를 가져온 다음 해당 사용자의 게시물을 가져옵니다. RxSwift에서는 flatMap 및 map 연산자를 사용하여 이러한 작업을 결합할 수 있습니다.

전통적인 방법 :

class CombineOperationsViewController: UIViewController {
    var user: User?
    var posts: [Post]?
    
    func fetchUser(completion: @escaping (User?) -> Void) {
        // Fetch the user...
    }
    
    func fetchPosts(for user: User, completion: @escaping ([Post]?) -> Void) {
        // Fetch the posts for the user...
    }
    
    func updateUserAndPosts() {
        fetchUser { user in
            self.user = user
            if let user = user {
                self.fetchPosts(for: user) { posts in
                    self.posts = posts
                }
            }
        }
    }
}

RxSwift

import RxSwift
import RxCocoa

class RxCombineOperationsViewController: UIViewController {
    let disposeBag = DisposeBag()
    
    func fetchUser() -> Observable<User> {
        // Fetch the user...
        return Observable.just(User())
    }
    
    func fetchPosts(for user: User) -> Observable<[Post]> {
        // Fetch the posts for the user...
        return Observable.just([Post]())
    }
    
    func updateUserAndPosts() {
        fetchUser()
            .flatMap { user in
                self.fetchPosts(for: user)
                    .map { posts in (user, posts) }
            }
            .subscribe(onNext: { user, posts in
                print("User: \(user), Posts: \(posts)")
            })
            .disposed(by: disposeBag)
    }
}

13.알림

기존 알림 센터를 사용하든 RxSwift용 알림 센터 확장을 사용하든 알림을 보내는 방법은 동일합니다.

전통적인 방법 :

class MyViewController: UIViewController {
    let notificationName = Notification.Name("MyNotification")

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(self.handleNotification(_:)), name: notificationName, object: nil)
    }

    @objc func handleNotification(_ notification: Notification) {
        // Handle the notification
        print("Notification received!")
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

RxSwift

class MyViewController: UIViewController {
    let disposeBag = DisposeBag()
    let notificationName = Notification.Name("MyNotification")

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.rx.notification(notificationName)
            .subscribe(onNext: { [weak self] notification in
                // Handle the notification
                print("Notification received!")
            })
            .disposed(by: disposeBag)
    }
}

추천

출처blog.csdn.net/u012881779/article/details/134745279