Created
February 19, 2021 05:17
-
-
Save TheWorstProgrammerEver/c97a3134f44c4694c51bc6d1eb0e816a to your computer and use it in GitHub Desktop.
SwiftUI Sheet NavigationView NavigationLink EnvironmentObject Environment presentationMode bug.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Found a weird AF SwiftUI bug today. | |
It's hard to articulate succinctly. I have a demo project that replicates it. | |
Basically if you: | |
- Have a modally presented (via sheet) view which contains a NavigationView and a NavigationLink which leads to a destination view which has an EnvironmentObject dependency AS WELL AS a dependency on the .presentationMode environment value, and... | |
- The user partially dismisses the modally presented view (with the swipe down gesture) without committing to the dismissal, and | |
- Your code inside the modally presented view hierarchy accesses the EnvironmentObject dependency... | |
then what happens is, the EnvironmentObject dependency seemingly having become unloaded after partially dismissing the modal, you get ye old No ObservableObject of type MyThing found error. | |
You can fix the issue by re-injecting the environmentObject in to your modally presented view hierarchy... which seems dirty, but I know that there's a long history of modals and Environment not playing well together so it's not super surprising. | |
*/ | |
import SwiftUI | |
class MyThing : ObservableObject { | |
@Published var yeh: Bool = false | |
} | |
struct ContentView: View { | |
var body: some View { | |
ViewThatPresentsModal() | |
.environmentObject(MyThing()) | |
} | |
} | |
struct ViewThatPresentsModal : View { | |
@EnvironmentObject private var myThing: MyThing | |
@State private var showModal: Bool = false | |
@State private var showPage2OfModal: Bool = false | |
var body: some View { | |
Button(action: { showModal = true }) { | |
if myThing.yeh { | |
Text("Yeeeh") | |
} else { | |
Text("Naaah") | |
} | |
} | |
.sheet(isPresented: $showModal) { | |
NavigationView { | |
VStack { | |
Text("Hmm...") | |
NavigationLink(destination: ViewWithEnvironmentObjectDependency { | |
showModal = false | |
showPage2OfModal = false | |
}, isActive: $showPage2OfModal) { | |
Text("How about...") | |
} | |
} | |
} | |
.navigationViewStyle(StackNavigationViewStyle()) | |
// .environmentObject(myThing) // Re-injecting the EnvironmentObject dependency fixes this. | |
} | |
} | |
} | |
struct ViewWithEnvironmentObjectDependency : View { | |
@EnvironmentObject private var myThing: MyThing | |
// Remove presentationMode and the bug goes away. It's not even used in the code. Merely its presence causes problems. | |
@Environment(\.presentationMode) private var presentationMode | |
let thenWhat: () -> Void | |
var body: some View { | |
Button(action: { | |
// This will fail if presentationMode is injected and you press the button after partially dismissing the modal with swipe. | |
// myThing becomes unavailable after the partial dismissal. | |
myThing.yeh = true | |
thenWhat() | |
}) { | |
Text("Yeh?") | |
} | |
} | |
} | |
struct ContentView_Previews: PreviewProvider { | |
static var previews: some View { | |
ContentView() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment