Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- uitableview
- tableView
- Clean Code
- swiftUI
- 클린 코드
- swift documentation
- rxswift
- collectionview
- 리펙토링
- 리팩토링
- 애니메이션
- Protocol
- SWIFT
- ios
- Refactoring
- Xcode
- 스위프트
- clean architecture
- Human interface guide
- combine
- UITextView
- MVVM
- Observable
- ribs
- 리펙터링
- UICollectionView
- map
- uiscrollview
- RxCocoa
- HIG
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] RxSwift의 share(), share(replay:scope:) 연산자, 여러곳에서 구독 시 한번만 수행되게끔 하는 방법 본문
RxSwift/RxSwift 응용
[iOS - swift] RxSwift의 share(), share(replay:scope:) 연산자, 여러곳에서 구독 시 한번만 수행되게끔 하는 방법
jake-kim 2022. 2. 5. 01:29subscribe or bind 시 그만큼 해당 Observable이 create되는 것을 주의
- 구독만 해도 해당 Observable의 `create`가 호출되면서 스트림이 생긴다는 것을 명심할 것
- 테스트에 사용할 예시 API 정의
enum API { private static var count = 0 static func requestSomeAPI() -> Observable<String> { Single<String>.create { single in let params: [String: Any] = [ "lon": 113, "lat": 23.1, "ac": 0, "unit": "metric", "output": "json", "tzshift": 0 ] AF .request( "https://www.7timer.info/bin/astro.php", method: .get, parameters: params ) .responseString { response in Self.count += 1 print("request 카운트 \(Self.count)") switch response.result { case let .success(string): single(.success(string)) case let .failure(error): single(.failure(error)) } } return Disposables.create() } .asObservable() } } // https://www.7timer.info/bin/astro.php?lon=113.2&lat=23.1&ac=0&unit=metric&output=json&tzshift=0
- 해당 Observable을 얻어서 이 하나의 Observable을 구독하는 곳이 여러곳이라면 여러곳에서 호출되는것을 주의
let apiObservable = API.requestSomeAPI() apiObservable .bind { _ in print("구독1!") } .disposed(by: self.disposeBag) apiObservable .bind { _ in print("구독2!") } .disposed(by: self.disposeBag) apiObservable .bind { _ in print("구독3!") } .disposed(by: self.disposeBag) /* request 카운트 1 구독1! request 카운트 2 구독2! request 카운트 3 구독3! */
구독 시 중복 호출 방지 - 별도의 PublishSubject를 두는 방벙
- PublishSubject 개념은 이전 포스팅 글 참고
- publishSubject를 선언해 놓고, API로 나온 Observable을 구독한 다음 값을 publishSubeject에 전달하는 방법
class ViewController: UIViewController { private let disposeBag = DisposeBag() private let myPublichSubject = PublishSubject<String>() // <- override func viewDidLoad() { super.viewDidLoad() API.requestSomeAPI() .bind { [weak self] in self?.myPublichSubject.onNext($0) } .disposed(by: self.disposeBag) ...
- api request는 단 한번만 수행
API.requestSomeAPI() .bind { [weak self] in self?.myPublichSubject.onNext($0) } .disposed(by: self.disposeBag) myPublichSubject .bind { _ in print("구독1!") } .disposed(by: self.disposeBag) myPublichSubject .bind { _ in print("구독2!") } .disposed(by: self.disposeBag) myPublichSubject .bind { _ in print("구독3!") } .disposed(by: self.disposeBag) /* request 카운트 1 구독1! 구독2! 구독3! */
구독 시 중복 호출 방지 - share() 연산자 사용 방법
- share() 연산자 사용 방법
- Observable에 share()연산자를 사용하여 구독이 여러번 되어도 create가 호출되지 않는 연산자
- 보통 Observable을 리턴하는 쪽에서 사용하므로 아래처럼보단 requestSomeAPI의 리턴 부분에 share()사용하여 사용
let requestObservable = API.requestSomeAPI().share() requestObservable .bind { _ in print("구독1!") } .disposed(by: self.disposeBag) requestObservable .bind { _ in print("구독2!") } .disposed(by: self.disposeBag) requestObservable .bind { _ in print("구독3!") } .disposed(by: self.disposeBag) /* request 카운트 1 구독1! 구독2! 구독3! */
share(replay:scope:) 연산자
- replay
- 신규 subscriber에게 이전 방출했던 요소를 몇 개를 기록했다가 방출할(replay) 것인가
- scope
- .whileConnected(subscriber가 한 개 이상 있으면 replay가 유지되지만, dispose되어 0개가 되면 replay 버퍼 초기화)
- .forever (subscriber가 없어도 버퍼 유지)
ex) replay 이해하기 - share(replay: 2, scope: .whileConnect) 예시
var count = 0
let tapObservable = self.didTapButtonObservable
.map { _ -> Int in
count += 1
return count
}
.share(replay: 2, scope: .whileConnected)
tapObservable
.do(onNext: { print($0) })
.subscribe()
.disposed(by: self.disposeBag)
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
print("두 번째 Subscriber 구독 시작!")
tapObservable
.do(onNext: { print($0) })
.subscribe()
.disposed(by: self.disposeBag)
}
-> 두 번째 subscription에게 스트림에서 방출되었던 요소 중 2가지(replay:2)를 한꺼번에 방출
ex) whileConnect, forever 이해하기 - share(replay: 2, scope: .whileConnect) 예시
- 두 번째 subscription이 생기기 전에 firstSubscription.dispose()가 호출되어, replay 버퍼가 해지
- 즉, 이전에 방출된 값들을 두 번째 subscription에서는 받아볼 수 없음
var count = 0
let tapObservable = self.didTapButtonObservable
.map { _ -> Int in
count += 1
return count
}
.share(replay: 2, scope: .whileConnected)
let firstSubscription = tapObservable
.do(onNext: { print($0) })
.subscribe()
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
firstSubscription.dispose() // <-
print("두 번째 Subscriber 구독 시작!")
tapObservable
.do(onNext: { print($0) })
.subscribe()
.disposed(by: self.disposeBag)
}
-> .whileConnected를 .forever로 바꾸면 기존 subscriber가 dispose되어도 버퍼 유지
var count = 0
let tapObservable = self.didTapButtonObservable
.map { _ -> Int in
count += 1
return count
}
.share(replay: 2, scope: .forever)
let firstSubscription = tapObservable
.do(onNext: { print($0) })
.subscribe()
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
firstSubscription.dispose() // <-
print("두 번째 Subscriber 구독 시작!")
tapObservable
.do(onNext: { print($0) })
.subscribe()
.disposed(by: self.disposeBag)
}
cf) share() 연산자는 `share(replay:0, scope: .whileConnected)`와 동일
* 전체 코드: https://github.com/JK0369/ExShare
* 참고
https://medium.com/@_achou/rxswift-share-vs-replay-vs-sharereplay-bea99ac42168
https://medium.com/gett-engineering/rxswift-share-ing-is-caring-341557714a2d
'RxSwift > RxSwift 응용' 카테고리의 다른 글
Comments