SwiftData 预览报错问题
SwiftData 预览报错问题

SwiftData 预览报错问题

问题描述

在学习SwiftData中,预览存在无法显示的情况,但是模拟器是正常的。

预览代码为:

#Preview {
    ProfileView()
}

报错的内容为:

CrashReportError: hackingwithswift crashed due to an uncaught exception
    
hackingwithswift crashed due to an uncaught exception `NSInvalidArgumentException`. Reason: Cannot insert 'Book' in this managed object context because it is not found in the associated managed object model..

经排查发现问题跟数据持久化和状态管理有关。

需要在SwiftUI的预览中,提供一个有效的ModelContainer,并插入示例数据,以便在预览中可以访问到数据。

#Preview {
    // 创建一个 ModelContainer,并插入一些示例数据
    let container = try! ModelContainer(for: Book.self)
    let exampleBook = Book(title: "示例书籍", isbn: "1234567890", publicationYear: 2024, author: "作者名")
    
    // 插入示例数据
    let context = container.mainContext
    context.insert(exampleBook)
    try? context.save() // 注意错误处理,使用 try? 来避免崩溃

    return ProfileView()
        .modelContainer(container)
}

预览代码

在部分视图中,可以在预览代码中使用modelContainer(for:),以一种更简洁的方式绑定模型容器来进行预览。

// 模型容器绑定单个类型
ContentView()
    .modelContainer(for: Friendface.self)
// 模型容器绑定多个类型
ContentView()
    .modelContainer(for: [Friendface.self,Friend.self])

同类问题

modelContainer(for:),这个简洁的方法并不总是奏效。   

这一次的问题为,一个展示内容视图预览报错:

import SwiftUI
import SwiftData

struct FriendInfo: View {
    @Environment(\.modelContext) var modelContext
    var friendInfo: Friendface
    
    var body: some View {
        Form {
            Section("name") {
                Text(friendInfo.name)
            }
            ...
        }
    }
}

#Preview {
    FriendInfo(friendInfo: Friendface(
        id: "123",
        isActive: true,
        name: "John Doe",
        age: 30,
        email: "john.doe@example.com",
        address: "123 Swift Street",
        about: "A friendly person.",
        registered: Date(),
        tags: ["developer", "iOS"],
        friends: []
    ))
    .modelContainer(container)
}

内容展示视图FriendInfo在模拟器或者真机的状态下,都是正常访问且无报错的,但是Xcode报错。

在Xcode中,内容列表视图ContentView可以通过预览视图访问到这个内容展示视图FriendInfo。

但是FriendInfo视图下预览就报错,因此推测问题是在预览代码中:

#Preview {
    FriendInfo(friendInfo: Friendface(
        id: "123",
        isActive: true,
        name: "John Doe",
        age: 30,
        email: "john.doe@example.com",
        address: "123 Swift Street",
        about: "A friendly person.",
        registered: Date(),
        tags: ["developer", "iOS"],
        friends: []
    ))
    .modelContainer(for: Friendface.self)
}

但是预览代码也是正常的生成一个Friendface实例并加载了modelContainer(for:)。

在ContentView视图中,modelContainer(for:)是可以正常预览的。

也没有从Xcode报错中找到问题的原因,只知道可能跟Friendface.init 的初始化有关。但没有定位到具体的报错行。

最后,还是通过上次的解决方案解决的这一问题:

Preview {
    // 创建一个 ModelContainer,并插入一些示例数据
        let container = try! ModelContainer(for: Friendface.self)
        let exampleBook = Friendface(
            id: "123",
            isActive: true,
            name: "John Doe",
            age: 30,
            email: "john.doe@example.com",
            address: "123 Swift Street",
            about: "A friendly person.",
            registered: Date(),
            tags: ["developer", "iOS"],
            friends: []
        )
        
        // 插入示例数据
        let context = container.mainContext
        context.insert(exampleBook)
        try? context.save() // 注意错误处理,使用 try? 来避免崩溃
    return FriendInfo(friendInfo: Friendface(
        id: "123",
        isActive: true,
        name: "John Doe",
        age: 30,
        email: "john.doe@example.com",
        address: "123 Swift Street",
        about: "A friendly person.",
        registered: Date(),
        tags: ["developer", "iOS"],
        friends: []
    ))
    .modelContainer(container)
}

使用 ModelContainer 和 modelContainer(_:) 解决了预览报错,显式地为预览提供了 SwiftData 的上下文环境和模型容器。

解决步骤

1、ModelContainer 的显式初始化

通过 ModelContainer(for:) 创建了一个与 Friendface 关联的模型容器。这样,预览可以初始化一个完整的 SwiftData 环境,而不再缺少必需的上下文。

let container = try! ModelContainer(for: Friendface.self)

SwiftData 的 @Model 类型需要在一个 ModelContainer 中管理。如果没有提供容器,SwiftData 无法在预览时管理模型对象,这正是之前的错误原因。

2、向容器插入数据

显式地将数据插入到了容器的上下文中:

let context = container.mainContext
context.insert(exampleBook)
try? context.save()

这一步模拟了 SwiftData 数据存储的行为,使得容器中有实际的数据供视图使用,而不会因为缺少数据导致运行时错误。

3、绑定容器到视图的预览环境

最后,通过 modelContainer(_:) 将模型容器绑定到视图,使视图能够访问和使用这个容器的上下文:

.modelContainer(container)

这一绑定建立了视图与 SwiftData 的连接,从而解决了之前预览环境中缺少上下文的报错问题。

为什么之前的预览代码报错?

SwiftData 缺少上下文: @Model 类型依赖一个有效的 ModelContainer 和上下文。如果没有绑定容器,Friendface 的 @Model 特性在预览中无法正确解析和初始化。

没有显式插入数据: 预览中访问的数据是动态加载的。如果上下文中没有插入数据,视图会尝试从空的数据源中加载,可能会导致崩溃或无法渲染。

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开放者。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注