I am able to create, insert and get an object using SwiftData from outside of SwiftUI. But I if try to change the value of some property of a previously saved and retrieved object, this change does not persist and a do not know why.
The idea is to use AppDelegate in order to later use notifications.
So when the app starts for the first time, appdelegate method "storeValue()" is called. This methods gets the AppSetting object (if the object does not exist, it is first created and succesfully inserted into SwiftData) and tries to change the property of this retrieved object.
But changing the property of this retrieved object is not persisted.
Below is a sample code using an Actor to manage saving, getting etc.
import SwiftUI
import SwiftData
class Persistance {
static var sharedModelContainer: ModelContainer = {
let schema = Schema([
AppSetting.self,
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
}
actor PersistanceActor: ModelActor {
let modelContainer: ModelContainer
let modelExecutor: any ModelExecutor
init(modelContainer: ModelContainer) {
self.modelContainer = modelContainer
let context = ModelContext(modelContainer)
modelExecutor = DefaultSerialModelExecutor(modelContext: context)
}
func save() {
do {
try modelContext.save()
}catch {
print("error inserting")
}
}
func insert<T:PersistentModel>(_ value:T) {
do {
modelContext.insert(value)
try modelContext.save()
}catch {
print("error inserting")
}
}
func delete<T:PersistentModel>(_ value:T) {
do {
modelContext.delete(value)
try modelContext.save()
}catch {
print("error inserting")
}
}
func get<T:PersistentModel>(_ descriptor:FetchDescriptor<T>)->[T]? {
var toReturn:[T]?
do {
toReturn=try modelContext.fetch(descriptor)
}catch {
print("error inserting")
}
return toReturn
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
Task {
await storeValue(name: "name from AppDelegate")
}
return true
}
}
extension AppDelegate {
func storeValue(name: String) async {
print("storing new name: \(name)")
let apps=await AppSetting.appSetting
print("current value of stored appsetting: \(apps.name)")
apps.name="value stored from AppDelegate"
let apps2=await AppSetting.appSetting
print("current value of stored appsetting after an attempt to save a new value: \(apps2.name)")
}
}
@Model
class AppSetting {
var name:String
init(name: String) {
self.name = name
}
init() {
self.name="init"
}
private class func getAppSetting() async -> AppSetting? {
var toReturn:AppSetting?
let actor=PersistanceActor(modelContainer: Persistance.sharedModelContainer)
toReturn=await actor.get(AppSetting.allFD)?.first
return toReturn
}
static var appSetting:AppSetting {
get async {
if let found=await AppSetting.getAppSetting() {
return found
}else{
let newVal=AppSetting()
newVal.name="new value"
let actor=PersistanceActor(modelContainer: Persistance.sharedModelContainer)
await actor.insert(newVal)
return newVal
}
}
}
}
extension AppSetting {
static var allFD:FetchDescriptor<AppSetting> = {
let predicate = #Predicate<AppSetting> {value in
true
}
let descriptor=FetchDescriptor<AppSetting>(predicate: predicate, sortBy: [SortDescriptor(\AppSetting.name)])
return descriptor
}()
}
@main
struct TestingSDApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
LaunchUIView()
}
.modelContainer(Persistance.sharedModelContainer)
}
}
struct LaunchUIView: View {
@Environment(\.modelContext) private var context
@Query private var appSettings:[AppSetting]
var body: some View {
Text(appSettings.first?.name ?? "none appsetting name")
}
}