{"id":24995625,"url":"https://github.com/peterfriese/swift-environmentobject-demo","last_synced_at":"2025-04-12T04:11:13.080Z","repository":{"id":43106803,"uuid":"202415147","full_name":"peterfriese/Swift-EnvironmentObject-Demo","owner":"peterfriese","description":"A quick demo to show that @EnvironmentObjects do not propagate to modals.","archived":false,"fork":false,"pushed_at":"2019-08-15T05:37:35.000Z","size":775,"stargazers_count":35,"open_issues_count":0,"forks_count":7,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-25T23:41:46.211Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/peterfriese.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-08-14T19:42:17.000Z","updated_at":"2024-06-22T12:59:33.000Z","dependencies_parsed_at":"2022-09-09T16:40:28.956Z","dependency_job_id":null,"html_url":"https://github.com/peterfriese/Swift-EnvironmentObject-Demo","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterfriese%2FSwift-EnvironmentObject-Demo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterfriese%2FSwift-EnvironmentObject-Demo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterfriese%2FSwift-EnvironmentObject-Demo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterfriese%2FSwift-EnvironmentObject-Demo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/peterfriese","download_url":"https://codeload.github.com/peterfriese/Swift-EnvironmentObject-Demo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248514221,"owners_count":21116903,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-02-04T15:39:19.602Z","updated_at":"2025-04-12T04:11:13.053Z","avatar_url":"https://github.com/peterfriese.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SwiftUI @EnvironmentObject Demo\n\nAccording to the SwiftUI documentation, `@EnvironmentObject` is _a dynamic view property that uses a bindable object supplied by an ancestor view to invalidate the current view whenever the bindable object changes_ ([source](https://developer.apple.com/documentation/swiftui/environmentobject)).\n\nEssentially, you can use it to manage access to global state. [Hacking with Swift has a [great article](https://www.hackingwithswift.com/quick-start/swiftui/whats-the-difference-between-objectbinding-state-and-environmentobject) that explains the differences between `@State`, `@ObservableObject`, and `@EnvironmentObject`.\n\nParticularly, it says _There’s a third type of property available to use, which is `@EnvironmentObject`. This 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._\n\nIt turns out, however, that while values put into the environment nicely percolate down the view hierarchy, this is not the case for modals.\n\nLet's assume the following code:\n\n```swift\nclass AppState: ObservableObject {\n  @Published var counter = 0\n}\n\nstruct ContentView: View {\n  @EnvironmentObject var state: AppState\n  @State var presentDetailsView = false\n  @State var presentDetailsViewNoEnvironment = false\n  \n  var body: some View {\n    NavigationView {\n      VStack {\n        Button(action: { self.state.counter += 1 }) {\n          Text(\"Current value: \\(state.counter)\")\n        }\n\n        // 1\n\n        Button(action: { self.presentDetailsView = true }) {\n          Text(\"Present details\")\n        }\n        Button(action: { self.presentDetailsViewNoEnvironment = true }) {\n          Text(\"Present details w/o Environment (will crash)\")\n        }\n      }\n      .navigationBarTitle(\"@EnvironmentObject: Master\")\n      // 2\n    }\n  }\n}\n\nstruct DetailsView: View {\n  @EnvironmentObject var state: AppState\n  \n  var body: some View {\n    Text(\"The value is \\(state.counter)\")\n  }\n}\n```\n\nIf you now insert the following code at location (1), you will see that you can navigate from the master view to the details view as expected, and the details view can access the `counter` state via the global environment:\n\n```swift\n        NavigationLink(destination: DetailsView()) {\n          Text(\"Navigate to details\")\n        }\n```\n\nHowever, adding the following code at location (2) will result in a crash:\n\n```swift\n      .sheet(isPresented: $presentDetailsViewNoEnvironment) {\n        DetailsView()\n      }\n```\n\n```console\nFatal error: No observable object of type AppState.Type found.\nA View.environmentObject(_:) for AppState.Type may be missing as an ancestor of this view.: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/Monoceros_Sim/Monoceros-21.1.2/Core/EnvironmentObject.swift, line 171\n```\n\nInstead, in order to present the details view as a modal, you have to pass the `state` on, like so:\n\n```swift\n      .sheet(isPresented: $presentDetailsView) {\n        DetailsView().environmentObject(self.state)\n      }\n```\n\nLooking at the view hierarchy in the view debugger, we can see that thethis actually makes sense, as a modal screen is added to the view hierarchy **in parallel** to the original `ContentView`.\n\nWhen navigating from `ContentView` to `DetailsView` using a `NavigationLink`, `DetailsView` becomes a child of `ContentView`:\n\n![View hierarchy when using NavigationLink](navigate.png)\n\nWhen displaying a modal sheet using `sheet(isPresented:content:)`, however, `DetailsView` will not become a child of `ContentView`. Instead, the hosting view will be instantiated in parallel to the originating view.\n\n![View hierarchy when using sheet(isPresented:content:)](modal.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpeterfriese%2Fswift-environmentobject-demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpeterfriese%2Fswift-environmentobject-demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpeterfriese%2Fswift-environmentobject-demo/lists"}