Swift @propertyWrapper 활용.

Jay Lee
2 min readFeb 18, 2024

--

Logging Property Access

@propertyWrapper
struct LogAccess<T> {
private var value: T
private let propertyName: String

init(wrappedValue: T, propertyName: String) {
self.value = wrappedValue
self.propertyName = propertyName
}

var wrappedValue: T {
get {
print("Accessing \(propertyName) with value: \(value)")
return value
}
set {
value = newValue
print("Setting \(propertyName) to \(value)")
}
}
}

struct Example {
@LogAccess(propertyName: "testProperty")
var testProperty: Int
}

Validating Property

@propertyWrapper
struct ValidateRange {
private var value: Int
private let range: ClosedRange<Int>

init(wrappedValue: Int, range: ClosedRange<Int>) {
self.range = range
self.value = wrappedValue.clamped(to: range)
}

var wrappedValue: Int {
get { value }
set { value = newValue.clamped(to: range) }
}
}

struct MySettings {
@ValidateRange(range: 1...10)
var volume: Int
}

Lazy Initialization

@propertyWrapper
struct Lazy<T> {
private var initializer: () -> T
private var _value: T?

init(wrappedValue: @autoclosure @escaping () -> T) {
self.initializer = wrappedValue
}

var wrappedValue: T {
mutating get {
if let value = _value {
return value
} else {
let value = initializer()
_value = value
return value
}
}
}
}

struct MyStruct {
@Lazy var expensiveObject: ExpensiveClass = ExpensiveClass()
}

Thread-Safe Access

@propertyWrapper
struct ThreadSafe<T> {
private var value: T
private let queue = DispatchQueue(label: "ThreadSafeQueue", attributes: .concurrent)

init(wrappedValue: T) {
self.value = wrappedValue
}

var wrappedValue: T {
get {
return queue.sync { value }
}
set {
queue.async(flags: .barrier) { self.value = newValue }
}
}
}

struct SafeData {
@ThreadSafe var data: Int
}

Observing Property Changes

@propertyWrapper
struct DidSet<T> {
private var value: T
private let callback: (T) -> Void

init(wrappedValue: T, _ callback: @escaping (T) -> Void) {
self.value = wrappedValue
self.callback = callback
}

var wrappedValue: T {
get { value }
set {
value = newValue
callback(newValue)
}
}
}

class MyClass {
@DidSet({ print("Value changed to \($0)") }) var myProperty: Int
}

UserDefaults

@propertyWrapper
struct UserDefaultsBacked<Value> {
let key: String
var wrappedValue: Value? {
get { UserDefaults.standard.object(forKey: key) as? Value }
set { UserDefaults.standard.set(newValue, forKey: key) }
}
}

struct Settings {
@UserDefaultsBacked(key: "autoRefresh")
var autoRefresh: Bool?
}

--

--