[Combine] 비동기 이벤트에 컴바인을!
비동기 이벤트는 언제 쓰이나?
- Completion Handler: 오랫동안 동작하는 업무가 종료될 때 한번 실행되는 클로져를 제공
- 클로져 프로퍼티: 비동기 이벤트가 발생할 때마다 실행하기 위함
컴플리션 핸들러 클로져를 Future로 대체하기
아래와 같은 컴플리션 핸들러가 있다고 가정.
func performAsyncAction(completionHandler: @escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline:.now() + 2) {
completionHandler()
}
}
위 패턴은 컴바인의 Future
로 대체가능.
Future
: 업무를 수행하고 난 뒤에 비동기로 성공 또는 실패를 전달하는 퍼블리셔
만약 성공이라면, Future
의 Promise
가 실행됨
Promise
: Future
에 의해 생성된 element를 전달 받는 클로져
업무가 완료되면 Future
가 Promiss
를 호출하고 Promiss
에 성공 또는 실패를 가리키는 Result
를 담도록 함.
호출자는 이 Result
를 Future
로부터 비동기로 전달받음.
func performAsyncActionAsFuture() -> Future <Void, Never> {
return Future() { promise in
DispatchQueue.main.asyncAfter(deadline:.now() + 2) {
promise(Result.success(()))
}
}
}// 함수의 리턴값이 Future, 즉 퍼블리셔이기 때문에
// `sink(receiveValue:)` 와 같은 Subscriber를 사용하여 접근.
cancellable = performAsyncActionAsFuture()
.sink() { _ in print(“Future succeeded.”) }
Future의 파라미터를 나타낼 때 아웃풋 타입 사용하기
오랫동안 돌아가는 작업이 어떤 값을 생성하는 경우, 즉, 컴플리션 핸들러에 파라미터가 있는 경우 Future
가 퍼블리싱하는 아웃풋 타입으로 파라미터를 선언하여 컴바인으로 대체
// Future가 Int형 element 를 생성한다고 선언
func performAsyncActionAsFutureWithParameter() -> Future <Int, Never> {
return Future() { promise in
DispatchQueue.main.asyncAfter(deadline:.now() + 2) {
let rn = Int.random(in: 1…10) // Result 타입이 Promise에 Int 값을 전달하도록 사용가능
promise(Result.success(rn))
}
}
}
Promise
를 실행할 때 Future
는 그 값을 퍼블리싱하고 호출자는 sink(receiveValue:)
와 같은 subscriber로 전달 받음
cancellable = performAsyncActionAsFutureWithParameter()
.sink() { rn in
print(rn)
}
반복적으로 호출되는 클로저를 Subject로 대체하기
아래와 같은 클로져 프로퍼티가 있다고 가정
vc.onDoSometing = { print(“뭔가 했음”) }
위 패턴은 Subject
로 대체할 수 있음.
Subject
send()
메소드를 사용해서 아무 때나 새 element를 퍼블리싱- 패턴을 적용할 때는
PassthroughSubject
또는CurrentValueSubject
를 private 하게 사용하고AnyPublihser
를 public 하게
private lazy var myDoSomethingSubject = PassthroughSubject<Void, Never>()// private 한 PassthroughSubject를 eraseToAnyPubliser 를 통해 외부로 노출
lazy var doSomethingSubject = myDoSomethingSubject.eraseToAnyPublisher()
클로져 프로퍼티 대신에 아래와 같이 sink(receiveValue:)
와 같은 subscriber 를 사용
cancellable = vc.doSomethingSubject
.sink() { print(“Did something with Combine.”) }
에러가 발생하거나 더이상 이벤트가 없을 때 send(completion:)
을 사용