I’m attempting to maintain a local copy of server data in my application and thought I could accomplish this with swift-data (in fact, I’ve followed this article about it in the official Apple Documentation). However, SwiftData ignores the values set in the @Model
initializer and any subsequent attempts to update the values.
My impression was that: by using the @Attribute(.unique)
value on the id
parameter, any time I created a model with the same ID as an existing one, SwiftData would update the existing model. That doesn’t appear to be the case.
I can’t quite determine why SwiftData is not allowing me to update my model (esp. from inside the model’s own initializer). Is this a bug? Some undocumented behavior? Or have I just got things configured incorrectly, or am using these models in an unsupported way?
This behavior can be replicated using a simple model object and initializer like this:
@Model
final class Item {
@Attribute(.unique)
var id: String
var timestamp: Date
var deleted: Bool
// Initializes a local SwiftData model from a "RemoteItem", which is just a Codable
// object generated from a server response.
//
init?(from remoteModel: RemoteItem) {
guard let remoteId = remoteModel.id else { return nil }
self.id = remoteId
self.timestamp = remoteModel.timestamp
print("Initialized Item model with deletion value from remote model: \(remoteModel.deleted)")
// Prints correct value of deleted to the console (e.g. true)
self.deleted = remoteModel.deleted
print("Initialized Item model with remote deletion. Value is now: \(self.deleted)")
// Prints INCORRECT value of deleted to the console (e.g. false)
}
}
Then, in a data source actor (or other similar class):
func fetchItems() async throws -> [Item] {
let someRemoteItems = try await networkRequestForRemoteItems()
// Map the remote items to locally cached items, similar to the demo
// provided by Apple here: https://developer.apple.com/documentation/swiftdata/maintaining-a-local-copy-of-server-data
let localItemCopies: [Item] = someRemoteItems.compactMap({ Item(from: $0) })
// "Cache" the remote items
for localItem in localItemCopies {
await cacheContainer.insert(localItem)
}
return localItemCopies
}
As noted above, any items that already existed locally in the SwiftData model store are not updated when inserted again here. And for additional context, I’ve seen other questions asking about similar behavior but only in the context of SwiftUI, which is not relevant here.