[출처:www.raywenderlich.com/books/combine-asynchronous-programming-with-swift/v2.0]
변화를 다루는 것은 Combine 의 핵심기능 입니다.
subscriber를 통해 Publisher 를 구독(subscribe) 하여 비동기적 event 를 처리할 수 있게 해줍니다.
이전 챕터에서 assign(to:on) 을 배웠습니다.
assign()은 주어진 객체에 대한 프로퍼티의 값(value)을 publisher가 매번 새로운 값을 emit 할 때 update 시켜줄 수 있습니다.
하지만 단 하나의 변수에 대해 변화를 관찰하려는 경우는 어떨까요?
Combine 은 이런 경우에 있어 몇가지 옵션을 제공합니다.
- KVO-compliant를 만족하는 모든(Any) 객체의 프로퍼티를 위한 publisher를 제공합니다.
- ObservableObject protocol 은 여러개의 변수들이 바뀔 수 있는 상황을 처리합니다.
What is KVO
KVO란 Key-Value Observing 의 약자로 program의 state 변화를 관찰하는 Swift와 Objective-C의 테크닉입니다.
몇개의 instance 변수들이 있는 객체 A를 다룬다고 했을 때 KVO 는 다른 객체들이 객체 A 의 instance 변수에 대한 변화를 관찰할 수 있게 해줍니다.
KVO 는 Observer pattern 의 전형적인 예시이고 Objective-C에서는 기본적으로 class 에 추가하는 모든 instance 변수들이 KVO 를 통해 바로 observable 하게 되지만 Swift의 경우 default로 disabled 되어 있습니다.
Objective-C class 가 Swift 에서 KVO 를 가능하게 하기 때문에 Swift class 의 base class 는 NSObject로 해야하고 @objc dynamic attribute 를 변수에 지정해줘야 한다. (이전방식)
새로운 API를 사용하면 observe(_:options:changeHandler) 메서드를 통해 KVO 를 할 수 있습니다.
Introducing publisher(for:options:)
KVO는 Objective-C에서 필수적인 기능이였습니다.
Foundation, UIKit, AppKit class의 많은 프로퍼티들이 KVO 적용이 가능합니다.
그래서 그것들에 변화를 KVO 를 통해 관찰할 수 있습니다.
KVO를 따르는 프로퍼티의 경우 관찰하기 쉬운데 아래 예제를 보자면
queue의 operationCount value 를 observing 하는 코드입니다.
매번 새로운 operation이 queue에 추가된다면 operationCount는 증가할 것이고 그 변화는 sink를 통해 출력될 것입니다.
그리고 queue가 작업을 끝내면 operationCount 는 감소하고 그 변화 또한 sink를 통해 출력됩니다.
OperationQueue 말고도 다른 많은 framework class들이 KVO 를 사용가능합니다.
해당 프로퍼티에 대해 publisher(for:)을 사용해 관찰을 시작하고 publisher를 통해 emitting value 에 대한 작업을 할 수 있게 됩니다.
* Apple 은 KVO가 가능한 프로퍼티 리스트를 정리해서 제공하지 않습니다. 각각의 class 문서의 경우 어떤 프로퍼티가 KVO가 가능한지 나와있지만 몇가지 프로퍼티의 경우 문서의 quick note만 정리되있습니다.
Preparing and subscribing to your own KVO-compliant properties
Apple 이 제공하는 Framework class 에서 KVO 를 사용해봤다면 이제는 custom 한 KVO-compliant property 를 observing 해봅니다.
이를 위해 두가지를 제공해야 하는데
- 객체는 class 여야 하고 NSObject를 상속받아야 합니다.
- properties를 observable 하게 만들기 위해 @objc dynamic attribute 를 붙여줍니다.
이렇게 하면 Combine 에서 관찰할 수 있는 KVO 사용이 가능한 프로퍼티를 가진 객체를 만들 수 있습니다.
*Swift 언어는 직접적으로 KCO 를 지원하지 않기 떄문에 @objc dynamic 을 통해 강제로 compiler 가 KVO 를 위한 숨겨진 trigger 메소드를 만들게 합니다. 이런 과정을 설명하는건 책에서 벗어나는 범위인데 이 과정은 NSObject protocol 과 관련이 있기 때문에 NSObject 를 inherit 하는 것 입니다.
아니 근데 NSObject 는 class 인데 왜 protocol 이라고 하지
NSObject protocol 이름은 NSObjectProtocol 이다.
Swift 에서 class name : XXX 일때
Conforming protocol 인지 inheriting superclass 인지 어떻게 알지?
objective-c 에서는 angle bracket으로 Protocol 감싸기 때문에 알기 쉬웠지만 Swift의 경우 동일하다.
어떻게 구분할까
- protocol 일 경우 필요한 메서드를 쓰라고 하기 때문에 알기 쉬울듯
- naming convention 에 따라서 뒤에 Protocol 이나 Delegate를 쓰면 되잖냐
Swift 는 protocol 을 type을 다룰때와 비슷하게 처리합니다.
왜 이렇게 디자인 됬냐면 많은 instance들 가운데서 protocol conformance 와 class inheritance를 즉시 구분하기가 힘들었기 때문입니다.
실제로 구분을 하려면 option-click 을 통해서 나오는 Declaration을 참고하면 됩니다.
예제 코드를 보면
- NSObject protocol 을 inherits 하는 class 를 만듭니다.
- 관찰하고 싶은 프로퍼티에 대해 @objc dynamic attribute를 표시합니다.
- publisher 를 만든후 integerProperty 에 대해 subscribe 하여 관찰을 시작합니다.
- 관찰하는 프로퍼티에 값을 바꿔줘봅니다.
초기값인 0을 출력하는게 좀 이상합니다. 만약에 이 초기값을 받고싶지 않으면 어떻게 해야 할까요?
TestObject는 평범한 Swift Int type을 사용하고 있고 Objective-C 의 KVO 기능을 사용하고 있는데 동작이 됩니다.
KVO 는 아무 Objective-C type과 동작되며 그 뜻은 Objective-C에 연결된 Swift type 과도 잘 동작한다는 것이고 이러한 Swfit type에는 거의 모든 Swift native type들이 포함되어 있습니다.
다른 타입의 프로퍼티를 추가해주고 값을 바꾸고 관찰해봅니다.
초기값들이 출력되는 걸 볼 수 있습니다.
만약 Objective-C로 bridged 되지 않은 Swift type을 쓰게 된다면 오류가 발생합니다.
*system frameworks object 에 대한 KVO 를 진행할 때는 주의해야 합니다. 문서가 해당 프로퍼티가 Observable 한지 확인해야 하며 왜냐하면 system object property list 를 보고 알 수 없기 때문입니다. Foundation, UIKit AppKit 등 모두 해당됩니다.
Observation options
변화를 관찰하는 full 메소드 시그니쳐는 publisher(for:options:) 입니다.
option 파라이터에는 4가지 값이 들어갈 수 있습니다. (디폴트 값은 [.inital] 임 - 모든 변화를 다 출력, 초기값도 출력]
- .inital : 초기값을 emit
- .prior : 변화가 발생했을 때 이전값과 새로운 값을 동시에 emit
- .old : 이번 publisher 에서는 사용안하고 아무일도 안함
- .new : 이번 publisher 에서는 사용안하고 아무일도 안함
만약 초기값을 원하지 않으면 아래와 같이 configure 하면 된다.
obj.publisher(for: \.stringProperty, option: [])
만약 .prior 를 사용하면 변화가 있을 때마다 2개씩 값을 받아오게 된다. (0,100) (100, 200)
ObservableObject
Combine의 ObservableObject 프로토콜은 NSObject를 상속받은 Object 뿐만 아니라 Swift Object 에서도 동작합니다.
@Published property wrapper를 사용해 compiler 가 만드는 objectWillChange publisher를 가지는 class 를 만들 수 있습니다.
이 방법을 통해 많은 양의 boilerplate 코드를 작성하지 않아도 됩니다.
예시를 들자면
ObservableObject protocol를 준수하는 것은 compiler가 자동으로 objecetWillChange 프로퍼티를 만들어 줄 수 있게 해줍니다.
그것은 ObservableObjectPublisher로써 Void 를 emit 하며 에러 타입은 Never 입니다.
매번 object 의 @Published 변수가 바뀔 때 마다 objectWillChange 는 trigger 되지만 어떤 특정 프로퍼티가 변화했는지는 알 수 없습니다.
이것은 화면 업데이트를 간소화하기 위해 이벤트를 통합하는 SwiftUI와 매우 잘 작동하도록 설계되었습니다.
Key points
- KVO는 대부분 Objective-C 런타임과 NSObject protocol 메소드에 의존합니다.
- Apple framework 상의 많은 Objective-C class들은 KVO 가 가능한 프로퍼티들을 제공합니다.
- observable 한 프로퍼티를 가지는 자신만의 Object를 만들고 싶다면 NSObject 를 상속하는 class 를 만들고 관찰을 원하는 변수에 @objc dynamic attribute 를 표시하면 됩니다.
- @Published를 통해 ObservableObject를 상속받아 objectWillChange publisher를 compiler 가 만들게 하여 해당 property 값이 가뀔 때 마다 동작을 trigger 할 수 있습니다. (근데 어떤게 바꼈는지는 특정할 수 없습니다.)
'iOS > Combine' 카테고리의 다른 글
#17 Schedulers (0) | 2020.11.03 |
---|---|
#Chapter15 In Practice: Combine & SwiftUI (0) | 2020.11.02 |
#9 Networking (0) | 2020.10.28 |
"UI events are asynchronous" (0) | 2020.10.28 |
#3 Transforming Operators (1) | 2020.10.28 |
댓글