Swift Combine Publisher의 기본 개념과 사용법

작성일 :

다양한 Swift Combine Publisher 사용 예제

Swift Combine은 애플의 리액티브 프로그래밍 프레임워크로, 비동기 이벤트 처리와 데이터 스트림 관리를 위해 설계되었습니다. Combine은 Publisher와 Subscriber로 구성되며, Publisher는 데이터를 발행하는 역할을 합니다. 이 글에서는 다양한 Swift Combine Publisher와 그 사용 예제에 대해 알아보겠습니다.

기본 Publisher 타입

Combine 프레임워크는 다양한 내장 Publisher 타입을 제공하여 다양한 상황에 맞게 사용할 수 있습니다. 주요 Publisher 타입과 그 사용 예제를 살펴보겠습니다.

Just

Just는 단 하나의 값을 발행하는 Publisher입니다. 오류를 발생시키지 않으며, 매우 간단한 형태의 Publisher입니다.

swift
import Combine

let justPublisher = Just("Hello, Combine!")

let cancellable = justPublisher.sink(receiveCompletion: { completion in
    switch completion {
    case .finished:
        print("Finished")
    case .failure(let error):
        print("Error: \(error)")
    }
}, receiveValue: { value in
    print("Received value: \(value)")
})

이 예제는 "Hello, Combine!" 문자열을 발행하고 완료를 출력합니다.

Future

Future는 비동기 작업의 결과를 발행하는 Publisher입니다. 비동기 작업이 완료되었을 때, 결과 값을 한 번 발행하고 종료합니다.

swift
let futurePublisher = Future<String, Error> { promise in
    DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
        promise(.success("Async result"))
    }
}

let cancellable = futurePublisher.sink(receiveCompletion: { completion in
    switch completion {
    case .finished:
        print("Finished")
    case .failure(let error):
        print("Error: \(error)")
    }
}, receiveValue: { value in
    print("Received value: \(value)")
})

이 예제는 비동기 작업이 완료된 후 "Async result"를 발행합니다.

Deferred

Deferred는 Subscriber가 구독할 때까지 Publisher의 생성 및 실행을 지연시키는 역할을 합니다. 이를 통해 구독 시점에 Publisher를 동적으로 생성할 수 있습니다.

swift
let deferredPublisher = Deferred {
    Just(Date())
}

let cancellable = deferredPublisher.sink(receiveValue: { value in
    print("Received date: \(value)")
})

이 예제는 구독 시점에 현재 날짜와 시간을 발행합니다.

Empty

Empty는 즉시 완료되거나 오류를 발생시키지 않고 아무 값도 발행하지 않는 Publisher입니다.

swift
let emptyPublisher = Empty<Int, Never>()

let cancellable = emptyPublisher.sink(receiveCompletion: { completion in
    switch completion {
    case .finished:
        print("Finished")
    case .failure:
        print("Error")
    }
}, receiveValue: { value in
    print("Received value: \(value)")
})

이 예제는 아무 값도 발행하지 않고 완료됩니다.

Fail

Fail는 특정 오류를 즉시 발행하고 완료되는 Publisher입니다.

swift
enum MyError: Error {
    case exampleError
}

let failPublisher = Fail<Int, MyError>(error: .exampleError)

let cancellable = failPublisher.sink(receiveCompletion: { completion in
    switch completion {
    case .finished:
        print("Finished")
    case .failure(let error):
        print("Error: \(error)")
    }
}, receiveValue: { value in
    print("Received value: \(value)")
})

이 예제는 오류를 발행하고 종료됩니다.

Sequence를 Publisher로 사용하기

Combine은 배열과 같은 Sequence 타입을 Publisher로 변환할 수 있습니다.

swift
let numbers = [1, 2, 3, 4, 5].publisher

let cancellable = numbers.sink(receiveCompletion: { completion in
    switch completion {
    case .finished:
        print("Finished")
    case .failure(let error):
        print("Error: \(error)")
    }
}, receiveValue: { value in
    print("Received number: \(value)")
})

이 예제는 배열의 각 요소를 순차적으로 발행하고 완료됩니다.

Timer Publisher

Combine은 주기적으로 값을 발행하는 Timer Publisher도 제공합니다.

swift
let timerPublisher = Timer.publish(every: 1.0, on: .main, in: .default).autoconnect()

let cancellable = timerPublisher.sink(receiveValue: { value in
    print("Timer fired at: \(value)")
})

이 예제는 1초마다 현재 시간을 발행합니다.

NotificationCenter Publisher

NotificationCenter에서 게시된 알림을 Publisher로 사용할 수도 있습니다.

swift
let notificationPublisher = NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)

let cancellable = notificationPublisher.sink(receiveValue: { notification in
    print("Received notification: \(notification)")
})

이 예제는 앱이 활성화될 때마다 알림을 발행합니다.

URLSession Data Task Publisher

Combine을 사용하여 네트워크 요청도 처리할 수 있습니다.

swift
let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")!
let dataTaskPublisher = URLSession.shared.dataTaskPublisher(for: url)

let cancellable = dataTaskPublisher.sink(receiveCompletion: { completion in
    switch completion {
    case .finished:
        print("Finished")
    case .failure(let error):
        print("Error: \(error)")
    }
}, receiveValue: { data, response in
    print("Received data: \(data)")
})

이 예제는 지정된 URL로 네트워크 요청을 보내고, 응답 데이터를 발행합니다.

Operator 활용

Combine에서는 다양한 연산자를 사용하여 Publisher의 데이터를 변환하거나 결합할 수 있습니다. 몇 가지 중요한 연산자를 살펴보겠습니다.

Map

map 연산자는 발행된 값을 변환하는 데 사용됩니다.

swift
let numbers = [1, 2, 3, 4, 5].publisher
let stringNumbers = numbers.map { "\($0)" }
let cancellable = stringNumbers.sink { print($0) }

이 코드는 숫자 배열을 문자열 배열로 변환하여 출력합니다.

Filter

filter 연산자는 조건에 맞는 값만을 통과시킵니다.

swift
let numbers = [1, 2, 3, 4, 5].publisher
let evenNumbers = numbers.filter { $0 % 2 == 0 }
let cancellable = evenNumbers.sink { print($0) }

이 코드는 짝수만을 통과시켜 출력합니다.

CombineLatest

combineLatest는 두 개의 Publisher의 최신 값을 결합합니다.

swift
let publisher1 = PassthroughSubject<String, Never>()
let publisher2 = PassthroughSubject<Int, Never>()

let combined = publisher1.combineLatest(publisher2)
let cancellable = combined.sink { (string, int) in
    print("Received: \(string) and \(int)")
}

publisher1.send("A")
publisher2.send(1)
publisher1.send("B")
publisher2.send(2)

이 코드는 각 Publisher의 최신 값을 결합하여 출력합니다.

결론

Swift Combine은 다양한 내장 Publisher와 연산자를 제공하여, 비동기 데이터 스트림을 효과적으로 관리하고 처리할 수 있는 강력한 도구입니다. 다양한 Publisher를 활용하면 복잡한 비동기 로직을 간단하고 명확하게 구현할 수 있으며, 이를 통해 더 나은 사용자 경험을 제공할 수 있습니다. Combine의 기본 사용법과 다양한 예제를 통해 리액티브 프로그래밍의 장점을 최대한 활용해보세요.