SwiftUI Sheets, Broken @State, and Navigation Bar

When @State property wrapper is broken, use @StateObject

Zane Carter
The Startup

--

Photo by Joseph Barrientos on Unsplash

If your trying to display dynamic content in a single sheet on iOS 14 with SwiftUI you may have come across this problem.

In my case, I had multiple buttons launching different views inside of the same sheet using a state variable. You would click one button and an empty sheet would popup. Where the hell is my content? If you click a different button straight after, it works. But whatever button you click first will fail to display the correct content until a second button is clicked beforehand.

Really strange right? What makes it even more confusing is that if you print out the state variable or add a breakpoint, the state variable appears to be set properly so your content should be displaying.

Here’s an example of some code that (should) work.

The code displays two buttons that display either a red or blue sheet by settings the whichSheet variable and then toggling the showSheet variable to launch the sheet.

This is one of a few methods to display dynamic content in a SwiftUI sheet. Another method is using the .sheet(item: _) initializer but the same behavior occurs with this method too.

Here’s an example of what ends up happening. Opening the Red sheet first shows a blank sheet until you open the blue sheet at least once. Then the sheets display properly. The same happens the other way round.

Furthermore, If you switch around the if statement in the .sheet modifier to use an else instead of another if, the first button will just display the content in the else. In this case, this means if I open the red sheet by clicking the red button, the blue sheet will open.

It goes without saying any of these behaviors are a terrible user experience but thankfully there’s a few workarounds. The issue is also discussed on the apple forums.

https://developer.apple.com/forums/thread/661777
https://developer.apple.com/forums/thread/661818

The Apple forums suggest using a @Binding instead, but this didn’t seem to work for me in some cases. Apparently @States are just broken on iOS 14 inside of sheets. Anyway, here’s my fix.

Basically, Since @State objects are broken, we have to use the new iOS 14 @StateObject type. @StateObject’s act pretty much like regular @State objects except they can contain a class. Have a look above and you’ll see we have created a SheetManager class and created an instance of it to observe in our view @StateObject var sheetManager = SheetManager() .

This means by storing our sheet variables inside this object as @Published variables instead of @State objects, the variable behave correctly inside our sheet.

See the results below

The sheets work correctly!

It is a bit disappointing that this is a thing in iOS 14. It took me a good few hours to find a fix for this. Although, it’s not the end of the world it really does slow down development and make you question if your decision to make this project in SwiftUI was a sane one…

If this was helpful to you give us a follow on Twitter.

Cheers 🥳

--

--

Zane Carter
The Startup

I make apps for the AppStore. Currently working on an app that makes gardening easier. Twitter: @iamzanecarter