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

작성일 :

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

Swift Combine은 애플의 리액티브 프로그래밍 프레임워크로, 비동기 이벤트 처리 및 데이터 스트림 관리에 유용하게 사용됩니다. Combine은 기본적으로 Publisher와 Subscriber로 구성되어 있으며, 이 글에서는 Publisher의 기본 개념과 사용법에 대해 깊이 있게 탐구해보겠습니다.

Publisher란 무엇인가?

Publisher는 Combine의 핵심 구성 요소 중 하나로, 데이터를 생성하고 이를 Subscriber에게 전달하는 역할을 합니다. Publisher는 데이터의 스트림을 나타내며, 이 스트림은 비동기적으로 발생할 수 있는 일련의 값 혹은 이벤트를 포함합니다. 간단히 말해, Publisher는 데이터를 발행하는 역할을 담당합니다.

Publisher는 두 가지 연관된 타입을 가지고 있습니다:

  1. Output: 발행되는 데이터의 타입
  2. Failure: 발행 과정에서 발생할 수 있는 오류의 타입

Publisher는 아래와 같은 기본 프로토콜을 따릅니다:

swift
protocol Publisher {
    associatedtype Output
    associatedtype Failure: Error

    func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input
}

기본 Publisher 타입

Combine 프레임워크는 다양한 내장 Publisher 타입을 제공합니다. 대표적인 몇 가지는 다음과 같습니다:

Just

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

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

이 Publisher는 "Hello, Combine!" 문자열을 단 한 번 발행하고 종료합니다.

Future

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

swift
let futurePublisher = Future<String, Error> { promise in
    DispatchQueue.global().async {
        // 비동기 작업 수행
        let result = "Async result"
        promise(.success(result))
    }
}

이 Publisher는 비동기 작업이 완료되면 "Async result"를 발행합니다.

Deferred

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

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

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

Publisher 사용 예제

Publisher를 사용하려면, 먼저 Subscriber를 정의해야 합니다. Combine에서는 다양한 Subscriber가 제공되며, 가장 일반적으로 사용되는 것은 sinkassign입니다.

Sink를 사용한 예제

sink는 클로저를 통해 값을 처리하고, 필요한 경우 오류를 처리할 수 있는 Subscriber입니다.

swift
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)")
})

이 코드는 Just Publisher가 "Hello, Combine!"을 발행하면, 이를 받아 출력하고 완료 시 "Finished"를 출력합니다.

Assign을 사용한 예제

assign은 Publisher의 값을 객체의 프로퍼티에 할당하는 데 사용됩니다.

swift
class MyObject: ObservableObject {
    @Published var myProperty: String = ""
}

let myObject = MyObject()
let cancellable = Just("Hello, Combine!").assign(to: \.myProperty, on: myObject)

이 예제에서 Just Publisher는 "Hello, Combine!" 값을 발행하고, 이 값은 myObjectmyProperty에 할당됩니다.

Operator 활용

Combine에서는 다양한 연산자(Operators)를 사용하여 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의 기본 개념과 사용법을 이해하면, 리액티브 프로그래밍의 장점을 최대한 활용할 수 있습니다. Combine을 통해 효율적인 비동기 처리를 구현하여, 더 나은 사용자 경험을 제공할 수 있기를 바랍니다.