생성일: 2022년 8월 19일 오후 10:49
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { fatalError("Invalid URL") }
let request = URLSession.shared.dataTaskPublisher(for: url).map(\.data).print()
let subscription1 = request.sink(receiveCompletion: { _ in }, receiveValue: {
print($0)
})
let subscription2 = request.sink(receiveCompletion: { _ in }, receiveValue: {
print($0)
})
// 출력값
/*
receive subscription: (DataTaskPublisher)
request unlimited
receive subscription: (DataTaskPublisher)
request unlimited
receive value: (27520 bytes)
27520 bytes
receive finished
receive value: (27520 bytes)
27520 bytes
receive finished
*/
특정 URL로 API 요청을 보내고 받아온 값을 출력하는 간단한 예시이다. 이때, 하나의 publisher에서 서버 통신을 해서 데이터를 가져오고 두 개의 subscription이 해당 publisher를 구독하고 있다면 위의 출력값과 같은 상황이 발생한다.
분명 동일한 데이터를 구독하고 있는데 publisher가 서버에 요청을 두 번 보내서 데이터를 가져오는 상황이 발생하는 것이다.
이는 매우 비효율적이다.
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { fatalError("Invalid URL") }
let request = URLSession.shared.dataTaskPublisher(for: url).map(\.data).print().share()
let subscription1 = request.sink(receiveCompletion: { _ in }, receiveValue: {
print($0)
})
let subscription2 = request.sink(receiveCompletion: { _ in }, receiveValue: {
print($0)
})
// 출력값
/*
receive subscription: (DataTaskPublisher)
request unlimited
receive value: (27520 bytes)
27520 bytes
27520 bytes
receive finished
*/
publisher에 share()
를 붙여주면 위와 같은 중복 서버 요청 상황이 해결된다.
share() : Shares the output of an upstream publisher with multiple subscribers.
var subscription3: AnyCancellable? = nil
let subject = PassthroughSubject<Data, URLError>()
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { fatalError("Invalid URL") }
let request = URLSession.shared.dataTaskPublisher(for: url).map(\.data).print().multicast(subject: subject)
let subscription1 = request.sink(receiveCompletion: { _ in }, receiveValue: {
print("Subscription1")
print($0)
})
let subscription2 = request.sink(receiveCompletion: { _ in }, receiveValue: {
print("Subscription2")
print($0)
})
let cancellable = request.connect()
// 출력값
/*
receive subscription: (DataTaskPublisher)
request unlimited
receive value: (27520 bytes)
Subscription2
27520 bytes
Subscription1
27520 bytes
receive finished
*/
share가 아닌 multicast를 사용할 수도 있다.
multicast(subject: )
는 connect()
를 호출해야 이벤트를 시작한다.
→ 하나의 publisher에 대한 여러 subscription을 만들어두고 원하는 시점에 connect() 하여 데이터를 한 번에 받아올 수 있다.
func performSomeWork() throws -> Int {
print("Performing some work and returning a result")
return 5
}
let future = Future<Int, Error> { fullfill in
do {
let result = try performSomeWork()
fullfill(.success(result))
} catch {
fullfill(.failure(error))
}
}
print("--------Subscribing---------")
let susbcription1 = future.sink(receiveCompletion: { _ in print("Subscription1 completed") }, receiveValue: {
print("Subscription1 received: \($0)")
})
let susbcription2 = future.sink(receiveCompletion: { _ in print("Subscription2 completed") }, receiveValue: {
print("Subscription2 received: \($0)")
})
// 출력값
/*
Performing some work and returning a result
--------Subscribing---------
Subscription1 received: 5
Subscription1 completed
Subscription2 received: 5
Subscription2 completed
*/
future는 우선 클로져 안의 동작을 즉시 실행한다. → “Performing some work and returning a result” 가 바로 출력된다.
그 후 구독이 발생하면 이전에 실행하여 얻은 값을 전달한다. → 위의 예시에서는 5를 전달