Tenho um script que adicionará a um armazenamento SwiftData se os dados ainda não existirem. Tenho a lógica funcionando, para uma janela comum, mas não consigo fazê-la funcionar em um Menu Extra. Aqui está um exemplo:
import SwiftUI
import SwiftData
@main
struct XBApp: App {
var bundleID: String = "whatever"
var body: some Scene {
MenuBarExtra("Something", systemImage: "questionmark.bubble") {
// WindowGroup {
VStack {
XBContent(bundleID: bundleID)
.padding(0)
}
.modelContainer(for: XBData.self)
}
}
}
@Model
class XBData {
@Attribute(.unique) var bundle: String
var note: String
init(bundle: String, note: String = "Note") {
self.bundle = bundle
self.note = note
}
}
struct XBList: View {
@Query(sort: \XBData.bundle, animation: .easeInOut) var allXBarData: [XBData]
@Environment(\.modelContext) private var modelContext
var body: some View {
VStack {
Text("Everything So Far …")
List {
ForEach(allXBarData) { xbd in
HStack {
Text(xbd.bundle)
Spacer()
Text(xbd.note)
Spacer()
Button("", systemImage: "trash", action: {
modelContext.delete(xbd)
})
.buttonStyle(.bordered)
}
}
}
}
.modelContainer(for: XBData.self)
}
}
struct XBContent: View {
var bundleID: String = ""
@Environment(\.modelContext) private var modelContext
@State var xBData: XBData?
@Query(sort: \XBData.bundle, animation: .easeInOut) var allXBarData: [XBData]
func loadData(bundleID: String) -> XBData? {
print("\(#fileID):\(#line) - \(bundleID)")
// let filter = FetchDescriptor<XBData>(predicate: #Predicate { $0.bundle == bundleID })
let filter = FetchDescriptor<XBData>(predicate: #Predicate { $0.bundle == bundleID })
do {
var xbd = try modelContext.fetch(filter).first
print("\(#fileID):\(#line) - \(xbd == nil)")
if xbd == nil {
print("\(#fileID):\(#line) - \(xbd!.note)")
xbd = XBData(bundle: bundleID, note: "Newish Note: \(bundleID)")
modelContext.insert(xbd!)
try modelContext.save()
}
else {
print("\(#fileID):\(#line) - \(xbd!.note)")
}
return xbd
} catch {
print("\(#line) oops")
return nil
}
}
init(bundleID: String) {
self.bundleID = bundleID
}
var body: some View {
VStack {
Text(bundleID)
// XBList()
List {
ForEach(allXBarData) { xbd in
HStack {
Text(xbd.bundle)
Spacer()
Text(xbd.note)
Spacer()
Button("", systemImage: "trash", action: {
modelContext.delete(xbd)
})
.buttonStyle(.bordered)
}
}
}
}
.modelContainer(for: XBData.self)
.onAppear {
xBData = loadData(bundleID: bundleID)!
xBData!.note = "Newish Note: \(bundleID)"
try? modelContext.save()
}
}
}
Desculpe pelo tamanho da amostra.
Presumindo que um item com id de whatever
não existe, ele deve ser adicionado e exibido na lista.
Na estrutura App, comentei o WindowGroup
. Se eu alternar para isso e não para o MenuBarExtra, funciona como esperado.
Também recebo a mensagem muito útil:
Não é possível encontrar ou decodificar os motivos
Falha ao obter ou decodificar os motivos indisponíveis
No entanto, isso acontece com o WindowGroup ou o MenuBarExtra, então não sei se está relacionado.
Qual é o truque para fazer isso funcionar com um MenuBarExtra?
Experimente esta abordagem para usar
MenuBarExtra
Observe que
func loadData
não use!
em seu código, especialmente emif xbd == nil { print("\(#fileID):\(#line) - \(xbd!.note)") ...
Observe também: pare de usar
.modelContainer(for: XBData.self)
em todos os lugares, apenas um em vocêXBApp
já é suficiente.Observe que, quando você faz isso
modelContext.insert(xbd!)
,save
os dados são automaticamente salvos, você não precisa usartry modelContext.save()
EDITAR-1
Este é o código completo que funciona muito bem para mim, testado no macOS 15.