[SwiftUI] Combine Futures & Promises

Junyoung Park·2022년 8월 24일
2

SwiftUI

목록 보기
48/136
post-thumbnail
post-custom-banner

How to use Futures and Promises in Combine with SwiftUI | Advanced Learning #20

Combine Futures & Promises

구현 목표

  • Combine의 미래 값을 가지고 올 PublisherFuture 사용법을 통해 비동기 데이터를 처리

구현 태스크

  1. 이스케이핑 클로저
  2. 일반적인 Publisher 구독을 통한 Combine 프레임워크의 비동기 처리
  3. Future 구독을 통한 Combine 프레임워크의 비동기 처리 → Promise 값을 통한 completion 핸들러

핵심 코드

    func getFuturePublisher() -> Future<String, Error> {
        return Future { promise in
            self.getEscapingClosure { returnedValue, error in
                if let error = error {
                    promise(.failure(error))
                } else {
                    promise(.success(returnedValue))
                }
            }
        }
    }

소스 코드

class FuturesBootCampViewModel: ObservableObject {
    @Published var title: String = "Starting Title"
    var cancellables = Set<AnyCancellable>()
    let url = URL(string: "https://www.google.com")!
    
    init() {
//        download()
//        downloadWithEscapingClosure()
//        downloadWithFuturePublisher()
        downloadWithFuturePublisher2()
    }
    
    func download() {
        getCombinePublisher()
            .sink { _ in
            } receiveValue: { [weak self] returnedValue in
                self?.title = returnedValue
            }
            .store(in: &cancellables)
    }
    
    func downloadWithEscapingClosure() {
        getEscapingClosure { [weak self] value, error in
            self?.title = value
        }
    }
    
    func downloadWithFuturePublisher() {
        getFuturePublisher()
            .sink { _ in
            } receiveValue: { [weak self] returnedValue in
                self?.title = returnedValue
            }
            .store(in: &cancellables)
    }
    
    func downloadWithFuturePublisher2() {
        doSomethingInTheFuture()
            .sink { _ in
            } receiveValue: { [weak self] returnedValue in
                self?.title = returnedValue
            }
            .store(in: &cancellables)
    }
    
    
    func getCombinePublisher() -> AnyPublisher<String, URLError> {
        URLSession.shared.dataTaskPublisher(for: url)
            .timeout(1, scheduler: DispatchQueue.main)
            .map({ _ in
                return "NEW VALUE"
            })
            .eraseToAnyPublisher()
    }
    
    func getEscapingClosure(completionHandler: @escaping (_ value: String, _ error: Error?) -> ()) {
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            completionHandler("New Value2", nil)
        }
        .resume()
    }
    
    func getFuturePublisher() -> Future<String, Error> {
        return Future { promise in
            self.getEscapingClosure { returnedValue, error in
                if let error = error {
                    promise(.failure(error))
                } else {
                    promise(.success(returnedValue))
                }
            }
        }
    }
    
    func doSomething(completionHandler: @escaping (_ value: String) -> ()) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
            completionHandler("NEW STRING")
        }
    }
    
    func doSomethingInTheFuture() -> Future<String, Never> {
        Future { promise in
            self.doSomething { value in
                promise(.success(value))
            }
        }
    }
}
  • FuturePublish 타입 중 성공/실패가 확실하게 결과값이 나오는 '하나'의 값을 리턴
  • promise를 통해 값 리턴 → 이스케이핑 클로저에 리턴값 및 에러를 핸들링 → Future를 구독하는 다운로드 코드에서 Combine 프레임워크의 sink, store 사용
struct FuturesBootCamp: View {
    @StateObject private var viewModel = FuturesBootCampViewModel()
    var body: some View {
        Text(viewModel.title)
            .font(.largeTitle)
            .fontWeight(.semibold)
    }
}
  • 비동기로 받아온 데이터를 그리고 있는 UI 뷰

구현 화면

profile
JUST DO IT
post-custom-banner

0개의 댓글