Understanding Property Wrappers

Published by @SoNiceInfo at 6/24/2020


Property Wrappers monitor the state of your variables in SwiftUI.
Property Wrappers allow you to share a variable between multiple Views and redraw the View when the variable is updated.
The SwiftUI comes with the following types of Property Wrappers

Property WrappersRoleUses(example)
@StateMonitoring the state of a View and the value of a variable.Monitoring Sheet (modal) status.
@BindingReferencing and modifying another View's @State variable.Passing variables to a Sheet (modal).
@ObservedObjectMonitoring for structure/class changes.Handling classes with multiple properties, such as user information.
@EnvironmentObtaining predefined environment information about the View.Dealing with the size of the screen.
@EnvironmentObjectMonitoring for struct/class changes.Handling a class with multiple properties.

@State

@State can hold a single value.
@State should be used only within a single view.
In the example, it's used to hold the sheet (modal) transition state in the View.
I'm adding $ (the binding of isPresented) to pass.

//
//  ContentView.swift
//

import SwiftUI

struct ContentView: View {
    @State private var isPresented: Bool = false
    @State private var catName: String = "Tora"
    
    var body: some View {
        VStack {
            Text(catName)
            Button("Show Result") {
                self.isPresented.toggle()
            }
        }
        .sheet(isPresented: $isPresented) {
            VStack {
                TextField("My cat's name is...", text: self.$catName)
                    .multilineTextAlignment(.center)
                Button("Close Result") {
                    self.isPresented.toggle()
                }
            }
        }
    }
}
    
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

@Binding

You can use @Binding to inherit another View's @State variable.
If you change a variable that you have made @Binding, other Views will be notified of the change.

//
//  ContentView.swift
//

import SwiftUI

struct SheetView: View {
    @Binding var isPresented: Bool
    @Binding var catName: String
    
    var body: some View {
        VStack {
            TextField("My cat's name is...", text: $catName)
                .multilineTextAlignment(.center)
            Button("Close Result") {
                self.isPresented.toggle()
            }
        }
    }
}

struct ContentView: View {
    @State private var isPresented: Bool = false
    @State private var catName: String = "Tora"
    
    var body: some View {
        VStack {
            Text(catName)
            Button("Show Result") {
                self.isPresented.toggle()
            }
        }
        .sheet(isPresented: $isPresented) {
            SheetView(isPresented: self.$isPresented, catName: self.$catName)
        }
    }
}
    
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

@ObservedObject

@ObservedObject is useful for monitoring the value of a structure or class.
Multiple instances can be created with ObservableObject protocol-compliant classes.
With @Published variables, it's changes are notified to the multiple views.

//
//  ContentView.swift
//

import SwiftUI

class CatModel: ObservableObject {
    @Published var name : String
    @Published var age : String
    
    init (name: String, age: String) {
        self.name = name
        self.age = age
    }
}

struct ResultView: View {
    @Binding var isPresented: Bool
    @ObservedObject var cat: CatModel

    var body: some View {
        VStack {
            Text("My cat is: \(cat.name), \(cat.age)")
            
            Button("Close Result") {
                self.isPresented.toggle()
            }
        }
    }
}

struct ContentView: View {
    @State private var isPresented: Bool = false
    @ObservedObject var cat = CatModel(name: "Mike", age: "3")
    
    var body: some View {
        VStack {
            HStack {
                Text("Name: ")
                TextField("Input name", text: $cat.name)
            }
            HStack {
                Text("Age: ")
                TextField("Input age", text: $cat.age)
            }
            Button("Show Result") {
                self.isPresented.toggle()
            }
        }
        .sheet(isPresented: $isPresented) {
            ResultView(isPresented: self.$isPresented, cat: self.cat)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
@ObservedObjectを使った例

@Environment

@Environment can retrieve environment information such as font, dark mode and light mode.
Please see what you can retrive here EnvironmentValues - SwiftUI | Apple Developer Documentation.

//
//  ContentView.swift
//

import SwiftUI

struct ContentView: View {
    @Environment(\.font) var font
    @Environment(\.colorScheme) var colorScheme
    
    var body: some View {
        VStack {
            Text("Text")
                .font(font)
        }
    }
}
    
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        HStack {
            ContentView()
                .environment(\.font, .body)
            ContentView()
                .environment(\.font, .largeTitle)
        }
    }
}
@Environmentを使う画像

@EnvironmentObject

@EnvironmentObject can hold multiple values in it.
@EnvironmentObject is available if you want to use it on all views.
While @ObservedObject can create multiple instances, @EnvironmentObject can create a single instance.

iOS 13 Support

//
//  SceneDelegate.swift
//

import UIKit
import SwiftUI

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Create the SwiftUI view that provides the window contents.
        // 大事!!
        let contentView = ContentView().environmentObject(CatModel())

        ...
    }
}

iOS 14 Support

import SwiftUI

@main
struct iOS14App: App {
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(CatModel())
        }
    }
}

Common

//
//  ContentView.swift
//

import SwiftUI

class CatModel: ObservableObject {
    @Published var name = ""
    @Published var age = ""
}

struct ContentView: View {
    @State var isPresented: Bool = false
    @EnvironmentObject var cat : CatModel
    
    var body: some View {
        VStack {
            HStack {
                Text("Name: ")
                TextField("Input name", text: $cat.name)
            }
            HStack {
                Text("Age: ")
                TextField("Input age", text: $cat.age)
            }
            Button("Show Result") {
                self.isPresented.toggle()
            }
        }
        .sheet(isPresented: $isPresented) {
            ResultView(isPresented: self.$isPresented)
                .environmentObject(self.cat)
        }
    }
}

struct ResultView: View {
    @Binding var isPresented: Bool
    @EnvironmentObject var cat : CatModel

    var body: some View {
        VStack {
            Text("My cat is: \(cat.name), \(cat.age)")
            
            Button("Close Result") {
                self.isPresented.toggle()
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
@EnvironmentObjectを使う画像

References

What’s the difference between @ObservedObject, @State, and @EnvironmentObject? - a free SwiftUI by Example tutorial

    I released iOS App!

    ToDo App

    Visualize Activity, Data sharing with iCloud, Dark mode supported.

    リリースしたToDoアプリのスクリーンショット

    IP Address bookmark.

    Check and bookamrk IP address of all interfaces with geolocation.

    リリースしたIPアドレス保存アプリのスクリーンショット