Hi! While working on my Swift Student Challenge submission it seems that I found a race condition (TOCTOU) bug in SwiftUI when using sheets, and I'm not sure if this is expected behaviour or not.
Here's an example code:
import SwiftUI
struct ContentView: View {
@State var myVar: Int?
@State private var presentSheet: Bool = false
var body: some View {
VStack {
// Uncommenting the following Text() view will "fix" the bug (kind of, see a better workaround below).
// Text("The value is \(myVar == nil ? "nil" : "not nil")")
Button {
myVar = nil
} label: {
Text("Set value to nil.")
}
Button {
myVar = 1
presentSheet.toggle()
} label: {
Text("Set value to 1 and open sheet.")
}
}
.sheet(isPresented: $presentSheet, content: {
if myVar == nil {
Text("The value is nil")
.onAppear {
print(myVar) // prints Optional(1)
}
} else {
Text("The value is not nil")
}
})
}
}
When opening the app and pressing the open sheet button, the sheet shows "The value is nil", even though the button sets myVar
to 1 before the presentSheet
Bool is toggled.
Thankfully, as a workaround to this bug, I found out you can change the sheet's view to this:
.sheet(isPresented: $presentSheet, content: {
if myVar == nil {
Text("The value is nil")
.onAppear {
if myVar != nil {
print("Resetting View (TOCTOU found)")
let mySwap = myVar
myVar = nil
myVar = mySwap
}
}
} else {
Text("The value is not nil")
}
})
This triggers a view refresh by setting the variable to nil and then to its non-nil value again if the TOCTOU is found.
Do you think this is expected behaivor? Should I report a bug for this? This bug also affects .fullScreenCover()
and .popover()
.