티스토리 뷰

🍏/SwiftUI + Combine

[Combine] Future

eungding 2020. 9. 3. 02:10
728x90
반응형

FuturePublisher 프로토콜 을 conform하고 있다.

Swift에서 asynchronous 프로그래밍을 위해 callback기반 completion handler를 사용했는데, (Rx안쓴다면)

이제 Future를 사용하면 된다..!
 

Future는 말그대로 아직 일어나지 않은 미래를 의미한다(??)

 

Future는 Output과 Error를 가지고 있고 (Publisher를 conform하니까 당연쓰)

final public class Future<Output, Failure> : Publisher where Failure : Error

 

Promise 클로져가 있는데,

 

 

Promise 클로져는 이렇게 생겼다. 

 

 

 

[1] 기본 사용법

let future = Future<Int, Never> { promise in
    promise(.success(1))
}

future.sink(receiveCompletion: { print($0) },
            receiveValue: { print($0) })
         
         
// 출력
// 1
// finished   

 

let future = Future<Int, Never> { promise in
    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
        promise(.success(1))
    }
}

future.sink(receiveCompletion: { print($0) },
            receiveValue: { print($0) })

print("end")


// 출력
// end
// 1
// finished

 

위의 두 예제를 통해 알 수 있듯이

Future는 one-shot이다. 즉  promise에게 value나 error를 한번 보내고 나면 바로 finished 된다. 

 

value를 두번 보내는 예제를 보면 더 명확하다. 

첫번째 value를 보낸 후, 스트림이 바로 끝나기때문에 두번째 value가 출력이 안되는 것을 볼 수 있다. 

let future = Future<Int, Never> { promise in
    promise(.success(1))
    promise(.success(2))
}

future.sink(receiveCompletion: { print($0) },
            receiveValue: { print($0) })
            
            
// 출력
// 1
// finished

 

[2] 실제 예제

 APIService의 getUser라는 메소드가 이렇게 생겼는데, 

    // session이 valid한지 체크할때 쓸 API.
    func getUser(with session: String, completion: @escaping (Result<Void, APIError>) -> ()) {
        
        let url = ""
        
        let headers: HTTPHeaders = ["Cookie": "SESSION=\(session)"]
        
        AF.request(url, method: .get, parameters: [:], headers: headers).responseData { (response) in
            
            let statusCode = response.response?.statusCode ?? 0
            let code = Code(rawValue: statusCode) ?? .notDefined
            
            switch response.result {
            case .success:
                if code == .ok {
                    completion(.success(()))
                } else {
                    completion(.failure(APIError(code: code)))
                }
            case .failure:
                completion(.failure(APIError(code: code)))
            }
        }
    }

 

뷰모델에서 completion 쓰기 싫어서 이런식으로 썼다. 

 func isValidSession(_ session: String) -> AnyPublisher<Bool, Never> {
      return Future<Bool, Never> { promise in
          apiService.getUser(with: session) { result in
              switch result {
              case .success:
                  promise(.success(true))
              case .failure:
                  promise(.success(false))
              }
          }
      }.eraseToAnyPublisher()
}
   
func determineNeedLogin() {
    isValidSession(session)
        .sink { isValid in
            self.needLogin = !isValid
        }
        .store(in: &cancellables)
}

 

 

물론 eraseToAnyPublisher 안해주고 그냥 이렇게 해도 된다 (차이는 문서 참고!)

    func isValidSession(_ session: String) -> Future<Bool, Never> {
        return Future<Bool, Never> { promise in
            apiService.getUser(with: session) { result in
                switch result {
                case .success:
                    promise(.success(true))
                case .failure:
                    promise(.success(false))
                }
            }
        }
    }

 

 

[3] RxSwift의 어떤 개념과 대응될까?

 

RxSwift의 Observable.create 또는 Single과 비슷하다고 할 수 있겠다. 

 

대응이 아니라 비슷하다고 말한 이유는

 

RxSwift의 Single은 success, error 두가지 이벤트만 받을 수 있는데, Future는 종료 이벤트까지 받을 수 있어서 Single과 대응된다고 하기가 좀 그렇고,,,

 

RxSwift의 Observable.create는 onCompleted를 해줘야지 스트림이 끝나는데, Future는 value나 error 한번 발행되면 알아서 스트림이 끝나니까 Observable.create 와 대응된다고 하기가 좀 그렇다,,

 

 

 

[ Reference ]

기본 사용법 예제들은 다 여기서 코드를 가져왔습니다.

 

www.vadimbulavin.com/asynchronous-programming-with-future-and-promise-in-swift-with-combine-framework/

 

Asynchronous Programming with Futures and Promises in Swift with Combine Framework

Learn asynchronous programming in Swift 5 with Combine futures and promises. What is a future and a promise? How to use futures and promises with the Combine framework in Swift 5? What is the difference between promises and callbacks? These are the questio

www.vadimbulavin.com

 

 

 

[ 더 보면 좋을 것 ]

 

Future 의 value 프로퍼티를 통해 async/await syntax 를 사용할 수 도 있습니다. 

func generateAsyncRandomNumberFromFuture() -> Future <Int, Never> {
    return Future() { promise in
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            let number = Int.random(in: 1...10)
            promise(Result.success(number))
        }
    }
}

let number = await generateAsyncRandomNumberFromFuture().value
print("Got random number \(number).")

 

자세한 것은 문서 > Integrating with Swift Concurrency  참고! 

 

 

 

반응형
댓글