Swift - RxSwift: Hot과 Cold ②(share가 뭐야)

3 분 소요


목차

1: Swift - RxSwift: Hot과 Cold ①

  • Hot Observable과 Cold Observable의 개념

2: Swift - RxSwift: Hot과 Cold ②(share가 뭐야)

  • 간단한 예제들


지난 번 핫 & 콜드에 이어서 포스팅 합니다
솔직히 첨 봤을 때 개념만 보고는 뭔 소리야?? 싶었어서ㅋㅋ 간단한 예시를 추가함니다


간단한 API

struct Joke: Decodable {
    var value: String
}

struct NetworkManager {
    func getJoke() -> Observable<String> {
        var request = URLRequest(url: URL(string: "https://api.chucknorris.io/jokes/random")!)
        request.httpMethod = "GET"
        
        return URLSession.shared.rx.data(request: request as URLRequest)
            .map { data -> String in
                do {
                    print("굿")
                    let joke = try JSONDecoder().decode(Joke.self, from: data)
                    return joke.value
                } catch {
                    return ""
                }
            }
    }
}

간단간단하게 노리스 아저씨 농담 가져오기 API를 추가합니다
통신이 성공하면 굿을 출력하도록 할게요

상황 1

let result = NetworkManager().getJoke()

// do nothing with result

getJoke()를 해서 result에 저장했어요
아직 어떻게 쓸진 안 정해서 사용은 안 했습니다

실행 시키면??

아무 것도 출력 되지 않습니다
이전 포스트 내용과 같이, Cold Observable은 누군가가 구독했을 때부터 이벤트를 방출하기 때문

상황 2

let result = NetworkManager().getJoke()

result
    .subscribe(onNext: { joke in
        print("결과:", joke)
    })
    .disposed(by: disposeBag)

한 번 구독해서 프린트 해 봐야지
결과는 아래와 같습니다.

굿
결과: Chuck Norris is my godfather. As he is yours.

잘 되네요~~

상황 3?

let result = NetworkManager().getJoke()

result
    .subscribe(onNext: { joke in
        print("결과:", joke)
    })
    .disposed(by: disposeBag)
result
    .subscribe(onNext: { joke in
        print("결과2:", joke)
    })
    .disposed(by: disposeBag)

근데… 같은 거 2번 따로 사용하고 싶어지면 어떡하죠??
일케 두 번 subscribe 해 버릴게요

굿
결과: Chuck Norris can divide by zero
굿
결과2: Chuck Norris is the reason why the words "Bad" and "Ass" came together

오잉
결과가 다른 게 나오네요??
이게 어케 된 일이지

왜냐하면 마찬가지로 이전 포스트에서 알아 봤듯이, Cold Observable은 스트림을 분기시키지 않습니다
Cold Observable을 여러 번 구독했기 때문에 각각 별도의 스트림이 생성되어 요청이 여러 번 가게 됨

그럼 어떻게 하나요

share 사용

let result = NetworkManager().getJoke().share()
result
    .subscribe(onNext: { joke in
        print("결과:", joke)
    })
    .disposed(by: disposeBag)
result
    .subscribe(onNext: { joke in
        print("결과2:", joke)
    })
    .disposed(by: disposeBag)

이렇게 share()를 붙여주면 말 그대로 스트림을 공유해서 사용 가능 합니다.
맨 처음 구독이 생기면 걔를 공유함

굿
결과: Once you go Norris, you are physically unable to go back.
결과2: Once you go Norris, you are physically unable to go back.

이렇게 나눠 가져서 API 요청 한 번만 가능~~

share 좀 더 보기
func share(replay: Int = 0, scope: SubjectLifetimeScope = .whileConnected) -> Observable<String>

임마를 좀 더 자세히 보면 이렇게 생긴 걸 알 수 있슴니다 replay는 저장할 버퍼 크기, scope은 해당 버퍼의 생명 주기로 보면 됨

간단하게 예제 보면 바로 이해갈 거임

ObservableA 생성, share(replay: 3)

B의 ObservableA 구독

ObservableA 방출 -> 0
ObservableA 방출 -> 1
ObservableA 방출 -> 2
ObservableA 방출 -> 3
ObservableA 방출 -> 4

C의 ObservableA 구독

ObservableA 방출 -> 5
ObservableA 방출 -> 6

이 경우 각 B와 C가 받은 결과는??

B: 0, 1, 2, 3, 4, 5, 6
C: 2, 3, 4, 5, 6

이렇게~~
B는 처음부터 다 봤으므로 다 받고,
C는 중간에 들어 와서 공유된 스트림을 사용하는데, 버퍼 크기가 3이라서 구독 당시의 최근값 2, 3, 4를 받아 보고, 이후의 값들을 받을 수 있음

또 다른 예제

ObservableA 생성, share(replay: 3)

B의 ObservableA 구독

ObservableA 방출 -> 0
ObservableA 방출 -> 1
ObservableA 방출 -> 2
ObservableA 방출 -> 3
ObservableA 방출 -> 4

B의 ObservableA 구독 해지 // Add!!

C의 ObservableA 구독

ObservableA 방출 -> 5
ObservableA 방출 -> 6

B의 구독 해지를 낑가 넣었습니다
이 경우 각 B와 C가 받은 결과는??

B: 0, 1, 2, 3, 4
C: 5, 6

scope가 기본적으로 .whileConnected이기 때문에, B가 해지하면 버퍼가 해방되고, C는 이전 버퍼를 받지 못하게 됩니다

그럼 같은 경운데 share(replay: 3, scope: .forever)를 쓰면?
맨 처음 경우와 같은 답이 나옴니다

Hot Observable 사용

Subject와 Relay에서 보았듯이, 얘네는 Cold를 Hot으로 바꿔주는 애들이므로 얘네를 사용해도 됩니다

Hot은 스트림 분기를 시켜 주니까, 네트워크 요청은 한 번만 하고 그 뒤는 너네 알아서 해라가 되겠지

let result = PublishSubject<String>()

NetworkManager().getJoke()
    .bind(to: result)
    .disposed(by: disposeBag)

result
    .subscribe(onNext: { joke in
        print("결과:", joke)
    })
    .disposed(by: disposeBag)
result
    .subscribe(onNext: { joke in
        print("결과2:", joke)
    })
    .disposed(by: disposeBag)

한 번 바꿔서 결과를 서브젝트에 바인드하고 실행 해 보면?

굿
결과: Chuck Norris's version of a "chocolate milkshake" is a raw porterhouse wrapped around ten Hershey bars, and doused in diesel fuel.
결과2: Chuck Norris's version of a "chocolate milkshake" is a raw porterhouse wrapped around ten Hershey bars, and doused in diesel fuel.

잘 되네여



굿


태그: ,

카테고리:

업데이트:

댓글남기기