Property Wrapper In SwiftUI

Introduction

 
Property wrappers in SwiftUI provide us with variables with a special logic depending on the type of property wrapper. The main goal here is wrapping properties with logic which can be extracted into the separated struct to reuse it across the codebase. 
 

@State

 
State property remains persistent in memory for as long as the view exists. When that state changes, SwiftUI knows to automatically reload the view with the latest changes so it can reflect the latest changes.
 
@state belongs to the specific view and never gets used outside that view, so it's important to make those properties  private to reinforce that idea.
 
You can easily declare a state by putting the @State keyboard in front of a variable.
 
Example
  1. //  
  2. //  ContentView.swift  
  3. //  Demo Property Wrapper  
  4. //  
  5. //  Created by Pravesh Dubey on 04/03/20.  
  6. //  Copyright © 2020 Pravesh Dubey. All rights reserved.  
  7. //  

  8. import SwiftUI  
  9.   
  10. struct ContentView: View {  
  11.     @State private var age = 31  
  12.     var body: some View {  
  13.         VStack{  
  14.             Text("\(age)")  
  15.             Button(action: {self.age += 1}){  
  16.                  Text("Tap me!")  
  17.             }  
  18.         }  
  19.     }  
  20. }  
  21. struct ContentView_Previews: PreviewProvider {  
  22.     static var previews: some View {  
  23.         ContentView()  
  24.     }  
  25. }  

@Binding 

 
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 elsewhere, instead of storing data directly. @binding provides access by reference for a value type.
 
Example
  1. //  
  2. //  PlayerView.swift  
  3. //  Demo Property Wrapper  
  4. //  
  5. //  Created by Pravesh Dubey on 05/03/20.  
  6. //  Copyright © 2020 Pravesh Dubey. All rights reserved.  
  7. //  
  8.   
  9. import SwiftUI  
  10.   
  11. struct PlayerView: View {  
  12.         @State private var isPlaying: Bool = false  
  13.         var body: some View {  
  14.             VStack {  
  15.                 PlayButton(isPlaying: $isPlaying)  
  16.             }  
  17.         }  
  18. }  
  19. struct PlayerView_Previews: PreviewProvider {  
  20.     static var previews: some View {  
  21.         PlayerView()  
  22.     }  
  23. }  
  24. struct PlayButton: View {  
  25.     @Binding var isPlaying: Bool  
  26.     var body: some View {  
  27.         Button(action: {  
  28.             self.isPlaying.toggle()  
  29.         }) {  
  30.             Image(systemName: isPlaying ? "pause.circle" : "play.circle")  
  31.         }  
  32.     }  
  33. }  
The above uses a special character, $ , to pass anchor link, because without  the $ symbol Swift will pass a copy of the value instead of the anchor link itself.
 

@ObservedObject

 
A property wrapper type that subscribes to an observable object and invalidates a view whenever the observable object changes. ObservedObject works similarly to State property but the main difference is that we can divide it among several independent views that can subscribe and watch for changes to this object. As soon as changes appear SwiftUI rebuilds all Views bound to this Object.
 
Whatever type you use with @ObservedObject should conform to ObserableObject Protocol. When you create properties on observable objects you get to decide whether changes to each property should force the view to refresh or not. 
 
There are multiple ways for an observed object to notify views that important data has changed, but the easiest is using @Published property wrapper. ObservableObject is capable of the following things,
  • ObservableObjects are wrapping classes not variables.
  • These classes can contain data, for example, a string assigned to a variable. 
  • We can bind one or multiple views of the ObservableObject.
  • The observing view can access and manipulate the data inside the ObservableObject. When a change happens to the Observableobject's data all observing views get automatically rendered, similar to when the state changes. 
Example
  1. //  
  2. //  UserSettingsView.swift  
  3. //  Demo Property Wrapper  
  4. //  
  5. //  Created by Pravesh Dubey on 05/03/20.  
  6. //  Copyright © 2020 Pravesh Dubey. All rights reserved.  
  7. //  
  8.   
  9. import SwiftUI  
  10.   
  11. struct UserSettingsView: View {  
  12.     @ObservedObject var settings = UserSettings()  
  13.     var body: some View {  
  14.         VStack {  
  15.             Text("Your score is \(settings.score)").padding(20)  
  16.             Button(action: {  
  17.                 self.settings.score += 1  
  18.             }) {  
  19.                 Text("Increase Score")  
  20.             }  
  21.         }  
  22.     }  
  23. }  
  24.   
  25. struct UserSettingsView_Previews: PreviewProvider {  
  26.     static var previews: some View {  
  27.         UserSettingsView()  
  28.     }  
  29. }  
  30. class UserSettings: ObservableObject {  
  31.     @Published var score = 0  
  32. }  

@EnvironmentObject

 
An environmentObject is a property wrapper for a data model which once initialized, can share data to all views of your app. The environmentObject is created by supplying an ObservableObject.
 
@EnvironmentObject is a value that is made available to your views through the application itself. It's shared data that every view can read if they want to. 
 
Example
  1. struct ContentView: View {  
  2.     @EnvironmentObject var settings: UserSettings  
  3.   
  4.     var body: some View {  
  5.         NavigationView {  
  6.             VStack {  
  7.                 // A button that writes to the environment settings  
  8.                 Button(action: {  
  9.                     self.settings.score += 1  
  10.                 }) {  
  11.                     Text("Increase Score")  
  12.                 }  
  13.   
  14.                 NavigationLink(destination: DetailView()) {  
  15.                     Text("Show Detail View")  
  16.                 }  
  17.             }  
  18.         }  
  19.     }  
  20. }  
  21. struct DetailView: View {  
  22.     @EnvironmentObject var settings: UserSettings  
  23.   
  24.     var body: some View {  
  25.         // A text view that reads from the environment settings  
  26.         Text("Score: \(settings.score)")  
  27.     }  
  28. }  

Common differences 

  • Use @State for simple properties that belong to a single view. They should usually be marked private.
  • Use @ObservedObject for complex properties that might belong to several views. Any time you're using a reference type you should be using @ObservedObject for it. 
  • Use @EnvironmentObject for properties that were created elsewhere in the app, such as shared data.