SwiftData 在 SwiftUI 中将 @Model 对象作为参数传递

Syd*_*olk 7 swift-data swiftui

我正在尝试在基于 SwiftData 的 SwiftUI 项目中使用多个视图。但是,当我在另一个视图中为 SwiftData @Model 对象创建参数时,当我在 #Preview 中创建该对象的示例时,编译器开始抱怨未在 @Model 对象中实现协议。

Xcode 15 测试版 2

我从一个新的 iOS SwiftData 项目开始。我像这样更新了 ContentView:

import SwiftUI
import SwiftData

struct ContentView: View {
    @Environment(\.modelContext) private var modelContext
    @Query private var items: [Item]
    
    @State private var selection: Item?
    
    var body: some View {
        NavigationSplitView {
            List(selection: $selection) {
                ForEach(items) { item in
                        Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))
                }
                .onDelete(perform: deleteItems)
            }
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    EditButton()
                }
                ToolbarItem {
                    Button(action: addItem) {
                        Label("Add Item", systemImage: "plus")
                    }
                }
            }
        } detail: {
            DetailView(selection: selection)
        }
        .navigationSplitViewStyle(.balanced)
    }

    private func addItem() {
        withAnimation {
            let newItem = Item(timestamp: Date())
            modelContext.insert(newItem)
        }
    }

    private func deleteItems(offsets: IndexSet) {
        withAnimation {
            for index in offsets {
                modelContext.delete(items[index])
            }
        }
    }
}

#Preview {
    ContentView()
        .modelContainer(for: Item.self, inMemory: true)
}
Run Code Online (Sandbox Code Playgroud)

然后我创建了 DetailView.swift:

import SwiftUI
import SwiftData

struct DetailView: View {
    var selection: Item?
    
    var body: some View {
        if let item = selection {
            Text("Item at \(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))")
        } else {
            Text("nothing selected")
        }
    }
}

#Preview {
    return DetailView(selection: Item(timestamp: Date()))
        .modelContainer(for: Item.self, inMemory: true)
}
Run Code Online (Sandbox Code Playgroud)

Item.swift 与在新项目中生成的方式没有变化:

import Foundation
import SwiftData

@Model
final class Item {
    var timestamp: Date
    
    init(timestamp: Date) {
        self.timestamp = timestamp
    }
}
Run Code Online (Sandbox Code Playgroud)

但是当我在 DetailView 中添加参数时#Preview,编译器将不会构建项目:

/var/folders/9j/8r6qn35j5jjf0gm3crp6gynw0000gn/T/swift- generated-sources/@_ swiftmacro_12navSplitView4Item5ModelfMc .swift:1:1 类型“Item”不符合协议“PersistentModel”

我需要做什么才能将这些 @Model 对象作为参数传递?

Joa*_*son 4

问题是Item您在#Preview宏内创建的对象不属于ModelContext生成错误的实例。

为了解决这个问题,我们首先需要一个单独的ModelContainer预览,并使用该容器的模型上下文来插入我们在预览中使用的对象,这是一个简单的示例。

#if DEBUG
@MainActor
let previewContainer: ModelContainer = {
    do {
        let container = try ModelContainer(for: Item.self, ModelConfiguration(inMemory: true))        
        container.mainContext.insert(Item.preview)
        
        return container
    } catch {
        fatalError("Failed to create preview container")
    }
}()
#endif
Run Code Online (Sandbox Code Playgroud)

其中Item.preview是定义在的静态属性Item

@Model
final class Item {
    // existing code...

    static let preview: Item = {
        Item(timestamp: .now)
    }()
}
Run Code Online (Sandbox Code Playgroud)

然后预览宏改为

#Preview {
    DetailView(selection: .preview)
}
Run Code Online (Sandbox Code Playgroud)

  • 对于第二个问题,在预览宏内的代码周围使用 MainAcor.assumeIsolated { … } 。 (2认同)