Passing Data to SwiftUI Views
A common question I see from people learning SwiftUI is how to pass data from one view to another. There are three ways to pass data in SwiftUI apps.
- Use @State and @Binding property wrappers
- Use @StateObject and @ObservedObject property wrappers
- Use @EnvironmentObject property wrapper
You’re going to use the first two ways more than you’ll use @EnvironmentObject.
Use @State and @Binding for Structs
If you use structs for your data, you must use @State and @Binding to pass data. Use the @State property wrapper in the master view and the @Binding property wrapper in any view where you want to pass the data.
Suppose you have a wiki app. The wiki consists of a list of pages. There are two views: the master view has a list of pages, and the detail view shows the contents of the selected page.
The master view needs to store the selected page.
@State private var selectedPage: Page = Page()
The detail view needs access to the selected page.
@Binding var page: Page
The following line of code demonstrates how to pass the selected page from the master view to the detail view:
NavigationLink(destination: PageView(page: $selectedPage))
When using bindings, you must put the $
character before the name of the @State variable.
The master and detail view point to the same page. Any changes you make in the detail view will appear in both views.
I have a demo project on GitHub that provides a full example on using @State and @Binding.
Use @StateObject and @ObservedObject for Classes
Use the @StateObject property to pass a class instance to other views. Using @StateObject has the following requirements:
- The class must conform to the
ObservableObject
protocol. - Any properties you want automatically updated must use the @Published property wrapper.
- The SwiftUI views must use the @StateObject and @ObservedObject property wrappers.
Suppose you have a Project
class that has a list of issues.
class Project: ObservableObject {
@Published var issues: [Issue]
}
By making the class conform to ObservableObject
and using @Published for the issues
array, you can use @StateObject in the view that owns the project.
@StateObject var project: Project
Pass the project
variable to any view that needs to access the project. The other views should use @ObservedObject so that all the views are referring to the same project.
@ObservedObject var project: Project
Remember that @StateObject is the class equivalent of @State and @ObservedObject is the class equivalent of @Binding.
I have an issue tracking project on GitHub that demonstrates passing data to views using @StateObject.
Use @EnvironmentObject for Data You Want Any View to Access
Create an environment object when you have a piece of data you want any SwiftUI view to access.
A common situation where you would use environment objects is an app that uses Core Data. When working with Core Data, your app needs to access the managed object context to add, update, and delete entities. Apple provides an environment object for the managed object context so you don’t have to create one. Use the @Environment property wrapper to access the managed object context.
@Environment(\.managedObjectContext) private var context
If you need to create your own environment object, you must create a class that conforms to ObservableObject
.
In the SwiftUI view, use the @EnvironmentObject property wrapper to access your environment object.
@EnvironmentObject var project: Project
Use the .environmentObject
modifier to pass the environment object to another view.
DetailView()
.environmentObject(project)