Combine : Apple에서 제공하는 반응형 프로그래밍 프레임워크
→ 비동기 이벤트 및 데이터의 처리와 반응형 프로그래밍을 위한 프레임워크
Publisher → Operator → Subscriber의 흐름으로 진행import Combine
// Publisher 생성 - Combine에서 제공하는 기본 Publisher 프로퍼티 이용
let numbers = [1, 2, 3, 4, 5].publisher
// Subscriber를 통해 값 출력
numbers
.map { $0 * 2 } // 값 변환 (Operator)
.sink { print($0) } // 값 출력 - Combine 기본 메소드 sink(receivedValue:)
// 2 4 6 8 10 출력
→ Combine에서는 기본적으로 여러 Publisher를 제공
→ Publisher를 subscribe 할 수 있는 두 가지 메소드 또한 제공
: sink(receiveCompletion:receiveValue:)
: assign(to:on:)
⇒ 예제에서는 sink(receiveValue:)를 사용하여 publisher인 numbers가 제공한 값(연산자를 통해 변환된)을 출력
⇒ sink 메소드를 통해 subscribe 되었으므로 numbers.map { $0 * 2 }.sink { print($0) } 자체가 Subscriber
참고: [Swift/Combine] Combine Framework - 이정훈님 velog
import Foundation
import Combine
// Publisher 생성
let pub = [10, 20, 30, 40, 50].publisher
// Subscriber 생성
pub.map { $0 * 2 } // 중간 연산자 - 값 2배 변환
.sink { print($0) } // 변환된 값 출력
💡 Just
A Publisher that emits an output to each subscriber just once, and then finishes
→ 각 subscriber에 output을 한 차례만 보내고(하나의 output만을 내보내고) 종료되는 publisher: publisher 체인의 시작부에 사용할 수 있다.
: Just publisher는 에러와 함께 실패할 수 없으며, 항상 값을 생성한다.(== 옵셔널 타입이 될 수 없다.)
참고: Just | Apple Developer Documentation
import Foundation
import Combine
let pub = Just(1)
pub.map { $0 + 50 }
.sink { print($0) }
// 51 출력
→ 처음 접하는 개념이라 이해와 구현이 어려워 힌트 그대로 사용하여 이런 코드인 것 같다~를 주석으로 작성...
import Foundation
import Combine
let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")!
var cancellable: AnyCancellable?
cancellable = URLSession.shared.dataTaskPublisher(for: url) // Publisher 생성
.map { $0.data } // url에서 data만 가져옴
.decode(type: Todo.self, decoder: JSONDecoder()) // JSON 타입을 디코드
.sink(
receiveCompletion: { print("Completion: \($0)") }, // Completion 결과 출력
receiveValue: { print("Title: \($0.title)") } // 수신 값 출력
)
// JSON Decode용
struct Todo: Codable {
var title : String
}
💡 Timer
A publisher that repeatedly emits the current date on a given interval
→ 현재 날짜를 주어진 주기에 따라 반복하여 내보내는 publisher
참고: Timer.TimerPublisher | Apple Developer Documentation
import Foundation
import Combine
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() // Timer publisher 생성
var cancellable: AnyCancellable? // (구독을) 취소 가능한 무언가..?
cancellable = timer.sink { print($0) } // timer.sink subscriber가 구독을 취소 가능하도록 cancellable에 할당
// main 큐에서 UI(현재 시간 출력) 업데이트, 함수의 deadline이 현재 시간으로부터 5초 뒤
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
cancellable?.cancel() // deadline 지나면 구독을 취소
}