As the name suggests its a wrapper over the property, so you can change the raw value before returning it. You can think of property wrapper as a regular property, which delegates its get
and set
to some other type. Property wrappers can be used with struct
, enum
and class
.
Property wrappers are a feature that was introduced in Swift 5.1 and they play a huge role in SwiftUI and Combine which are two frameworks that shipped alongside Swift 5.1 in iOS 13.
The idea of property wrappers, is that commonly used computed property getter and setter patterns can be encapsulated into a type with a wrappedValue
computed property:
@propertyWrapper struct MyWrapper { // ... var wrappedValue : SomeType { get { /*...*/ } set { /*...*/ } } }
You can then declare your computed property using the property wrapper type name as a custom attribute:
@MyWrapper var myProperty
The result is that, behind the scenes, a MyWrapper
instance is created for you, and when your code gets or sets the value of myProperty
, it is the getter or setter of this MyWrapper
instance that is called.
In real life, your property wrapper’s purpose will almost certainly be to act as a facade for access to a stored instance property, declared inside the property wrapper’s type.
To implement your own property wrapper, you need to do a few things:
struct
that will wrap the property.@propertyWrapper
before the struct
keyword.var wrappedValue
computed variable. You can use get
to get the value of the property, and set
to set it. With this, you can see that you can let a property wrapper store its value anywhere.To exemplify this, we will write a simple property wrapper that works with Strings and it changes them to uppercase letters.
@propertyWrapper struct Capitalized { private(set) var text: String = "" var wrappedValue: String { get { return text } set { text = newValue.uppercased() } } } struct User { @Capitalized var firstName: String @Capitalized var lastName: String } var user = User(firstName: "Thomas", lastName: "Anderson") user.lastName // prints ANDERSON
Another example
@propertyWrapper struct Rating { private var _rating: Int = 0 var wrappedValue: Int { get { return _rating } set (value) { if value > 5 { _rating = 5 } else if value < 1 { _rating = 1 } else { _rating = value } } } } struct MusicRating { @Rating var rating: Int init(rating: Int) { self.rating = rating print(rating) } } MusicRating(rating: 9)// prints 5 MusicRating(rating: 0)// prints 1
Styling UILabel
@propertyWrapper public class TitleLabel { public var wrappedValue: UILabel public init(text: String) { self.wrappedValue = UILabel() wrappedValue.text = text configureLabel() } private func configureLabel() { wrappedValue.textColor = .lightGray wrappedValue.font = .systemFont(ofSize: 28, weight: .regular) wrappedValue.sizeToFit() } } @TitleLabel(text: "The Matrix") var titleLabel: UILabel
UserDefaults
UserPreferences
is a property wrapper that helps to save and get values from UserDefaults.
@propertyWrapper struct UserDefault<T> { let key: String let defaultValue: T init(_ key: String, defaultValue: T) { self.key = key self.defaultValue = defaultValue } var wrappedValue: T { get { return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue } set { UserDefaults.standard.set(newValue, forKey: key) } } } struct UserData { @UserDefault("hasVoted", defaultValue: false) static var hasVoted: Bool } UserData.hasVoted = true print(UserData.hasSeenAppIntroduction) // prints false
Also you can extend UserDefault
and save custom object.