SwiftUI @AppStroage - 'Simultaneous accesses to *, but modification requires exclusive access'

SwiftUI @AppStroage - 'Simultaneous accesses to *, but modification requires exclusive access'

在我的项目中,我正在处理这个问题。几个小时后,我能够找到原因并将它们简化为一个小演示,但不了解真正的问题。正如您在下面的代码中看到的,这里有很多东西在起作用,如果使用下面提到的任何“安全”替代方案,错误就不会浮出水面。这是 Swift 内存访问问题,还是 SwiftUI 错误?

重现步骤:

  1. 将以下代码复制到项目中
  2. 基于 iPad mini
  3. 点击后退按钮

崩溃

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())
    }
}

我将把这归因于 SwiftUI 错误。我已将其提交给 Apple (FB9975464)。问题似乎出在 CrashSetting 初始值设定项中,但实际上没有它就会发生。我认为问题出在 AppStorage 密钥中使用点 (.) 字符。也造成了[error] precondition failure: setting value during update: 5848。我没能用那个崩溃制作一个小演示,但删除了“。”从我的钥匙也解决了它。如果您遇到上述任何情况,特别是在 transitioning/navigating 次查看时,值得检查您的 AppStorage 密钥。

最简单的崩溃演示

  1. 基于 iPad mini
  2. 点击后退按钮
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())
    }
}