안녕하세여? 린다임니다.
최근에 마무리 한? 프로젝트가 있어여.
뛰어말어라는.. 실시간 지하철 API를 활용한 프로젝트인데
올해 안으로 출시 해볼것 같슴니다..
어쨌든 이 프로젝트 하면서 도입했던 기술이 컴바인이었는데..
컴바인 처음 공부할때 지피티한테 막 물어가면서 개념 공부를 하고
정리해뒀던 거를.. 블로깅 해놓을려구 노션에서 가져왔어요...
어쨌든 그렇기 때문에 충분히 틀린 말이 있을수도 있음을 감안하며
컴바인이 머지? 하는 분들이 읽으시는 걸 추천드립니다..
Combine이란?
- 비동기 작업들을 이벤트 처리 연산자로 결합하여 처리하는 방법
- 선언적인 프로그래밍 형태로 사용 → Stream 하나를 만들고 그 Stream에 필요한 Operator를 덫붙여 사용
*스트림은 무엇?
→스트림 : 실제의 입력이나 출력이 표현된 데이터의 이상화된 흐름
즉, 스트림은 운영체제에 의해 생성되는 가상의 연결 고리를 의미하며, 중간 매개자 역할을 함
💡 Stream!!! 자꾸 스트림 스트림, 스트림이 무엇이냐!!
Combine에서 upstream과 downstream은 데이터 흐름을 설명하는 용어
- Upstream:
- 정의: Upstream은 데이터의 흐름이 시작되는 곳. 즉, 데이터가 생성되는 원본
- 역할: Upstream == Publisher
- Publisher는 값을 생성하고, 변경하거나 완료되었음을 알리는 역할
- 예) Future, URLSession의 dataTask 등 여러가지 Combine 연산자
- Downstream:
- 정의: upstream에서 생성된 데이터의 흐름을 받아 처리하는 부분
- 즉, upstream에서 생성된 데이터를 소비하고 처리하는 구독자를 나타냄
- 역할: Downstream == Subscriber
- Subscriber는 값을 소비하고, upstream에서 생성된 데이터에 반응하거나, 완료되었음을 처리하는 역할을 함
- Subscriber는 upstream으로부터 받은 값을 처리하는 데 사용되는 작업을 수행함
즉, Publisher에서 생성된 데이터는 Subscriber를 통해 소비되고, Subscriber는 값을 받아서 필요한 작업을 수행함
Combine의 구성요소
1. Publisher (게시자)
- 일련의 시간에 따라 값을 전송할 수 있음을 선언하는 프로토콜
- 게시자는 하나 이상의 구독자(Subscriber)인스턴스에 요소를 전달함
- 게시자의 <Output, Failure>의 type과 구독자의 <Input, Failure> type은 일치해야함
- 게시자는 가입자를 수락하기 위해 수신(subscriber:) 메서드를 구현함
게시자는 구독자로부터 해당 메서드를 호출할 수 있음
- receive(subscripton:) : 구독 요청을 승인하구 구독 인스턴스를 반환. 구독자는 구독을 사용하여 게시자에게 요소를 요구하고 게시를 취소하는데에 사용할 수 있음.
- receive(_:): 게시자에서 구독자로 하나의 요소를 전달함
- receive(complete:): 구독자에게 일반적으로 또는 오류가 발생하여 게시가 종료되었음을 알림
→ 이런 게시자의 종류에는 Future, Just, Deferred, Empty, Fail, Record 6가지가 있음
2. Subscriber 구독자
- 게시자로부터 입력을 받을 수 있는 유형을 선언하는 프로토콜
- 인스턴스로 게시자들의 스트림 요소를 받음
구독의 순서
- 게시자의 subscription(_:) 메서드를 호출하여 구독자를 게시자에 연결
- 호출 수행 후 게시자는 구독자의 receive(subscription:) 메서드를 호출
- 구독자에게 구독 인스턴스를 제공하여 게시자에게 요소를 요구하고 선택적으로 구독을 취소하는 데 사용함
- 구독자가 초기 요청을 한 후 게시자는 receive(completion:)를 호출하여 새로 게시된 요소를 전달
- 게시자가 게시를 중지하면 subscribers.Complement 형식 매개 변수를 사용하여 receive(complete:)를 호출
Combine은 게시자타입의 연산자로부터 구독자를 제공한다. (즉, 구독자를 만들어주는 메서드들인듯)
- sink(receiveComplete:receiveValue:) : 완료 신호를 수신할 때와 새 요소를 수신할 때마다 임의 폐쇄를 실행
- assign(to:on :) : 새로 수신한 각 값을 지정된 인스턴스의 키 경로로 식별된 속성에 사용
Publisher Subscriber 예제로 확인
ex) Just publisher 사용
let publisher = Just("MetroCity")
let subscriber = publisher.sink { (value) in
print(value) // MetroCity
}
- Just를 사용하여 MetroCity를 게시함
- 게시한 MetroCity를 받을 수 있는 구독자를 .sink를 통하여 생성
- 즉, sink == subscriber를 만들어주는 메소드
- .sink(receiveCompletion:) 메서드도 있음!
3. Subject
- Publisher의 일종이며, 이벤트(값)을 방출할 수 있는 send 기능이(이벤트 방출 inject) 존재함
- 게시자의 한 유형으로, 값을 게시하고 관리하며 여러 구독자에게 전달할 수 있는 중앙 허브 역할을 하는 객체
→ Subject는 게시자(Publisher)와 구독자(Subscriber) 간의 중간 매개체 역할을함.
→ Subject는 값(이벤트)을 게시하고, 해당 값을 구독하는 다수의 구독자에게 전파함
💡 Subject는 게시자 프로토콜을 따르며, 값을 게시하고 구독자에게 전파하는 역할 Cancellable 프로토콜을 따르는 값을 반환하여 구독을 취소하거나 제어할 수 있음
그럼 일반적인 Publisher와의 차이점은?
- 구독자 수 관리:
- Publisher: 게시자는 여러 구독자에게 값을 게시할 수 있지만, 값을 한 번만 게시하면 해당 값을 받은 구독자는 다시 같은 값을 받을 수 없음
- Subject: Subject는 여러 구독자에게 값을 게시하고, 값이 게시될 때마다 모든 구독자에게 새로운 값이 전달됨
- 최신 값 유지:
- Publisher: 값을 한 번만 게시하고, 새로운 값을 받으면 이전 값을 잊고 새로운 값으로 대체함
- Subject: Subject는 현재 상태를 유지하고, 새로운 구독자가 구독을 시작하면 현재 값을 바로 전달함
- 완료 이벤트:
- Publisher: 게시자는 값 뿐만 아니라 완료(completion) 이벤트도 전달할 수 있음
- Subject: Subject도 완료 이벤트를 전달할 수 있음
- 게시자 종류:
- Publisher: Combine 프레임워크에 내장된 여러 Publisher 유형 중 하나
- Subject: Combine에서 Subject는 PassthroughSubject와 CurrentValueSubject와 같은 구체적인 유형을 가짐
- 구독 취소:
- Publisher: Cancellable을 반환하여 구독을 취소할 수 있음
- Subject: Subject도 Cancellable을 반환하여 구독을 취소할 수 있음
요약하면, Subject는 값의 스트림을 생성하고 유지하는 데 특화된 게시자의 한 유형!!
(Publisher는 Combine에서 제공하는 여러 유형의 게시자 중 하나일 뿐임)
Subject의 두 가지 주요 타입
- PassthroughSubject / CurrentValueSubject
- PassthroughSubject
- 이 Subject는 값의 전파를 담당
- PassthroughSubject를 사용하여 새로운 값을 게시하면, 해당 값을 Subject에 구독한 모든 Subscriber들에게 전달됨
- 이 Subject는 초기값을 가지지 않으며, 게시된 값은 현재 구독자에게만 전달됩니다.
import Combine let passthroughSubject = PassthroughSubject<String, Never>() let cancellable = passthroughSubject.sink { value in print(value) } passthroughSubject.send("Hello")
- CurrentValueSubject
- 이 Subject는 현재 값을 저장하고, 새로운 값이 게시되면 저장된 값을 업데이트함
- 초기값을 가질 수 있으며, 이 값은 새로운 구독자에게 즉시 전달됨
- 현재 값을 구독자에게 제공하고 변경 사항을 감지할 때 유용함
import Combine
let currentValueSubject = CurrentValueSubject<String, Never>("Initial Value")
let cancellable = currentValueSubject.sink { value in
print(value)
}
currentValueSubject.send("Hello")
4. Scheduler
- Scheduler는 비동기 코드 실행을 관리하는 중요한 역할을 하는 타입
- 작업(클로저가)이 언제 수행되어야 하는지, 어떤 큐에서 실행되어야 하는지를 결정함
- 스케줄러로 가능한 빨리 코드를 실행시킬 수도 있고,특정 시간 이후에 코드를 실행시킬 수도 있음
[애플 공식문서 왈]
스케줄러를 사용하여 가능한 한 빨리 코드를 실행하거나 미래의 날짜 이후에 코드를 실행할 수 있습니다.
개별 스케줄러 구현은 시간 유지 시스템이 의미 있는 모든 것을 사용합니다.
스케줄러는 이를 스케줄러 타임타입(SchedulerTimeType)이라고 표현합니다.
이 타입은 스케줄러 SchedulerTimeIntervalCovertible을 준수하므로
.millisecond(500)와 같은 편의 함수로 항상 이 시간을 표현할 수 있습니다.
스케줄러는 자신에게 전달된 작업을 실행하는 방법을 제어하는 옵션을 수락할 수 있습니다.
이 옵션은 어떤 스레드나 디스패치 큐가 작업을 실행하는지와 같은 요소를 제어할 수 있습니다.
그치만,, 지금까지 게시자, 구독자, 서브젝트를 공부할때 스케줄러가 없던 이유는…
→ Scheduler를 지정하지 않더라도 Combine은 기본 Scheduler를 제공함.
→ Scheduler는element가 생성된 스레드와 동일한 스레드를 사용
그럼 이런 스케줄러 개념을 어떤 메소드를 통해서 설정하는가?
-> receive(on: )과 subscribe(on:)
[1] receive(on: )
- publisher로 부터 element를 수신할 scheduler를 지정하는 역할
- downstream의 실행 컨텍스트를 변경하는 역할 ****(subscribe(on:)은 upstream에 영향
[2] subscribe(on:)
- subscribe, cancel, request operation을 수행 할 scheduler를 지정하는 역할 위에서도 언급했지만, upstream의 실행 컨텍스트를 변경
5. Combine의 Cancellable (AnyCancellable)
Cancellable
- Combine 작업들을 취소할 수 있다는 의미를 가지고 있는 프로토콜
- 활동이나 작업이 취소를 지원함을 나타내는 프로토콜
→ Combine에서는 이벤트 스트림을 action이라고 하며, 이 action을 취소할 수 있는 프로토콜이 Cancellable
Cancellable
Cancellable은 Combine에서 비동기 작업을 처리하는 중요한 요소 중 하나
이를 통해 구독을 취소하거나 제어할 수 있음
- Cancellable의 정의:
- Cancellable은 Combine에서 비동기 작업의 결과물로 반환되는 객체
- 주로 sink 메서드로 Combine Publisher에 구독할 때 반환됨
- Cancellable의 역할:
- 구독 취소: Cancellable은 cancel() 메서드를 제공하며, 이를 호출하면 해당 비동기 작업에 대한 구독이 취소됨, 이를 통해 메모리 누수를 방지하고 불필요한 업데이트나 이벤트를 방지할 수 있음
- 자원 관리: Cancellable으로 효과적인 자원관리 가능.
- 예) 뷰가 해제되면 해당 뷰와 관련된 비동기 작업에 대한 구독을 취소하여 자원을 효율적으로 관리할 수 있음
예시:
// 예시 Publisher
let publisher = [1, 2, 3].publisher
// sink를 통해 구독하고, 반환된 Cancellable을 활용
let cancellable = publisher.sink { value in
print("Received value: \\(value)")
}
// 비동기 작업에 대한 구독 취소
cancellable.cancel()
→ 위 예시에서 sink 메서드는 Cancellable을 반환하고, 이를 통해 나중에 구독을 취소할 수 있음
AnyCancellable
- 취소 시 제공된 클로저를 실행하는 유형 지우기 취소 가능 클래스
- AnyCancellable은 Combine에서의 Cancellable 프로토콜을 채택하는 타입 중 하나
- 이는 여러 개의 Cancellable을 하나로 묶어서 처리할 수 있는 유용한 타입
- AnyCancellable의 역할:
- 여러 Cancellable 관리: 여러 개의 Cancellable 객체를 하나의 AnyCancellable에 묶어서 관리할 수 있음
- 메모리 관리: AnyCancellable이 소유하는 모든 Cancellable 객체들은 deinit 시에 자동으로 해제됨
- 활용 예시:
var cancellables: Set<AnyCancellable> = []
// 예시 Publisher
let publisher = [1, 2, 3].publisher
// sink를 통해 구독하고, 반환된 AnyCancellable을 Set에 추가
let cancellable = publisher.sink { value in
print("Received value: \\(value)")
}
cancellable.store(in: &cancellables) // AnyCancellable을 Set에 저장
// Set 내의 모든 Cancellable을 해제
// 해당 뷰나 객체가 해제되거나 더 이상 필요하지 않을 때 호출
cancellables.removeAll()
위 예시에서 store(in:) 메서드를 사용하여 AnyCancellable을 Set에 저장하고,
나중에 필요할 때 해당 Set을 통해 모든 Cancellable을 해제할 수 있음
\AnyCancellable은 Combine에서 구독을 취소하거나 관리할 때 유용한 도구로 활용됨
그럼 연산자는?
데이터 스트림을 조작하고 변환하는 것 이 연산자
Combine 프레임워크는 다양한 연산자(Operators)를 제공하여 데이터 스트림을 조작하고 변환하는 데 사용됨
- Transforming Operators:
- map: 각 요소를 특정 함수를 사용하여 변환합니다.
- compactMap: nil이 아닌 결과만을 포함하는 새로운 스트림을 생성합니다.
- flatMap: 각 요소를 새로운 Publisher로 변환하고 이들을 하나의 스트림으로 병합합니다.
- Filtering Operators:
- filter: 특정 조건을 만족하는 요소만을 포함하는 스트림을 생성합니다.
- removeDuplicates: 연속된 중복 값을 제거합니다.
- Combining Operators:
- merge: 여러 개의 Publisher를 하나로 병합합니다.
- combineLatest: 여러 Publisher에서 최신 값을 결합합니다.
- zip: 여러 Publisher에서 동일한 인덱스의 요소를 결합합니다.
- Time-based Operators:
- debounce: 지정된 시간 동안 새로운 값이 도착하지 않으면 값을 내보냅니다.
- throttle: 최초로 값을 내보낸 후 일정 시간 동안 추가 값을 무시합니다.
- Error Handling Operators:
- catch: 오류가 발생했을 때 대체 Publisher를 사용합니다.
- retry: 오류가 발생하면 지정된 횟수만큼 재시도합니다.
- Utility Operators:
- sink: 값을 구독하여 처리하고, 완료 또는 에러 이벤트를 처리합니다.
- assign: 값을 SwiftUI 뷰의 속성에 할당합니다.
- Completion Handling Operators:
- handleEvents: 특정 이벤트가 발생할 때 특정 작업을 수행합니다.
- Mathematical and Aggregate Operators:
- count: Publisher에서 내보낸 요소의 수를 세어 값을 내보냅니다.
- reduce: 모든 요소를 결합하여 하나의 값으로 내보냅니다.
등이 있다고 함니다.
Combine의 간단한.. 개요라고 봐주시면 될거 같아여.
틀린거 있다면 댓글로 알려주세요!
'Swift' 카테고리의 다른 글
DispatchQueue(GCD)를 알아보자3, DispatchGroup과 wait/enter/leave까지.. (1) | 2024.01.15 |
---|---|
SwiftUI로 디자인 시스템(Design System)을 구현해보자 (SYM - Part 1) (1) | 2024.01.06 |
iOS Push Notification 구현하기 (feat, Firebase) (0) | 2023.12.01 |
apple sandbox push services 인증서를 신뢰하지 않음 해결하기 (3) | 2023.12.01 |
Tuist에서 CoreData 사용하기 (1) | 2023.11.29 |