3. Property wrappers in SwiftUI
• @Stat
e
• @Bindin
g
• @ObservedObjec
t
• @Publishe
d
• @EnvironmentObjec
t
• Etc
4. What is property wrappers?
A property wrapper adds a layer of separation between code that manages how a
property is stored and the code that defines a property. For example, if you have
properties that provide thread-safety checks or store their underlying data in a database,
you have to write that code on every 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
.
https://docs.swift.org/swift-book/LanguageGuide/Properties.html
5. Example
import Foundation
struct AppSettings {
// Normal way
static shared = AppSettings()
var appLanguage: String {
get { UserDefaults.standard.value(forKey: "app_language") as? String ?? "" }
set { UserDefaults.standard.set(newValue, forKey: “app_language") }
}
var isLoggedIn: String {
get { UserDefaults.standard.value(forKey: "is_logged_in") as? Bool ?? false }
set { UserDefaults.standard.set(newValue, forKey: "is_logged_in") }
}
// Property wrapper
@PropertyWrapperUserDefault(key: "app_language", defaultValue: "") static var _appLanguage: String
@PropertyWrapperUserDefault(key: "is_logged_in", defaultValue: false) static var _isLoggedIn: Bool
}
// How to access
AppSettings.shared.appLanguage = "en"
AppSettings._appLanguage = “en"
6. How to create
import Foundation
@propertyWrapper
struct PropertyWrapperUserDefault<T> {
private var key : String
private var defaultValue: T
init(key: String, defaultValue: T) {
self.key = key
self.defaultValue = defaultValue
}
var wrappedValue: T {
get { UserDefaults.standard.value(forKey: key) as? T ?? defaultValue }
set { UserDefaults.standard.set(newValue, forKey: key) }
}
}
7. Example 2
Validate Phone number property wrappe
r
`ProjectedValue
`
Condition
s
- Phone number should have 10 character
s
- We can check it is true or false by access projectedValue
8. Example 2
import Foundation
@propertyWrapper struct PhoneNumberValidation {
var projectedValue: Bool = false
var storedValue: String = ""
init(wrappedValue: String) {
self.wrappedValue = wrappedValue
}
var wrappedValue: String {
get { storedValue }
set {
projectedValue = newValue.count == 10 ? true : false
storedValue = newValue.count < 10 ? newValue : String(newValue.prefix(10))
}
}
}
9. Example 2
struct Phone {
@PhoneNumberValidation var phoneNumber: String
func printPhoneNumber() {
print(phoneNumber)
print($phoneNumber)
}
}
let phone = Phone(phoneNumber: “1234”)
phone.printPhoneNumber()
// Result
1234
false
10. @State
A property wrapper type that can read and write a value managed by SwiftUI
.
SwiftUI manages the storage of any property you declare as a state. When the state
value changes, the view invalidates its appearance and recomputes the body. Use the
state as the single source of truth for a given vie
w
https://developer.apple.com/documentation/swiftui/state
11. @State
struct ContentView: View {
@State private var counter: Int = 0
var body: some View {
VStack {
Text("Counter: (counter)")
Button("Hit me", action: {
counter += 1
})
}.padding(16)
}
}
16. ProjectedValue of @State
/// A binding to the state value.
/// Use the projected value to pass a binding value down a view hierarchy.
/// To get the `projectedValue`, prefix the property variable with `$`
public var projectedValue: Binding<Value> { get }
17. @Binding
A property wrapper type that can read and write a value owned by a source of truth
.
Use a binding to create a two-way connection between a property that stores data, and
a view that displays and changes the data. A binding connects a property to a source of
truth stored elsewher
e
https://developer.apple.com/documentation/swiftui/binding
18. @Binding
struct ContentView: View {
@State private var randomNumber: Int = 0
var body: some View {
Text("1st horse number is: (randomNumber)")
CustomView(randomNumber: $randomNumber)
}
}
struct CustomView: View {
@Binding var randomNumber: Int
var body: some View {
Button("Hit me") {
self.randomNumber = Int.random(in: 1...100)
}
}
}
19. @Published
A type that publishes a property marked with an attribute
.
Publishing a property with the @Published attribute creates a publisher of this type. You
access the publisher with the $ operato
r
https://developer.apple.com/documentation/combine/published
21. @ObservedObject
A property wrapper type that subscribes to an observable object and invalidates a view
whenever the observable object changes
.
https://developer.apple.com/documentation/swiftui/observedobject
22. @ObservedObject
struct ContentView: View {
@ObservedObject var someObservable = SomeObservable()
var body: some View {
Text("Hello, (someObservable.userName)!”)
.padding()
}
}
23. @Published @ObservedObject
struct ContentView: View {
@ObservedObject var someObservable = SomeObservable()
var body: some View {
Text("Hello, (someObservable.userName)!")
.padding()
}
}
class SomeObservable: ObservableObject {
@Published var userName: String = ""
init() {
userName = “Ton"
}
}
24. @EnvironmentObject
A property wrapper type for an observable object supplied by a parent or ancestor view
.
An environment object invalidates the current view whenever the observable object
changes. If you declare a property as an environment object, be sure to set a
corresponding model object on an ancestor view by calling its environmentObject(_:)
modifier
.
https://developer.apple.com/documentation/swiftui/environmentobject
25. @EnvironmentObject
class UserSettings: ObservableObject {
@Published var userName = "Ton"
}
@main
struct EnvironmentObjectApp: App {
let userSettings = UserSettings()
var body: some Scene {
WindowGroup {
ContentView().environmentObject(userSettings)
}
}
}
struct ContentView: View {
@EnvironmentObject var userSettings: UserSettings
var body: some View {
Text("Hello, (userSettings.userName)")
.padding()
}
}
27. Let’s create a Project
MVVM with all property wrappers that I’ve talked about
.
- Display data from ViewModel to View (@Published, @ObservedObject
)
- Pass data between views (@Binding
)
- Update our view (@State
)
- Share data in our app (@EnvironmentObject)