Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- collectionview
- combine
- uiscrollview
- clean architecture
- RxCocoa
- Clean Code
- Human interface guide
- Xcode
- 리팩토링
- ios
- MVVM
- map
- Observable
- 스위프트
- swiftUI
- Refactoring
- UITextView
- 리펙토링
- uitableview
- rxswift
- HIG
- 리펙터링
- ribs
- tableView
- swift documentation
- SWIFT
- 애니메이션
- 클린 코드
- Protocol
- UICollectionView
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - SwiftUI] ObservableObject 모델을 wrapping하여 protocol 다루기 ("Type any cannot conform to ObservableObject", @ObservedObject, ObservableObject) 본문
iOS 응용 (SwiftUI)
[iOS - SwiftUI] ObservableObject 모델을 wrapping하여 protocol 다루기 ("Type any cannot conform to ObservableObject", @ObservedObject, ObservableObject)
jake-kim 2024. 8. 30. 01:59@ObservedObject 사용 요건
- ObservableObject 모델을 사용할 때 아래 에러를 마주치는 경우가 존재
- 바로 @ObservedObject를 선언한 프로퍼티는 프로토콜이 아닌 구체적인 타입을 사용해야함
struct ContentView: View {
// error: Type 'any Personable' cannot conform to 'ObservableObject'
@ObservedObject var person: Personable
var body: some View {
HStack {
}
}
}
protocol Personable where Self: ObservableObject {
var name: String { get }
var age: Int { get }
}
class Person: Personable, ObservableObject {
@Published var name: String
@Published var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
- any를 붙여도 또 다른 에러가 발생
// error: Type 'any Personable' cannot conform to 'ObservableObject'
@ObservedObject var person: any Personable
해결 방법
- Personable을 내부적으로 가지고 있는 Wrapper class를 만들기
class AnyPerson: ObservableObject {
@Published var wrapped: any Personable
init(_ wrapped: any Personable) {
self.wrapped = wrapped
}
}
- 이렇게 만들면 @ObservedObject를 선언한 쪽에서도 구체적인 타입을 사용하기 때문에 오류가 나지 않음
struct ContentView: View {
@ObservedObject var person: AnyPerson // OK!
var body: some View {
HStack {
}
}
}
KeyPath를 통해 더욱 편리하게 만들기
- 모델이 아래와 같이 생기면 이 모델을 사용할 때 불필요하게 wrapped에 직접 접근해서 name, age 속성에 접근해야함
class AnyPerson: ObservableObject {
@Published var wrapped: any Personable
init(_ wrapped: any Personable) {
self.wrapped = wrapped
}
}
@ObservedObject var person: AnyPerson
...
person.wrapped.name
person.wrapped.age
- wrapped 없이 person.name, person.age 로 접근할 수 없을까?
- keyPath를 적용하면 wrapped 접근을 생략할 수 있고, wrapped도 private으로 만들어서 불필요한 정보를 외부에 노출 방지가 가능
- getter 역할을 하는 KeyPath와 setter 역할을 하는 WritableKeyPath 선언
@dynamicMemberLookup
class AnyPerson: ObservableObject {
@Published private var wrapped: any Personable
init(_ wrapped: any Personable) {
self.wrapped = wrapped
}
subscript<T>(dynamicMember keyPath: KeyPath<any Personable, T>) -> T {
wrapped[keyPath: keyPath]
}
subscript<T>(dynamicMember keyPath: WritableKeyPath<any Personable, T>) -> T {
get { wrapped[keyPath: keyPath] }
set { wrapped[keyPath: keyPath] = newValue }
}
}
완성)
struct ContentView: View {
@ObservedObject var person: AnyPerson
var body: some View {
HStack {
}
.onAppear {
print(person.age) // OK!
print(person.name) // OK!
}
}
}
--- 전체 코드
struct ContentView: View {
@ObservedObject var person: AnyPerson
var body: some View {
HStack {
}
.onAppear {
print(person.age)
print(person.name)
}
}
}
protocol Personable where Self: ObservableObject {
var name: String { get }
var age: Int { get }
}
class Person: Personable, ObservableObject {
@Published var name: String
@Published var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
@dynamicMemberLookup
class AnyPerson: ObservableObject {
@Published private var wrapped: any Personable
init(_ wrapped: any Personable) {
self.wrapped = wrapped
}
subscript<T>(dynamicMember keyPath: KeyPath<any Personable, T>) -> T {
wrapped[keyPath: keyPath]
}
subscript<T>(dynamicMember keyPath: WritableKeyPath<any Personable, T>) -> T {
get { wrapped[keyPath: keyPath] }
set { wrapped[keyPath: keyPath] = newValue }
}
}
'iOS 응용 (SwiftUI)' 카테고리의 다른 글
Comments