In my project I was dealing with this issue. After many hours, I was able to track down the causes and reduce them into a small demo, but do not understand the real issue. As you can see in the below code there are many things at play here, and the bug doesn't surface if any of the "safe" alternatives mentioned below are used. Is this a Swift Memory Access issue, or a SwiftUI bug?
Steps to reproduce:
Crash
import SwiftUI
/*
Reproduction Steps:
===================
#1. Build on iPad mini (other devices will work in split screen, but Detail view must be showing, sidebar view must be hidden)
#2. Tap Back Button
=====================
Crash
Thread 1: Simultaneous accesses to 0x7f8af986bae0, but modification requires exclusive access
// Note: if any of the "safe" alternatives are used below, the bug won't surface.
*/
// MARK: - Model
public struct SafeSetting<V> {
public let key: String
public let defaultValue: V
public init(name: String, defaultValue: V) {
self.key = name
self.defaultValue = defaultValue
}
}
public struct CrashSetting<V> {
public let key: String
public let defaultValue: V
public init(namespace: String, name: String, defaultValue: V) {
/// - NOTE: I believe this is the main issue, but why?
self.key = String(namespace + "." + name)
self.defaultValue = defaultValue
}
}
enum Settings {
static let safe = SafeSetting(name: "safe", defaultValue: "")
static let crash = CrashSetting(namespace: "com.mynamespace", name: "crash", defaultValue: "")
}
// MARK: - View
@main
struct NavigationLinkCrashApp: App {
var body: some Scene {
WindowGroup {
RootView()
}
}
}
struct RootView: View {
// MARK: Safe
/*
@AppStorage(Settings.safe.key)
var safe = Settings.safe.defaultValue
*/
// MARK: Crash
@AppStorage(Settings.crash.key)
var crash = Settings.crash.defaultValue
@ViewBuilder var content: some View {
NavigationView {
Sidebar()
Color.yellow
}
}
var body: some View {
content
}
}
struct Sidebar: View {
var body: some View {
// List { NavigationLink } hierarchy is needed!
List {
NavigationLink {
Color.red
} label: {
// MARK: Safe
/// `Text("")`
/// `Label("", systemImage: "circle")`
/// `Color.green`
// MARK: Crash
Text("crashes")
//Label("crashes", systemImage: "circle")
}
}
// List Style is needed!
// MARK: Safe
//.listStyle(SidebarListStyle())
//.listStyle(InsetListStyle())
// MARK: Crash
.listStyle(InsetGroupedListStyle())
//.listStyle(PlainListStyle())
//.listStyle(GroupedListStyle())
}
}
I am going to attribute this to a SwiftUI bug. I have submitted it to Apple (FB9975464). The issue seemed to be in the CrashSetting
initializer, but it actually occurs without it. I believe the issue is using the dot (.
) character in the AppStorage key. It has also caused [error] precondition failure: setting value during update: 5848
. I haven't been able to produce a small demo with that crash, but removing the "." from my keys also solves it. If you experience any of the above, specifically while transitioning/navigating views, it's worth checking your AppStorage keys.
import SwiftUI
@main
struct AppStorage_Key_IssueApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
// Using the "." character results in many hard to track crashes
@AppStorage("a.b")
var val = ""
var body: some View {
NavigationView {
PrimaryView()
Color.blue
}
}
}
struct PrimaryView: View {
var body: some View {
List {
NavigationLink {
Color.red
} label: {
Text("crashes")
}
}
.listStyle(InsetGroupedListStyle())
}
}