Swift - RxSwift: Hot과 Cold ②(share가 뭐야)
목차
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.
잘 되네여
굿
댓글남기기