일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- OperationQueue
- Remot Push
- 진입점
- AppTransportSecurity
- UIButton
- 원격 푸시
- property wrapper
- DispatchQueue
- IBOutlet
- uikit
- CoreLocation
- SWiFT
- firebase
- for-in
- TableView
- RunLoop
- tableViewCell
- viewcontroller
- 후행 클로저
- IBOutletCollection
- Choosing Between Structures and Classes
- ios
- entrypoint
- 트레일링 클로저
- Understanding Swift Performance
- userdefaults
- weak
- WWDC16
- 동시성프로그래밍
- 연산 프로퍼티
- Today
- Total
iOS 공부하는 감자
Swift) Property Wrapper 본문
Property Wrapper란
Swift 5.1에서 추가된 기능으로, 해석 그대로 프로퍼티를 한번 감싸는 것을 의미한다.
Property Wrapper를 사용하면 프로퍼티가 저장되는 방식을 관리하는 코드와 프로퍼티를 정의하는 코드 사이에 분리 계층을 추가하여 프로퍼티를 관리할 수 있다.
만약 특정 문자열 프로퍼티의 값을 대문자로만 사용하고 싶을 때, Property Wrapper를 사용하지 않고
연산 프로퍼티로 구현하면 아래와 같이 코드를 작성할 수 있다.
struct Person {
private var _name: String
// name에 값이 할당되면 _name에 그대로 저장하고
// 값을 반환할 때는 _name에서 uppercased()메서드를 호출한 후 반환한다.
var name: String {
get { return self._name.uppercased() }
set { self._name = newValue }
}
init(name: String) {
self._name = name
}
}
var somePerson = Person(name: "abcd")
print(somePerson.name) // ABCD
somePerson.name = "qwqeweqwe"
print(somePerson.name) // QWQEWEQWE
위 코드를 Property Wrapper를 사용하여 적용하면 프로퍼티 자체에 연결할 수 있어서
보일러플레이트 코드와 코드 재사용성을 높혀준다.
프로퍼티를 가질 수 있는 타입(class, struct, enum)에 @Property Wrapper 키워드를 붙여서 구현하며, 프로퍼티가 해야할 행동을 정의하는 타입으로 사용할 수 있게 한다.
여기서 @Property Wrapper 키워드는 직접 사용하려는 타입에 붙이는게 아니라 (위 코드에서는 Person타입)
프로퍼티가 해야 할 행동을 정의하는 타입에 붙여준다.
예시를 보면 이해가 쉽다..
1. 프로퍼티가 해야 할 행동을(대문자화) 정의하는 타입 생성
@propertyWrapper
struct Uppercase {
private var value: String
// wrappedValue는 필수 구현사항
var wrappedValue: String {
get { return self.value.uppercased() }
set { self.value = newValue }
}
init(wrappedValue initialValue: String) {
self.value = initialValue
}
}
2. 실제 사용할 프로퍼티 앞에 "@Property Wrapper 타입 이름"을 붙여준다.
struct Person {
@Uppercase
var name: String
}
3. Property Wrapper를 적용시킨 프로퍼티를 사용해본다.
var somePerson = Person(name: "abcd")
print(somePerson.name)
somePerson.name = "qwer"
print(somePerson.name)
좀 정리하자면..
@Property Wrapper 키워드를 부착? 채택?한 타입에서 프로퍼티가 해야 할 행동을 정의해준다. (약간 확장 메서드처럼.. 생각해도 괜찮을 것 같기도 함..)
Property Wrapper를 붙여주면 wrappedValue라는 연산 프로퍼티를 내부에 필수로 구현해야 한다.
이렇게 프로퍼티가 해야 할 행동을 정의했다면, 이 행동을 수행할 프로퍼티에 해당 타입의 이름을 부착해준다.
부착만 잘(?) 해주면 별다른 추가 코드 없이, 직접 정의한 Property Wrapper부착 타입의 행동을 프로퍼티가 채택한다..
정리가 안된거같은디.. 머릿속에서는 이해가 좀 된거같은데 텍스트로 표현이 어렵다 ㅠ
UserDefault에 Property Wrapper 사용하기
WWDC 19에서도 Property Wrapper를 설명하기 위해 애플이 UserDefaults를 예시로 들었다고 한다.
뭔가 애플 공인 사용법(?) 이니까 잘 기억해두었다가 사용하면 좋을 것 같다.
위에서 작성한 Property Wrapper 사용과 동일한 순서로 진행한다.
1. 프로퍼티가 해야 할 행동을(대문자화) 정의하는 타입 생성
(프로퍼티가 해야 할 행동을 정의 -> 연산 프로퍼티로 UserDefaults 사용)
// 다양한 타입을 수용하기 위해 제네릭 문법 사용 <T>
@propertyWrapper
struct UserDefault<T> {
let key: String
let defaultValue: T // key값에 대응하는 value가 없다면 반환해줄 기본 데이터
var wrappedValue: T {
// key값에 대응하는 value를 타입캐스팅 후 반환
get { return UserDefaults.standard.object(forKey: self.key) as? T ?? defaultValue }
//
set { UserDefaults.standard.set(newValue, forKey: self.key) }
}
}
2. 실제 사용할 프로퍼티 앞에 "@Property Wrapper 타입 이름"을 붙여준다.
(UserDefaults에 저장하고 꺼내올 데이터)
class UserDefaultManager {
@UserDefault(key: "isSelected", defaultValue: false)
static var isSelected: Bool
@UserDefault(key: "name", defaultValue: "홍길동")
static var name: String
@UserDefault(key: "email", defaultValue: nil)
static var email: String?
}
3. Property Wrapper를 적용시킨 프로퍼티를 사용해본다.
프로퍼티에 값을 할당하면, 자동으로 UserDefaults에 저장하고,
프로퍼티를 통해 값을 조회하면, UserDefaults에서 해당 프로퍼티가 가지고 있는 key값을 바탕으로 value를 조회하여 반환한다.
// 1.
print(UserDefaultManager.isSelected) // false : 설정했던 기본값이 반환됨
UserDefaultManager.isSelected = true // 자동으로 해당 프로퍼티가 가진 key값을 사용하여 UserDefaults에 내가 할당한 값 저장
print(UserDefaultManager.isSelected) // true : UserDefaults에 잘 저장되었음을 확인
// 2.
print(UserDefaultManager.email) // nil : 설정했던 기본값 반환
UserDefaultManager.email = "abc@ab.com" // 자동으로 해당 프로퍼티가 가진 key값을 사용하여 UserDefaults에 내가 할당한 값 저장
print(UserDefaultManager.email) // Optional("abc@ab.com") : 잘 저장되었음을 확인
☘️ SeSac 2주차 과제 코드 수정
기념일 계산기의 Date를 UserDefaults에 저장하는 방식에 propertyWrapper를 적용해 보았다.
1. 타입 생성
@propertyWrapper
struct UserDefault<T> {
let key: String
let defaultValue: T
var wrappedValue: T {
get { UserDefaults.standard.object(forKey: self.key) as? T ?? defaultValue }
set { UserDefaults.standard.set(newValue, forKey: self.key) }
}
}
class UserDefaultManager {
static var shared = UserDefaultManager()
private init() {}
@UserDefault(key: "anniversaryDateKey", defaultValue: Date())
var anniversaryDateKey: Date
}
2. 데이터를 저장하는 코드
// 기존
UserDefaults.standard.set(dateString, forKey: AnniversaryKey.anniversary.rawValue)
// 변경 후
UserDefaultManager.shared.anniversaryDateKey = datePicker.date
3. 데이터를 불러오는 코드
// 기존
if let dateString = UserDefaults.standard.string(forKey: AnniversaryKey.anniversary.rawValue), let date = stringToDate(dateString) {
...
}
// 변경 후
let savedDate = UserDefaultManager.shared.anniversaryDateKey
'Swift' 카테고리의 다른 글
Swift) Choosing Between Structures and Classes (0) | 2023.01.30 |
---|---|
Operation, OperationQueue (feat. GCD) (0) | 2022.12.28 |
split() vs components() (0) | 2022.06.04 |
문자열 다루기 (0) | 2022.01.31 |