SwiftUI 和单元测试失败:使用核心数据堆栈时无法添加相同的商店两次

SwiftUI & Unit test fails: Can't add the same store twice when using core data stack

将 Xcode 更新到 13.4 后,我的核心数据持久性堆栈开始无法用于 SwiftUI 预览和 Xcode 单元测试。

错误信息:

Unresolved error Error Domain=NSCocoaErrorDomain Code=134081 "(null)" UserInfo={NSUnderlyingException=Can't add the same store twice}, ["NSUnderlyingException": Can't add the same store twice ...

持久性堆栈:

struct PersistenceController {
    static let shared = PersistenceController()

    let container: NSPersistentContainer

    init(inMemory: Bool = false) {
        container = NSPersistentContainer(name: "Persistence")
        if inMemory {
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        }
        let description = NSPersistentStoreDescription()

        description.shouldInferMappingModelAutomatically = true
        description.shouldMigrateStoreAutomatically = true

        container.persistentStoreDescriptions.append(description)
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        container.viewContext.automaticallyMergesChangesFromParent = true
    }
}

SwiftUI 预览的用法:

struct MyScreen_Previews: PreviewProvider {
    static var previews: some View {
        MyScreen()
            .environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
    }
}

支持 SwiftUI 预览的扩展:

extension PersistenceController {
    static var preview: PersistenceController = {
        let result = PersistenceController(inMemory: true)
        // Pre-fill core data with required information for preview.
    }
}

我注意到,在加载持久性存储后生成了错误。此错误(无法添加同一商店两次),对于 SwiftUI 预览和单元测试可以忽略。

要忽略此类错误需要检查进程信息中的 XCTestSessionIdentifierXCODE_RUNNING_FOR_PREVIEWS

if let error = error as NSError? {
    if ProcessInfo.processInfo.environment["XCTestSessionIdentifier"] == nil && 
       ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == nil {
        fatalError("Unresolved error \(error), \(error.userInfo)")
    }
}

您的代码中有一个错误,当您附加到描述数组时,您现在有 2 个描述。

改为:

let description = container.persistentStoreDescriptions.first!
description.shouldInferMappingModelAutomatically = true
description.shouldMigrateStoreAutomatically = true

// Load