J
[Swift & SwiftUI] PropertyWrapper 본문
1. Property Wrapper
PropertyWrapper는 Swift에서 속성의 동작을 캡슐화하여 속성 접근과 변경에 특정 로직을 추가할 수 있도록 지원하는 기능.
반복적인 코드를 줄이고 속성의 동작을 추상화하여 재사용 가능한 구조를 제공.
@propertyWrapper를 사용해 정의되며, SwiftUI의 @State, @Binding, @AppStorage 등이 대표적인 예.
2. Property Wrapper 기본 구조
@propertyWrapper
struct ClampedValue {
private var value: Int
private let range: ClosedRange<Int>
var wrappedValue: Int {
get { value }
set { value = min(max(range.lowerBound, newValue), range.upperBound) }
}
init(wrappedValue: Int, range: ClosedRange<Int>) {
self.range = range
self.value = range.contains(wrappedValue) ? wrappedValue : range.lowerBound
}
}
struct Example {
@ClampedValue(range: 0...10) var level: Int = 5
}
var example = Example()
example.level = 15
print(example.level) // 출력: 10 (범위 내에서 출력)
주요 구성 요소
- wrappedValue
- 속성의 값을 정의하거나 읽는 데 사용.
- 내부적으로 캡슐화된 값을 관리하고, 값을 읽거나 쓸 때 추가 로직을 수행할 수 있음.
- 초기화
- init(wrappedValue:)를 사용해 기본값 설정과 함께 속성을 초기화.
- 속성 정의
- @ClampedValue를 사용해 속성을 정의하면 자동으로 캡슐화된 로직이 적용.
3. Property Wrapper 추가 기능: projectedValue
PropertyWrapper는 projectedValue라는 추가 속성을 제공하여 속성 값 외에 부가 정보를 노출할 수 있음.
이는 $를 사용해 접근 가능.
@propertyWrapper
struct ExampleWrapper {
var wrappedValue: Int
var projectedValue: String {
return "Projected value for \\(wrappedValue)"
}
}
struct Example {
@ExampleWrapper var value: Int = 42
}
let example = Example()
print(example.value) // 출력: 42 (wrappedValue)
print(example.$value) // 출력: "Projected value for 42" (projectedValue)
SwiftUI의 @State와 @Binding의 projectedValue 측면 탐구
2024.04.29 - [SwiftUI] - [SwiftUI] @State와 @Binding의 projectedValue ($)
4. 일반적인 사용 사례
UserDefaults와 연동
UserDefaults를 간편하게 관리할 수 있도록 속성 래퍼를 구현.
@propertyWrapper
struct UserDefault<T> {
private let key: String
private let defaultValue: T
init(wrappedValue: T, key: String) {
self.defaultValue = wrappedValue
self.key = key
}
var wrappedValue: T {
get { UserDefaults.standard.object(forKey: key) as? T ?? defaultValue }
set { UserDefaults.standard.set(newValue, forKey: key) }
}
var projectedValue: String {
return "Key: \\(key), Default: \\(defaultValue)"
}
}
struct Settings {
@UserDefault(wrappedValue: false, key: "isDarkMode") var isDarkMode: Bool
}
let settings = Settings()
print(settings.isDarkMode) // 출력: false
print(settings.$isDarkMode) // 출력: Key: isDarkMode, Default: false
값 유효성 검사
값의 유효성을 검사하여 잘못된 입력을 방지.
@propertyWrapper
struct ValidatedString {
private var value: String
private let maxLength: Int
init(wrappedValue: String, maxLength: Int) {
self.maxLength = maxLength
self.value = String(wrappedValue.prefix(maxLength))
}
var wrappedValue: String {
get { value }
set { value = String(newValue.prefix(maxLength)) }
}
var projectedValue: String {
return value.count <= maxLength ? "Nickname of valid length" : "Nickname of invalid length"
}
}
struct User {
@ValidatedString(maxLength: 10) var username: String = "DefaultUser"
}
var user = User()
// 값 설정
user.username = "VeryLongUsernameExceedingLimit"
print(user.username) // 출력: VeryLongUs
print(user.$username) // 출력: Nickname of valid length
// 유효한 값 설정
user.username = "ShortName"
print(user.username) // 출력: ShortName
print(user.$username) // 출력: Nickname of valid length
4. SwiftUI에서의 활용
SwiftUI는 @State, @Binding, @Environment, @AppStorage 등 다양한 속성 래퍼를 제공하여 상태 관리를 쉽게 함.
@State
- 뷰 내부의 로컬 상태를 관리하며, 상태가 변경되면 뷰가 다시 렌더링.
struct ContentView: View {
@State private var count = 0
var body: some View {
VStack {
Text("Count: \\(count)")
Button("Increment") {
count += 1
}
}
}
}
@Binding
- 상위 뷰의 상태를 하위 뷰에서 읽고 수정할 수 있도록 연결.
struct ParentView: View {
@State private var isOn = false
var body: some View {
ToggleView(isOn: $isOn)
}
}
struct ToggleView: View {
@Binding var isOn: Bool
var body: some View {
Toggle("Switch", isOn: $isOn)
}
}
@AppStorage
- UserDefaults와 연결해 데이터를 저장하고 관리.
struct SettingsView: View {
@AppStorage("username") var username: String = "Guest"
var body: some View {
TextField("Username", text: $username)
}
}
6. PropertyWrapper vs. Generic
PropertyWrapper | Generic | |
캡슐화 여부 | 속성 동작 캡슐화 가능 | 일반화된 타입 로직 정의 |
사용 용도 | 특정 속성 동작 반복 제거 및 재사용 | 여러 타입에서 동일한 로직 재사용 |
접근성 | 속성 접근 시 자동으로 동작 적용 | 호출자가 명시적으로 사용 |
유연성 | 속성의 특정 동작을 정의 | 타입 간 호환성 제공 |
7. 주요 특징 요약
- 재사용성
- 공통된 로직을 캡슐화해 중복 코드 제거 및 유지보수 용이성을 높임.
- 캡슐화:
- 값을 읽고 쓸 때 발생하는 로직을 숨겨 속성 사용을 단순화.
- SwiftUI 통합:
- SwiftUI의 상태 관리와 자연스럽게 통합되어 UI 업데이트를 자동화.
8. 결론
PropertyWrapper는 속성 관리와 동작을 추상화하여 효율적이고 깔끔한 코드를 작성할 수 있게 함.
캡슐화, 재사용성, 그리고 SwiftUI와의 통합으로 인해 Swift 개발에 있어 필수적인 도구가 되고 있음.
'Swift & SwiftUI' 카테고리의 다른 글
[Swift & SwiftUI] 버전 대응 Wrapper (0) | 2024.05.19 |
---|---|
[Swift & SwiftUI] OpaqueType (0) | 2024.04.19 |