Swift, SwiftUI로 코드를 작성하다보면
프로퍼티 앞에 붙여주는 아래와 같은 친구들(?)을 마주하게 됩니다.
@main, @Environment, @State, @Binding, @Published, @ObservedObject, @ViewBuilder, @escaping 등..
@가 앞에 붙어 있는 이 친구들은 Property Wrapper라고 부릅니다.
그 동안은 그냥 각각의 용도에 맞춰서 프로퍼티 앞에 붙여서 사용하고 있었는데,
(1)에서는 이 Property Wrapper가 무엇인지 알아보고,
(2)(다음글)에서는 자주 마주하는 Property Wrapper들의 용도도 정리해보려 합니다.
swift.org의 property 문서를 보면 Property Wrapper의 설명이 아래와 같이 되어 있습니다.
(https://docs.swift.org/swift-book/LanguageGuide/Properties.html)
A property wrapper adds a layer of separation between code that manages how a property is stored and the code that defines a property. ...(중략)... When you use a property wrapper, you write the management code once when you define the wrapper, and then reuse that management code by applying it to multiple properties.
property가 저장되는 방법을 관리하는 코드와 property를 정의하는 코드 사이에 구분 레이어를 추가해준다.
Property Wrapper를 사용할 땐, wrapper를 정의할 때 관리코드를 한번 작성하고
여러 property들에 적용하여 해당 관리코드를 재사용한다.
...라고 합니다.
핵심은 '관리코드를 한번 작성'하고, '해당 관리코드를 재사용한다'인 거 같습니다.
문서 바로 아래에 예제가 있는데,
필요한 Property Wrapper를 만들어서 사용할 수 있다라는 걸 알았습니다.
Property Wrapper를 정의해주려면
아래와 같이 @propertyWrapper를 구조체, 열거형, 클래스 앞에 선언해주고,
wrappedValue 계산프로퍼티를 정의해주면 됩니다.
문서의 예제를 참고로 해서 응용하여,
서버로부터 SnakeCase 문자열을 받았을 때, CamelCase의 문자열로 저장해주는
@CamelFromSnake 라는 Property Wrapper를 작성해봤습니다.
@propertyWrapper
struct CamelFromSnake {
private var string = ""
var wrappedValue: String {
get { return string }
set {
let seperated = newValue.components(separatedBy: "_")
string = (seperated.first?.lowercased() ?? "") + seperated.dropFirst()
.map { $0.prefix(1).uppercased() + $0.dropFirst() }
.reduce("") { $0 + $1 }
}
}
init(wrappedValue initialValue: String) {
self.wrappedValue = initialValue
}
}
위처럼 작성한 Property Wrapper는
(다른 Property Wrapper를 사용할 때과 똑같이)아래처럼 사용을 해주면 됩니다.
class SomeClass {
@CamelFromSnake var someProperty: String = ""
}
var someInstance = SomeClass()
someInstance.someProperty = "from_server_property"
print("someProperty: \(someInstance.someProperty)")
그럼 아래처럼 출력이 되어 CamelCase로 값이 저장이 된 것을 확인할 수 있습니다.
someProperty: fromServerProperty
Property Wrapper는 스스로 property를 가질 수 있습니다.
아래는 @UserDefaults라는 Property Wrapper로
저장할 key값과 default value를 지정한 @UserDefaults를 작성한 예시입니다.
@propertyWrapper
struct UserDefault<T> {
let key: String
let defaultValue: T
var wrappedValue: T {
get {
return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
UserDefaults.standard.synchronize()
}
}
}
이제 @UserDefault를 property 앞에 붙여주면 해당 값에서 값을 get, set할 때마다 UserDefaults를 통해 값을 가져오고, 저장할 수 있게 됩니다.
// 사용 예시
struct SomeStruct {
@UserDefault(key: "SomeKey", defaultValue: "")
static var somProperty: String
}
Property Wrapper를 활용하면,
관리가 필요한 프로퍼티에 대한 정의를 하고, 이를 간단하게 선언함으로써 반복되는 코드도 많이 줄일 수 있어 보입니다.
Swift-evolution의 Property Wrapper를 보면 이를 활용한 다양한 예제들을 확인해 볼 수가 있습니다.
Swift, SwiftUI로 코드를 작성하다보면
프로퍼티 앞에 붙여주는 아래와 같은 친구들(?)을 마주하게 됩니다.
@main, @Environment, @State, @Binding, @Published, @ObservedObject, @ViewBuilder, @escaping 등..
@가 앞에 붙어 있는 이 친구들은 Property Wrapper라고 부릅니다.
그 동안은 그냥 각각의 용도에 맞춰서 프로퍼티 앞에 붙여서 사용하고 있었는데,
(1)에서는 이 Property Wrapper가 무엇인지 알아보고,
(2)(다음글)에서는 자주 마주하는 Property Wrapper들의 용도도 정리해보려 합니다.
swift.org의 property 문서를 보면 Property Wrapper의 설명이 아래와 같이 되어 있습니다.
(https://docs.swift.org/swift-book/LanguageGuide/Properties.html)
A property wrapper adds a layer of separation between code that manages how a property is stored and the code that defines a property. ...(중략)... When you use a property wrapper, you write the management code once when you define the wrapper, and then reuse that management code by applying it to multiple properties.
property가 저장되는 방법을 관리하는 코드와 property를 정의하는 코드 사이에 구분 레이어를 추가해준다.
Property Wrapper를 사용할 땐, wrapper를 정의할 때 관리코드를 한번 작성하고
여러 property들에 적용하여 해당 관리코드를 재사용한다.
...라고 합니다.
핵심은 '관리코드를 한번 작성'하고, '해당 관리코드를 재사용한다'인 거 같습니다.
문서 바로 아래에 예제가 있는데,
필요한 Property Wrapper를 만들어서 사용할 수 있다라는 걸 알았습니다.
Property Wrapper를 정의해주려면
아래와 같이 @propertyWrapper를 구조체, 열거형, 클래스 앞에 선언해주고,
wrappedValue 계산프로퍼티를 정의해주면 됩니다.
문서의 예제를 참고로 해서 응용하여,
서버로부터 SnakeCase 문자열을 받았을 때, CamelCase의 문자열로 저장해주는
@CamelFromSnake 라는 Property Wrapper를 작성해봤습니다.
@propertyWrapper
struct CamelFromSnake {
private var string = ""
var wrappedValue: String {
get { return string }
set {
let seperated = newValue.components(separatedBy: "_")
string = (seperated.first?.lowercased() ?? "") + seperated.dropFirst()
.map { $0.prefix(1).uppercased() + $0.dropFirst() }
.reduce("") { $0 + $1 }
}
}
init(wrappedValue initialValue: String) {
self.wrappedValue = initialValue
}
}
위처럼 작성한 Property Wrapper는
(다른 Property Wrapper를 사용할 때과 똑같이)아래처럼 사용을 해주면 됩니다.
class SomeClass {
@CamelFromSnake var someProperty: String = ""
}
var someInstance = SomeClass()
someInstance.someProperty = "from_server_property"
print("someProperty: \(someInstance.someProperty)")
그럼 아래처럼 출력이 되어 CamelCase로 값이 저장이 된 것을 확인할 수 있습니다.
someProperty: fromServerProperty
Property Wrapper는 스스로 property를 가질 수 있습니다.
아래는 @UserDefaults라는 Property Wrapper로
저장할 key값과 default value를 지정한 @UserDefaults를 작성한 예시입니다.
@propertyWrapper
struct UserDefault<T> {
let key: String
let defaultValue: T
var wrappedValue: T {
get {
return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
UserDefaults.standard.synchronize()
}
}
}
이제 @UserDefault를 property 앞에 붙여주면 해당 값에서 값을 get, set할 때마다 UserDefaults를 통해 값을 가져오고, 저장할 수 있게 됩니다.
// 사용 예시
struct SomeStruct {
@UserDefault(key: "SomeKey", defaultValue: "")
static var somProperty: String
}
Property Wrapper를 활용하면,
관리가 필요한 프로퍼티에 대한 정의를 하고, 이를 간단하게 선언함으로써 반복되는 코드도 많이 줄일 수 있어 보입니다.
Swift-evolution의 Property Wrapper를 보면 이를 활용한 다양한 예제들을 확인해 볼 수가 있습니다.