1. Combine 공식문서
- Customize handling of asynchronous events by combining event-processing operators.
이벤트 처리 연산자를 결합하여 비동기 이벤트를 사용자 지정 방식으로 처리하세요. - iOS 13.0+
iOS 13.0 이상 - iPadOS 13.0+
iPadOS 13.0 이상
- Overview
개요 - The Combine framework provides a declarative Swift API for processing values over time.
Combine 프레임워크는 시간에 따라 변하는 값을 처리하기 위한 선언형 Swift API를 제공합니다. - These values can represent many kinds of asynchronous events.
이러한 값들은 여러 종류의 비동기 이벤트를 나타낼 수 있습니다. - Combine declares publishers to expose values that can change over time, and subscribers to receive those values from the publishers.
Combine은 시간이 지나면서 변경될 수 있는 값을 노출하는 퍼블리셔(Publisher)와, 그 값을 수신하는 구독자(Subscriber)를 선언합니다. - The Publisher protocol declares a type that can deliver a sequence of values over time.
Publisher 프로토콜은 시간이 지나면서 값의 시퀀스를 전달할 수 있는 타입을 선언합니다. - Publishers have operators to act on the values received from upstream publishers and republish them.
퍼블리셔는 업스트림 퍼블리셔에서 받은 값에 대해 연산을 수행하고, 이를 다시 퍼블리셔 형태로 전달하는 연산자(Operators)를 제공합니다. - At the end of a chain of publishers, a Subscriber acts on elements as it receives them.
퍼블리셔 체인의 끝에서 구독자(Subscriber)는 받은 요소에 대해 특정 동작을 수행합니다. - Publishers only emit values when explicitly requested to do so by subscribers.
퍼블리셔는 구독자로부터 명시적인 요청이 있을 때만 값을 방출합니다. - This puts your subscriber code in control of how fast it receives events from the publishers it’s connected to.
이를 통해 구독자 코드가 퍼블리셔로부터 이벤트를 받는 속도를 직접 제어할 수 있습니다. - Several Foundation types expose their functionality through publishers, including Timer, NotificationCenter, and URLSession.
Timer, NotificationCenter, URLSession 등 여러 Foundation 타입이 퍼블리셔를 통해 기능을 제공합니다. - Combine also provides a built-in publisher for any property that’s compliant with Key-Value Observing.
Combine은 Key-Value Observing(KVO)을 준수하는 모든 프로퍼티에 대해 기본 퍼블리셔를 제공합니다. - You can combine the output of multiple publishers and coordinate their interaction.
여러 퍼블리셔의 출력을 결합하고, 그들의 상호작용을 조정할 수 있습니다. - For example, you can subscribe to updates from a text field’s publisher, and use the text to perform URL requests.
예를 들어, 텍스트 필드의 퍼블리셔에서 업데이트를 구독하고 입력된 텍스트를 사용하여 URL 요청을 수행할 수 있습니다. - You can then use another publisher to process the responses and use them to update your app.
그런 다음, 다른 퍼블리셔를 사용하여 응답을 처리하고 이를 앱을 업데이트하는 데 활용할 수 있습니다. - By adopting Combine, you’ll make your code easier to read and maintain,
Combine을 도입하면 코드가 더 읽기 쉽고 유지보수가 용이해집니다. - by centralizing your event-processing code and eliminating troublesome techniques like nested closures and convention-based callbacks.
이벤트 처리 코드를 중앙에서 관리하고, 중첩된 클로저나 관습적인 콜백 같은 복잡한 기법을 제거할 수 있습니다.
2. SubScriber 공식문서
- Overview
개요 - A Subscriber instance receives a stream of elements from a Publisher, along with life cycle events describing changes to their relationship.
Subscriber 인스턴스는 Publisher로부터 요소의 스트림을 수신하며, 관계의 변화를 설명하는 라이프 사이클 이벤트도 함께 받습니다. - A given subscriber’s Input and Failure associated types must match the Output and Failure of its corresponding publisher.
특정 Subscriber의 Input 및 Failure 연관 타입은 해당 Publisher의 Output 및 Failure 타입과 일치해야 합니다. - You connect a subscriber to a publisher by calling the publisher’s subscribe(_:) method.
Publisher의 subscribe(_:) 메서드를 호출하여 Subscriber를 Publisher에 연결할 수 있습니다. - After making this call, the publisher invokes the subscriber’s receive(subscription:) method.
이 호출이 이루어지면, Publisher는 Subscriber의 receive(subscription:) 메서드를 호출합니다. - This gives the subscriber a Subscription instance, which it uses to demand elements from the publisher, and to optionally cancel the subscription.
이를 통해 Subscriber는 Subscription 인스턴스를 받으며, 이를 사용하여 Publisher에 요소를 요청하거나 구독을 취소할 수 있습니다. - After the subscriber makes an initial demand, the publisher calls receive(_:), possibly asynchronously, to deliver newly-published elements.
Subscriber가 초기 요청을 수행한 후, Publisher는 receive(_:) 메서드를 호출하여 새로운 요소를 전달합니다. (비동기적으로 호출될 수도 있음) - If the publisher stops publishing, it calls receive(completion:), using a parameter of type Subscribers.Completion to indicate whether publishing completes normally or with an error.
Publisher가 발행을 중단하면, receive(completion:) 메서드를 호출하며, Subscribers.Completion 타입의 매개변수를 사용하여 정상 종료인지 오류 발생인지 나타냅니다. - Combine provides the following subscribers as operators on the Publisher type:
Combine은 Publisher 타입에서 사용할 수 있는 다음과 같은 Subscriber 연산자를 제공합니다. - sink(receiveCompletion:receiveValue:) executes arbitrary closures when it receives a completion signal and each time it receives a new element.
sink(receiveCompletion:receiveValue:)는 완료 신호를 받을 때 및 새로운 요소를 받을 때마다 임의의 클로저를 실행합니다. - assign(to:on:) writes each newly-received value to a property identified by a key path on a given instance.
assign(to:on:)는 새로 수신된 값을 지정된 인스턴스의 키 경로(KeyPath)로 식별된 속성에 할당합니다.
3. Publisher
1. 공식 문서
- Overview
개요 - A publisher delivers elements to one or more Subscriber instances.
Publisher는 하나 이상의 Subscriber 인스턴스에 요소를 전달합니다. - The subscriber’s Input and Failure associated types must match the Output and Failure types declared by the publisher.
Subscriber의 Input 및 Failure 연관 타입은 Publisher가 선언한 Output 및 Failure 타입과 일치해야 합니다. - The publisher implements the receive(subscriber:) method to accept a subscriber.
Publisher는 receive(subscriber:) 메서드를 구현하여 Subscriber를 받을 수 있도록 합니다.
- After this, the publisher can call the following methods on the subscriber:
이후 Publisher는 Subscriber에서 다음 메서드를 호출할 수 있습니다. - receive(subscription:): Acknowledges the subscribe request and returns a Subscription instance.
receive(subscription:) : 구독 요청을 확인하고 Subscription 인스턴스를 반환합니다. - The subscriber uses the subscription to demand elements from the publisher and can use it to cancel publishing.
Subscriber는 Subscription을 사용하여 Publisher에서 요소를 요청할 수 있으며, 필요하면 구독을 취소할 수도 있습니다. - receive(_:): Delivers one element from the publisher to the subscriber.
receive(_:) : Publisher가 Subscriber에 하나의 요소를 전달합니다. - receive(completion:): Informs the subscriber that publishing has ended, either normally or with an error.
receive(completion:) : Publisher가 정상 종료되었거나 오류가 발생했음을 Subscriber에 알립니다.
- Every Publisher must adhere to this contract for downstream subscribers to function correctly.
모든 Publisher는 하위 Subscriber가 올바르게 동작하도록 이 규칙을 따라야 합니다. - Extensions on Publisher define a wide variety of operators that you compose to create sophisticated event-processing chains.
Publisher에 대한 확장을 통해 다양한 연산자를 사용할 수 있으며, 이를 조합하여 복잡한 이벤트 처리 체인을 만들 수 있습니다. - Each operator returns a type that implements the Publisher protocol.
각 연산자는 Publisher 프로토콜을 구현하는 타입을 반환합니다. - Most of these types exist as extensions on the Publishers enumeration.
이러한 타입의 대부분은 Publishers 열거형의 확장으로 존재합니다. - For example, the map(_:) operator returns an instance of Publishers.Map.
예를 들어, map(_:) 연산자는 Publishers.Map 인스턴스를 반환합니다.
- Tip
팁 - A Combine publisher fills a role similar to, but distinct from, the AsyncSequence in the Swift standard library.
Combine의 Publisher는 Swift 표준 라이브러리의 AsyncSequence와 유사한 역할을 하지만, 서로 다른 방식으로 동작합니다. - A Publisher and an AsyncSequence both produce elements over time.
Publisher와 AsyncSequence는 모두 시간이 지나면서 요소를 생성합니다. - However, the pull model in Combine uses a Subscriber to request elements from a publisher,
하지만 Combine의 "풀 모델(Pull Model)" 은 Subscriber가 Publisher에서 요소를 요청하는 방식으로 동작합니다. - while Swift concurrency uses the for-await-in syntax to iterate over elements published by an AsyncSequence.
반면, Swift의 동시성(Concurrency)에서는 for-await-in 구문을 사용하여 AsyncSequence에서 발행된 요소를 순회합니다. - Both APIs offer methods to modify the sequence by mapping or filtering elements,
두 API 모두 요소를 매핑하거나 필터링하는 방법을 제공합니다. - while only Combine provides time-based operations like debounce(for:scheduler:options:) and throttle(for:scheduler:latest:),
하지만 Combine만이 debounce(for:scheduler:options:) 및 throttle(for:scheduler:latest:) 같은 시간 기반 연산을 제공합니다. - and combining operations like merge(with:) and combineLatest(::).
또한, merge(with:) 및 combineLatest(_:_:). 같은 퍼블리셔 결합 연산도 제공합니다. - To bridge the two approaches, the property values exposes a publisher’s elements as an AsyncSequence,
이 두 가지 접근 방식을 연결하기 위해, values 속성을 사용하면 Publisher의 요소를 AsyncSequence로 변환할 수 있습니다. - allowing you to iterate over them with for-await-in rather than attaching a Subscriber.
이를 통해 Subscriber를 붙이는 대신, for-await-in 구문을 사용하여 요소를 순회할 수 있습니다.
- Creating Your Own Publishers
직접 Publisher 생성하기 - Rather than implementing the Publisher protocol yourself,
Publisher 프로토콜을 직접 구현하는 대신, - you can create your own publisher by using one of several types provided by the Combine framework:
Combine 프레임워크에서 제공하는 여러 유형을 사용하여 직접 Publisher를 만들 수 있습니다. - Use a concrete subclass of Subject, such as PassthroughSubject, to publish values on-demand by calling its send(_:) method.
PassthroughSubject 같은 Subject의 구체적인 서브클래스를 사용하여 send(_:) 메서드를 호출해 값을 원하는 시점에 발행할 수 있습니다. - Use a CurrentValueSubject to publish whenever you update the subject’s underlying value.
CurrentValueSubject 를 사용하면 해당 값이 변경될 때마다 자동으로 값을 발행할 수 있습니다. - Add the @Published annotation to a property of one of your own types.
사용자 정의 타입의 속성에 @Published 어노테이션을 추가할 수도 있습니다. - In doing so, the property gains a publisher that emits an event whenever the property’s value changes.
이렇게 하면 해당 속성은 값이 변경될 때마다 이벤트를 방출하는 Publisher가 됩니다. - See the Published type for an example of this approach.
이 방법의 예제는 Published 타입을 참고하세요.
3. 프로퍼티 && 함수
1. associatedtype Output
- 필수 구현
- 정의 : Publisher가 발행하는 값의 유형입니다.
2. associatedtype Failure : Error
- 필수 구현
- Error을 상속해야함
- 정의 : Publisher가 발행할 수 있는 오류의 유형입니다.
3. receive(subscriber:)
func receive<S>(subscriber: S)
where S : Subscriber,
Self.Failure == S.Failure,
Self.Output == S.Input
- 필수 구현
- Publisher 프로토콜을 준수하는 모든 타입은 이 메서드를 반드시 구현해야합니다.
- 지정된 Subscriber를 이 Publisher에 연결합니다.
- Subcriber의 Failure타입과 Input 타입이 Publisher의 Failure 타입과 Output타입이 같아야함.
- 매개 변수: subscriber
- 이 Publisher에 연결할 Subscriber
- Subscriber가 연결된 후, 값을 받을 수 있음
- 기본적으로 제공되는 subscribe(_:) 구현은 이 메서드를 호출합니다.
- subscribe(_:)를 사용하면 receive(subscriber:)를 사용하지 않아도 되나?
4. subscribe(_:)
func subscribe<S>(_ subscriber: S)
where S : Subscriber,
Self.Failure == S.Failure,
Self.Output == S.Input
- 지정된 Subscriber를 이 Publisher에 연결합니다.
- 연결된 후 매개 변수의 Subscriber는 값을 받을 수 있다.
- Publisher의 Output 타입과 Subscriber의 Input 타입이 같아야함
- Publisher의 Failure 타입과 Subscriber의 Failure 타입이 같아야함
- 항상 receive(subscriber:) 대신 subscribe(_:)를 호출해야합니다.
- Publisher를 구현하는 클래스는 반드시 receive(subscriber:)를 구현해야 합니다.
- Publisher의 기본 subscribe(_:)구현은 내부적으로 receive(subscriber:)를 호출합니다.
5. subscribe(_:)
func subscribe<S>(_ subject: S) -> AnyCancellable
where S : Subject,
Self.Failure == S.Failure,
Self.Output == S.Output
- 함수 선언
- S는 Subject 프로토콜을 준수하는 타입
- Publisher의 Output과 Subject의 Output 타입이 같아야함
- Publisher의 Failure 타입과 Subject의 Failure 타입이 같아야함
- 반환 값 : AnyCancellable
- 구독을 해제할 수 있는 객체
- 이를 저장해 두었다가 필요할 때 구독을 해제할 수 있다.
- 매개변수 : subject
- Publisher에 연결한 Subject
- Subject가 연결되면 Publisher가 발행하는 값을 받을 수 있음
- Subject
- Publisher와 Subscriber의 중간 역할
- 값을 직접 발행할 수 도 있고, 다른 Publisher의 값을 받아 다시 방출할 수도 있다.
6. map(_:)
func map<T>(_ transform: @escaping (Self.Output) -> T) -> Publishers.Map<Self, T>
- Upstream Publisher에서 수신한 모든 요소를 제공된 클로져를 사용하여 변환합니다.
- 함수 선언
- T : 변환된 요소의 타입
- transform : Self.Output 타입의 요소를 받아 T 타입의 요소로 변환하는 클로저
- 반환 값 : 변환된 요소를 발행하는 Publisher (Publishers.Map<Self, T> 타입)
7. tryMap(_:)
func tryMap<T>(_ transform: @escaping (Self.Output) throws -> T) -> Publishers.TryMap<Self, T>
- 변환 과정에서 오류를 발생시킬 가능성이 있는 경우 사용
- 즉, 클로져 안에서 오류를 발생시킬 가능성이 있을 때 사용
8. mapError(_:)
func mapError<E>(_ transform: @escaping (Self.Failure) -> E) -> Publishers.MapError<Self, E> where E : Error
- tryMap(_:)은 오류를 던질 수 있는 연산이므로, 오류 타입이 Error로 고정됨
- mapError(_:)를 사용하면 오류타입을 통일하여 다운스트림 연산자가 더 쉽게 오류를 처리할 수 있음
- 특정 publisher가 다른 publisher와 결합 될때, 오류 타입이 다르면 연산이 불가능하다.
- 즉, 오류 타입을 일치시키는 역할을 한다.
9. replaceNil(with:)
func replaceNil<T>(with output: T) -> Publishers.Map<Self, T> where Self.Output == T?
import Combine
let values: [Double?] = [1.0, 2.0, nil, 4.0, 5.0]
let cancellable = values.publisher
.replaceNil(with: 0.0)
.sink { print("\($0)", terminator: " ") }
// 출력 결과: "1.0 2.0 0.0 4.0 5.0 "
- 업스트립에서 Publisher가 Optional 값ㅇ르 방출하는 경우, nil 값을 특정 값으로 치환 가능
10. scan(_:_:)
func scan<T>(
_ initialResult: T,
_ nextPartialResult: @escaping (T, Self.Output) -> T
) -> Publishers.Scan<Self, T>
import Combine
// 1️⃣ 배열을 퍼블리셔로 변환
let numbers = [1, 2, 3, 4, 5].publisher
// 2️⃣ scan을 사용하여 누적 합계를 계산
let cancellable = numbers
.scan(0, +)
.sink { print("누적 합: \($0)") }
/*
입력값 누적 합계
0 0 + 1 = 1
2 1 + 2 = 3
3 3 + 3 = 6
4 6 + 4 = 10
5 10 + 5 = 15
*/
- 이전 결과값과 현재 결과 값을 이용하여 누적된 값을 생성하는 연산자
- 매개 변수
- initailResult
- 첫 번째 계산에 사용될 초기값
- 이전 값이 없을 대 기본값으로 사용됨
- nextPartialResult
- 이전 값과 현재 값을 인자로 받아 변환된 값 반환
- 매번 Publisher가 새로운 값을 방출할 때 실행됨
- initailResult
- 반환값
- 누적된 결과값을 새롭게 방출하는 Publisher (Publishers.Scan<Self, T>)
11. filter(_:)
func filter(_ isIncluded: @escaping (Self.Output) -> Bool) -> Publishers.Filter<Self>
- 주어진 조건을 만족하는 요소만 발행하는 연산자
- 클로져를 사용하여 각 요소를 검사하고, true를 반환하는 요소만 다운스트림(Subscriber)로 저장
12. compactMap(_:)
func compactMap<T>(_ transform: @escaping (Self.Output) -> T?) -> Publishers.CompactMap<Self, T>
- 옵셔널 값을 포함하는 스트림에서 nil 값을 제거하고, nil이 아닌 값을 방출
13. removeDuplicates()
func removeDuplicates() -> Publishers.RemoveDuplicates<Self>
- 이전의 요소와 동일한 값을 가진 요소를 필터링하여 중복된 값을 제거
- 연속된 중복 값만 제거( 이전에 나타난 값이라도 연속되지 않으면 제거되진 않음
- Output 타입이 Equatable을 준수해야 사용가능
- 즉, 비교연산(==)을 통해 중복 요소를 판단할 수 있어야함
14. removeDuplicates(by:)
func removeDuplicates(by predicate: @escaping (Self.Output, Self.Output) -> Bool)
-> Publishers.RemoveDuplicates<Self>
let words = ["Swift", "swift", "Combine", "COMBINE", "filter"].publisher
let cancellable = words
.removeDuplicates { $0.lowercased() == $1.lowercased() }
.sink { print($0) }
// 출력 결과:
// Swift
// Combine
// filter
- 사용자 정의 비교 클로져를 제공할 수 있음
15. replaceEmpty(with:)
func replaceEmpty(with output: Self.Output) -> Publishers.ReplaceEmpty<Self>
let numbers: [Double] = []
cancellable = numbers.publisher
.replaceEmpty(with: Double.nan)
.sink { print("\($0)", terminator: " ") }
// Prints "(nan)".
let otherNumbers: [Double] = [1.0, 2.0, 3.0]
cancellable2 = otherNumbers.publisher
.replaceEmpty(with: Double.nan)
.sink { print("\($0)", terminator: " ") }
// Prints: 1.0 2.0 3.0
- 업스트림 Publisher가 아무런 요소도 방출하지 않고 종료될 경우, 미리 지정한 기본값을 대신 방출하는 연산자
- 요소가 하나라도 있으면 그대로 방출한다.
- 아무 요소도 방출하지 않고 종료되면 output 값 방출한다.
16. replaceError(with:)
func replaceError(with output: Self.Output) -> Publishers.ReplaceError<Self>
- Publisher에서 오류가 발생했을 때 제공된 기본값으로 대체하고, 정상적으로 스트림을 종료하는 연산자
- 오류를 감지하면 즉시 대체 값 (output)을 방출한 후, 스트림을 정상적으로 완료
- 오류를 복구할 필요가 없고, 특정 기본값을 반환한 후 종료하는 경우 유용
17. collect()
func collect() -> Publishers.Collect<Self>
import Combine
let numbers = (0...10)
let cancellable = numbers.publisher
.collect()
.sink { print("\($0)") }
// 출력: "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
- 업스트림 퍼블리셔로부터 수신한 모든 요소를 수집하여, 업스트림 퍼블리셔가 완료될 때 단일 배열로 방출하는 연산자입니다.
- 업스트림 퍼블리셔가 완료되기 전까지 모든 요소를 메모리에 저장하며, 완료 시점에 한 번에 배열로 전달합니다.
- 만약 업스트림 퍼블리셔가 오류를 방출하면, 수집된 요소 없이 오류를 그대로 다운 스트림으로 전달합니다.
- 메모리 사용량을 주의해야함
- 업스트림 퍼블리셔가 완료될 때 까지 모든 요소를 메모리에 저장하므로, 요소의 수가 많을 경우 메모리 사용량이 크게 증가할 수 있다.
18. ignoreOutput()
func ignoreOutput() -> Publishers.IgnoreOutput<Self>
- 업스트림 퍼블리셔의 모든 요소를 무시하고, 완료 또는 실패와 같은 완료 이벤트만을 다운 스트림으로 전달하는 연산자 입니다.
- 데이터 자체보다는 퍼블리셔의 완료 여부에만 관심이 있을 때 유용하게 사용됩니다.
import Combine
struct NoZeroValuesAllowedError: Error {}
let numbers = [1, 2, 3, 4, 5, 0, 6, 7, 8, 9]
let cancellable = numbers.publisher
.tryFilter { anInt in
guard anInt != 0 else { throw NoZeroValuesAllowedError() }
return anInt < 20
}
.ignoreOutput()
.sink(
receiveCompletion: { print("completion: \($0)") },
receiveValue: { print("value: \($0)") }
)
// 출력: "completion: failure(NoZeroValuesAllowedError())"
19. reduce(_: _:)
func reduce<T>(
_ initialResult: T,
_ nextPartialResult: @escaping (T, Self.Output) -> T
) -> Publishers.Reduce<Self, T>
- 업스트림 퍼블리셔가 방출하는 여러 값을 하나의 축약된 값으로 결합하여, 업스트림 퍼블리셔가 완료될 때 최종 결과를 방출하는 연산자입니다.
- 매개변수
- initialResult : 초기 누적 값으로, 연산이 시작될 때 사용된다.
- nextPartialResult : 업스트림 퍼블리셔가 방출하는 각 값과 현재 누적 값을 결합하여 새로운 누적 값을 생성하는 클로저 입니다.
- 반환값
- 업스트림 퍼블리셔의 모든 값을 결합하여 최종 결과를 방출하는 퍼블리셔입니다.
- 만약 업스트림 퍼블리셔가 에러를 방출하면, 에러를 다운스트림으로 전달하고, 최종 값을 방출하지 않습니다.
- 주의 사항
- 업스트림 퍼블리셔가 완료될 때까지 최종 값을 방출하지 않으므로, 무한 스트림이나 완료되지 않는 퍼블리셔와 함께 사용하면 메모리 누수가 예상치 못한 동작이 발생할 수 있습니다.
- 업스트림 퍼블리셔가 에러를 방출하면, 최종 값을 방출하지 않고 에러를 전달하며 스트림이 종료됩니다.
출처
번역과 상세 설명은 챗 지피티의 도움을 받았습니다.
https://developer.apple.com/documentation/combine
Combine | Apple Developer Documentation
Customize handling of asynchronous events by combining event-processing operators.
developer.apple.com
'swift' 카테고리의 다른 글
[Swift] Codable 이해하기 (0) | 2025.04.27 |
---|---|
UIView의 draw(_:)는 그림을 그리는 함수이다. (0) | 2025.04.18 |
[Swift] map, flatMap, compactMap, reduce (0) | 2025.02.27 |
[iOS] super.init(nibName: nil, bundle: nil) 이해하기 (0) | 2025.02.26 |
[iOS] NSCoder 생성 방법 (0) | 2025.02.26 |