Resources in Combine

이세진·2022년 9월 20일
0

iOS

목록 보기
46/46
post-custom-banner

생성일: 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가 서버에 요청을 두 번 보내서 데이터를 가져오는 상황이 발생하는 것이다.

이는 매우 비효율적이다.

해결책

share


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.

multicast


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() 하여 데이터를 한 번에 받아올 수 있다.


Future


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를 전달

profile
나중은 결코 오지 않는다.
post-custom-banner

0개의 댓글