틀린 부분이나 다른 의견은 언제든 댓글로 지적해 주세요!!!
ios 투두 앱을 개발하는 중에 한가지 고민이 생겼다.
내 투두 앱은 달력탭과, 투두 리스트 탭이 있는데, 달력 탭에서도 그 날의 투두 리스트 목록을 하단에 작게 표기해준다. 투두 리스트 탭에서 투두를 완료하거나 삭제할 때, 달력 탭에서도 즉각 적용이 되어야 할텐데.. 그걸 어떻게 알릴까!? 라는 고민이 들었다.
옵저버 패턴처럼.. notify, update 할 수 있는 기능이 Swift 에 있지 않을까..? 하고 찾아보니 Combine이라는 것을 발견 했다!
Combine이란?
combine은 애플에서 정식으로 지원하는 프레임워크로, 비동기적으로 이벤트 핸들링을 지원하는 프레임워크라고 한다. 이전에 외부 라이브러리인 RxSwift의 애플 버전 이라고는 하는데.. 나는 Swift 코린이므로.. 모른다..! 그래서 차근 차근 하나하나 알아 볼 것이다.
Combine에 대해서 알아보기 위해 6가지 프로토콜을 이해하고 가면 좋다. 6가지 프로토콜을 알아보자
- Publisher
- Subscriber
- Subscription
- Subject
- Scheduler
- Cancellable
이렇게 6가지 프로토콜을 알아 볼 것이다.
Publisher
애플 공식 설명에서는 시간에 따른 값 시퀀스를 전달할 수 있는 타입이라고 한다. 내 멍충한 머리와 지식으로는.. 이해가 어렵다.. 그래서 차근차근~~ 알아본 결과 내 스스로 생각한 publisher는 데이터를 subscriber에게 제공하는 방식을 설정하고, 그 방식 대로 제공하는 제공자! 이다.
let just = Just("honeybug")
let subscriber = just.sink { value in
print(value)
}
- Just는 단 하나의 값만 전달하는 Publisher 구현체 이다.
- publisher.sink로 클로저를 넘기면, publisher로부터 값을 받아서 처리하는 subscriber를 클로저로 정의한다.
지금은 일단 간단하게 이정도로만 알아보고, 추후 Publisher에 대한 글에서 자세히 알아 볼 것이다.
Subscriber
서브스크라이버는 퍼블리셔 에게 데이터를 요청하고, 받은 데이터를 처리하는 것을 정의하는 프로토콜 이다.
- receive(subscription:) - 퍼블리셔를 최초로 구독에 성공 했을 때 Subscription 오브젝트와 함께 호출된다.
- receive(input:) - 퍼블리셔에게 데이터를 받아 처리하는 로직을 정의한다.
- receive(completion:) - 퍼블리셔에 대한 데이터 처리가 완료 되었을 때 호출된다.
let just = Just("sejin")
class HoneyBugSubscriber: Subscriber {
typealias Input = String
typealias Failure = Never
func receive(subscription: Subscription) {
print("subscription을 받았따!")
subscription.request(.unlimited)
}
func receive(_ input: String) -> Subscribers.Demand {
print("input 값을 받았다! input: \(input)")
return .none
}
func receive(completion: Subscribers.Completion<Never>) {
print("subscribe가 완료 되었따!")
}
}
just.subscribe(HoneyBugSubscriber())
Subscription
Subscription은 퍼블리셔와 서브스크라이버 간 소통을 하는 프로토콜 이다. 서브스크라이버에서 퍼블리셔에서 최초 연결 시 호출되는 receive(subscription:) 메소드에서 subscription.request(_:)를 통해 퍼블리셔에게 데이터를 요청한다.
Subject
Subject는 외부로부터 값을 지속적으로 주입 받을 수 있는 퍼블리셔 이다. 애플에서 정의해준 구현체로 아래와 같이 두가지가 있다.
- PassthroughSubject
- CurrentValueSubject
PassthroughSubject
PassthroughSubject는 내부적으로 값을 유지하지 않고, 외부에서 send로 값을 보내면, 구독 하고 있는(연결 되어 있는) Subscriber들에게 값을 보낸다. send(:) 이후 연결된 Subscriber에게는 값을 보내지 않는다.(값을 유지 하지 않으므로 보낼 수가 없다)
let passthroughSubject = PassthroughSubject<String, Never>()
let subscriber = passthroughSubject.sink { input in
print("subscriber, input: \(input)")
}
passthroughSubject.send("sejin")
passthroughSubject.send("jeehee")
let subscriber2 = passthroughSubject.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("subscriber2 finished")
case .failure(let error):
print(error)
}
}, receiveValue: { input in
print("subscriber2, input: \(input)")
})
passthroughSubject.send("inyoung")
passthroughSubject.send(completion: .finished)
passthroughSubject.send("already ended!")
subscriber, input: sejin
subscriber, input: jeehee
subscriber2, input: inyoung
subscriber, input: inyoung
subscriber2 finished
CurrentValueSubject
값을 내부적으로 하나만 유지하는 Subject이다.
let currentValueSubject: CurrentValueSubject = CurrentValueSubject<String, Never>("HoneyBug")
let subscriber = currentValueSubject.sink { input in
print("subscriber, input: \(input)")
}
currentValueSubject.send("sejin")
currentValueSubject.send("jeehee")
let subscriber2 = currentValueSubject.sink { input in
print("subscriber2, input: \(input)")
}
currentValueSubject.send("inyoung")
subscriber, input: HoneyBug
subscriber, input: sejin
subscriber, input: jeehee
subscriber2, input: jeehee
subscriber2, input: inyoung
subscriber, input: inyoung
subscriber2가 구독하는 시점에 subject는 "jeehee"를 갖고 있으므로, "jeehee"는 출력 된다.
Scheduler
Scheduler는 언제(즉시 혹은 몇초 뒤 등..) 또는 어느 스레드에서 publisher혹은 subscriber가 실행되는지에 대한 것을 정의 할 수 있는 프로토콜 이다. 기본적으로 subscribe나 publish 행위는 해당 오브젝트가 생성된 시점의 스레드에서 실행 되지만, Scheduler 프로토콜을 통해 지정해 줄 수 있다.
- receive(on:) - 해당 메소드 실행 시점 이후 퍼블리셔는, 파라미터로넘긴 Scheduler가 적용되어 정의 된다. (downstream)
- subscribe(on:) - 해당 메소드로 Scheduler를 지정해 주면, 해당 퍼블리셔를 subscribe하는 로직이 어떤 Scheduler에서 실행되는지에 대해 정의한다.
Cancellable
어떤 액션을 취소할 수 있는 프로토콜 이다. cancel을 호출하면 할당된 리소스를 해제한다.
네트워크, 타이머, 디스크IO등 리소스를 해제할 필요가 있을 경우 해제하는데 유용하게 사용할 수 있다.
sink등으로 subscriber를 생성하면 AnyCancellable로 리턴 된다.
일단은.. Combine을 자세히 알아보기 전에 이해하기 쉽도록 전반적인 프로토콜이나 클래스에 대해서 대략적으로 알아 보았다. 자세한 내용은 각 프로토콜 별로 좀 더 자세하게 글을 작성 할 예정이다.
틀린 부분이나 다른 의견은 언제든 댓글로 지적해 주세요!!!
'IOS' 카테고리의 다른 글
UIKit 스토리 보드 없이 프로젝트 시작하기 (0) | 2024.02.25 |
---|---|
iOS fcm을 이용한 remote push 알림 (0) | 2023.09.22 |
SwiftUI onDelete 수정자 사용 (0) | 2023.02.25 |
SwiftUI ViewBuilder란 (0) | 2023.02.17 |
Swift GCD (0) | 2023.02.02 |