Deferred는 클로저 안에 정의된 Publisher가 실제로 구독이 되기 전까지 생성을 지연시키는 Publisher에요.
let deferredPublisherWithDelay = Deferred {
return Just([1,2,3,4,5]) // 네트워킹 작업을 가정한 Publisher
.delay(for: .seconds(1), scheduler: RunLoop.main)
}
deferredPublisherWithDelay
.sink { values in
print(values)
}
.store(in: &cancellables)
위 예제에서 Deferred의 내부 생성된 Publisher Just([1,2,3,4,5])
는 sink
메서드를 통해서 구독이 이루어 질때 인스턴스가 생성돼요.
Deferred는 Publisher을 주입하여 사용하기 때문에 상황에 따라 동적으로 다른 Publisher를 생성할 수 있습니다.
여기서는 인터넷 연결상태에 따라 Local 또는 원격 API 에서 데이터를 가져오는 Publisher를 구독하는 코드를 작성해보겠습니다.
// 로드될 구조체
struct UserProfile {
var userId: Int
var profileUrl: URL
}
// 로컬 데이터 로드
func fetchUserFromLocal() -> AnyPublisher<Result<UserProfile, Error>, CustomError> {
return Just(.success(UserProfile(userId: -1, profileUrl: URL(string: "<https://www.mock.placeholder>")!)))
.setFailureType(to: CustomError.self)
.eraseToAnyPublisher()
}
// 원격 데이터 로드
func fetchUserFromRemote(userId: Int) -> AnyPublisher<Result<UserProfile, Error>, CustomError> {
// 예제를 위한 가상의 URL
let url = URL(string: "<https://example.com/users/\\(userId)>")!
return Future<Result<UserProfile, Error>, Never> { promise in
promise(.success(.success(UserProfile(userId: userId, profileUrl: url))))
}
.setFailureType(to: CustomError.self)
.eraseToAnyPublisher()
}
// 인터넷 연결여부 체크 가정
func isConnectedToInternet() -> Bool {
return Bool.random()
}
// 인터넷 연결여부에 따라 다른 Publisher 적용
let dynamicPublisher = Deferred {
isConnectedToInternet() ? fetchUserFromRemote(userId: 1) : fetchUserFromLocal()
}
// Deferred 구독
dynamicPublisher
.sink { completion in
switch completion {
case .finished:
print("finished")
case .failure(let error):
print("error occured: \\(error)")
}
} receiveValue: { result in
switch result {
case .success(let userProfile):
print(userProfile)
case .failure(let error):
print("custom error occured: \\(error)")
}
}
.store(in: &cancellables)