SwiftData将添加到树状结构中某个子文件夹中的项,只有在重新启动应用后才会显示
我的SwiftData应用包含两个主要的模型类,形成一棵树,Folder(根节点和子节点)和Entry(叶节点),我在一个List中展示——这一切都按预期工作。
要添加新项,我使用AddWindow,其中有一个按钮用于向根文件夹添加新项,另一个按钮用于向子文件夹添加新项。
向根文件夹添加项可以正常工作。
但向子文件夹添加的项没有在列表中显示。
事实是,如果我退出应用并重新启动,将seedItems的标志设为false以不覆盖数据,那么在上次启动中向子文件夹添加的项现在可见。所以它们已经创建并插入到上下文中,但并未立即显示。
编辑:如果我把添加按钮移到主窗口,问题就解决了。所以这与AddWindow和context有关。
我该如何解决?
一个最小可复现示例(MRE)如下:
SwiftData 模型:
import SwiftData
@Model
final class Folder {
var name: String
// Parent
var parentFolder: Folder?
// Child folders
@Relationship(deleteRule: .cascade, inverse: \Folder.parentFolder)
var subfolders: [Folder] = []
// Leaf entries
@Relationship(deleteRule: .cascade, inverse: \Entry.folder)
var entries: [Entry] = []
init(name: String, parentFolder: Folder? = nil) {
self.name = name
self.parentFolder = parentFolder
}
}
@Model
final class Entry {
var name: String
// Parent
var folder: Folder? // Inverse relationship pointing to Folder.entries
init(name: String, folder: Folder? = nil) {
self.name = name
self.folder = folder
}
}
下面是 App 和 TestView 的代码:
import SwiftData
import SwiftUI
@main
struct TestMacApp: App {
var body: some Scene {
WindowGroup {
TestView()
.modelContainer(for: Folder.self)
}
Window("Add", id: "openWindow") {
AddWindow()
.modelContainer(for: Folder.self)
}
}
}
struct TestView: View {
@Environment(\.modelContext) private var modelContext
@Environment(\.openWindow) private var openWindow
@Query(filter: #Predicate<Folder> { $0.parentFolder == nil })
private var rootFolders: [Folder]
var seedItems = true
var body: some View {
NavigationSplitView {
VStack {
List {
ForEach(rootFolders) { folder in
FolderRow(folder: folder)
}
}
.listStyle(.sidebar)
.onAppear {
if seedItems {
seed()
}
}
Button {
openWindow(id: "openWindow")
} label: {
Image(systemName: "plus")
}
}
} detail: {
Text("detail")
}
}
func seed() {
do {
let descriptor = FetchDescriptor<Folder>()
let folders: [Folder] = try modelContext.fetch(descriptor)
for folder in folders {
modelContext.delete(folder)
}
let rootFolder = Folder(name: "Root")
let entry1 = Entry(name: "One")
let entry2 = Entry(name: "Two")
rootFolder.entries.append(contentsOf: [entry1, entry2])
let subFolder = Folder(name: "Sub", parentFolder: rootFolder)
let entry3 = Entry(name: "Three")
let entry4 = Entry(name: "Four")
subFolder.entries.append(contentsOf: [entry3, entry4])
modelContext.insert(rootFolder)
try modelContext.save()
}
catch {
print(error)
}
}
}
struct FolderRow: View {
var folder: Folder
@State private var isExpanded: Bool = true
var body: some View {
DisclosureGroup(isExpanded: $isExpanded) {
// Subfolders
ForEach(folder.subfolders.sorted { $0.name < $1.name }) { subfolder in
FolderRow(folder: subfolder)
}
// Entries (leaf nodes)
ForEach(folder.entries.sorted { $0.name < $1.name }) { entry in
NavigationLink {
Text(entry.name)
} label: {
EntryRow(entry: entry)
}
}
} label: {
Label(folder.name, systemImage: "folder")
}
}
}
struct EntryRow: View {
var entry: Entry
var body: some View {
Text(entry.name)
}
}
下面是 AddWindow 的代码:
struct AddWindow: View {
@Environment(\.modelContext) private var modelContext
var body: some View {
Button("Add to Root") {
addToRoot()
}
.padding()
Button("Add to Sub") {
addToSub()
}
.padding()
}
func addToRoot() {
do {
let entry = Entry(name: "New")
let descriptor = FetchDescriptor<Folder>()
let folders: [Folder] = try modelContext.fetch(descriptor)
for folder in folders {
if folder.name == "Root" {
folder.entries.append(entry)
}
}
try modelContext.save()
}
catch {
print(error)
}
}
func addToSub() {
do {
let entry = Entry(name: "New")
let descriptor = FetchDescriptor<Folder>()
let folders: [Folder] = try modelContext.fetch(descriptor)
for folder in folders {
if folder.name == "Sub" {
folder.entries.append(entry)
}
}
try modelContext.save()
}
catch {
print(error)
}
}
}
解决方案
Ok, I think found the solution here:
在macOS应用中使用SwiftData打开带有动态数据的新窗口
我应该使用一个 sharedModelContainer,并将其注入到两个窗口中:
@main
struct TestMacApp: App {
var sharedModelContainer: ModelContainer = {
let schema = Schema([Folder.self])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
var body: some Scene {
WindowGroup {
TestView()
.modelContainer(sharedModelContainer)
}
Window("Add", id: "openWindow") {
AddWindow()
.modelContainer(sharedModelContainer)
}
}
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。