Swift DynamicProperty
DynamicPropertyPermalink
DynamicProperty는 내부적으로 @State
, @AppStorage
, @StateObject
, @Environment
가 SwiftUI 에서 동작하게 해주는 마법같은 프로토콜이다.
공식 설명은 다음과 같다.
An interface for a stored variable that updates an external property of a view.
View
외부에 View
가 읽거나 쓰는 변수를 저장해두는 기능을 추가해주는 역할을 한다.
가령 View
는 struct
이며 immutable이고 SwiftUI 렌더링 엔진이 수시로 파괴하고 새롭게 생성하지만, 그 내부에 있는 @State
같은 값들은 계속해서 동일한 것이 유지되어야 하기 때문에 이러한 방식을 취한다.
이러한 SwiftUI의 동작방식 때문에 View
의 init
에서 myState = .init(initialValue: ...)
처럼 @State
를 초기화 하는 방식은 위험한 방식이다.
이 부분에 대해서 더 자세히 알고 싶다면 How to (not) initialize @State inside the View’s init 이라는 글을 읽어보는 것을 추천한다.
어쨌든 DynamicProperty
의 사용법을 알아보자. 우리도 이 프로토콜을 conform 하는 property wrapper
를 이용하면 @State
처럼 동작하는 property wrapper를 만들 수 있다.
DynamicProperty 사용법Permalink
@propertyWrapper
struct MyAppStorage: DynamicProperty {
private let storageKey: String
@State private var value: Bool
init(_ storageKey: String) {
self.storageKey = storageKey
self.value = UserDefaults.standard.bool(forKey: storageKey)
}
var wrappedValue: Bool {
get {
value
}
nonmutating set {
UserDefaults.standard.set(newValue, forKey: storageKey)
value = newValue
}
}
}
단순하게 Bool
을 UserDefaults
에 저장할 수 있는 propertyWrapper
이다.
@propertyWrapper
이므로 wrappedValue
를 적절하게 구현을 해주어야 한다.
set
에 nonmutating
을 붙여주는 이유는 struct
의 객체를 변경하지 않고 사용하기 위함이며 내부의 value
값은 변하더라도 struct
의 값이 아닌 SwiftUI 가 외부적으로 관리해주는 값이기 때문에 변경해도 문제가 없다는 것을 알려주기 위함이다.
View
의 body
같은 컨텍스트는 struct
이기 때문에 mutating context가 아니다. 따라서 mutating set
으로 표기되면 사용할 수 없기 때문에 SwiftUI에서 사용하는 DynamicProperty
들의 wrappedValue
setter들은 nonmutating
을 표기하는 것이다.
사용법은 다음과 같다.
struct ContentView: View {
@MyAppStorage("myKey")
private var isTapped: Bool
var body: some View {
VStack {
Text("Hello, world! \(isTapped)")
Button("Tap") {
isTapped.toggle()
}
.padding(.top)
}
.padding()
}
}
Comments