In Xcode Version 15.0 beta (15A5160n) this predicate will not build:
let thisPredicate = #Predicate<Ledgers> {
($0.myStore != nil)
}
The definition of .myStore in the Ledgers class is:
var myStore: Stores?
an optional relationship. Is there anyway to test for nil optional relationships in SwiftData?
The inverse relationship in Stores is:
@Relationship(.deny, inverse: \Ledgers.myStore) var myReceipts: [Ledgers]
SwiftData
RSS for tagSwiftData is an all-new framework for managing data within your apps. Models are described using regular Swift code, without the need for custom editors.
Posts under SwiftData tag
200 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Hi,
I am looking for information on how to setup a ModelContainer for a Swift app when the SwiftData model is located in a Swift package.
Let's say the model code resides in a package called DomainModel. That package was added target's embedded content and the model types are used to init the model container using the modelContainer(for:) view modifier on the ContentView of the app.
This crashes the app on startup with a EXC_BAD_ACCESS code=2. When copying the model code from the package directly into the app, the app starts just fine.
I kindly ask for a code sample or any information about how to setup the model container when using a model from a Swift package.
Thanks in advance and I am looking forward to replacing CoreData π
Hi,
if I have a @Model class there's always an id: PersistentIdentifier.ID underneath which, according to the current documentation "The value that uniquely identifies the associated model within the containing store.".
So I am wondering if it is (good) enough to rely on this attribute to uniquely identify @Model class entities, or if there are edge cases where it does not work (like maybe when using CloudKit)?
If anybody saw some information regarding this, please let me know :-)
Cheers,
Michael
I have an app that uses CoreData and I want to migrate to SwiftData. After following the Migrate to SwiftData session, I only need to point to my old Core Data file to read the old data and convert it to the new SwiftData format.
My question is how do I do this? Maybe worth mentioning is that my NSPersistentContainer(name: "Model") is different to my app name.
Possible Solution?
According to a Tweet by Donny Wals this is done this way:
By default a SwiftData ModelContainer will create its underlying storage in a file called default.store. If you want to change this so you can use an existing Core Data SQLite file, you can point your container to that file instead:
// point to your old sqlite file
let url = URL.applicationSupportDirectory.appending(path: "Model.sqlite")
let config = ModelConfiguration(url: url)
modelContainer = try ModelContainer(for: [
Movie.self
], config)
My Tested Code
@main
struct SwiftData_TestApp: App {
let url = URL.applicationSupportDirectory.appending(path: "Model.sqlite")
let config = ModelConfiguration(url: url)
let modelContainer = try ModelContainer(for: [
Item.self
], config)
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(modelContainer)
}
}
The problem here is that I donβt get it to work in the main app struct. When using this the way described in Dive deeper into SwiftData (at 6:58) I only get the error: Cannot use instance member 'url' within property initializer; property initializers run before 'self' is available
PS: There seems to be an issue with this WWDC session method anyway β see this post.
It seems that @Model is not fully supported in visionOS. I've tried both an iPad app and a native visionOS app, and both crash when trying to use SwiftData. It's a minimal app from the template (no data) that I add a little data code to:
import SwiftData
@Model
final class mapPin {
var lat : Double
var lon : Double
init(lat: Double, lon: Double) {
self.lat = lat
self.lon = lon
}
}
Building for visionOS produces:
/var/folders/9p/ppkjrfhs393__cqm9z57k9mr0000gn/T/swift-generated-sources/@__swiftmacro_7Vision16mapPin5ModelfMm_.swift:2:13: error: declaration name '_$backingData' is not covered by macro 'Model'
private var _$backingData: any SwiftData.BackingData<mapPin> = SwiftData.DefaultBackingData(for: mapPin.self)
^
/Users/arenberg/Developer/Beta/Vision1/Vision1/ContentView.swift:13:7: note: in expansion of macro 'Model' here
final class mapPin {
^~~~~~~~~~~~~~
/var/folders/9p/ppkjrfhs393__cqm9z57k9mr0000gn/T/swift-generated-sources/@__swiftmacro_7Vision16mapPin5ModelfMc_.swift:1:1: error: type 'mapPin' does not conform to protocol 'PersistentModel'
extension mapPin : SwiftData.PersistentModel {}
^
/Users/arenberg/Developer/Beta/Vision1/Vision1/ContentView.swift:13:7: note: in expansion of macro 'Model' here
final class mapPin {
^~~~~~~~~~~~~~
/var/folders/9p/ppkjrfhs393__cqm9z57k9mr0000gn/T/swift-generated-sources/@__swiftmacro_7Vision16mapPin5ModelfMc_.swift:1:1: note: do you want to add protocol stubs?
extension mapPin : SwiftData.PersistentModel {}
^
/Users/arenberg/Developer/Beta/Vision1/Vision1/ContentView.swift:13:7: note: in expansion of macro 'Model' here
final class mapPin {
^~~~~~~~~~~~~~
/var/folders/9p/ppkjrfhs393__cqm9z57k9mr0000gn/T/swift-generated-sources/@__swiftmacro_7Vision16mapPin5ModelfMm_.swift:2:64: error: module 'SwiftData' has no member named 'DefaultBackingData'
private var _$backingData: any SwiftData.BackingData<mapPin> = SwiftData.DefaultBackingData(for: mapPin.self)
^~~~~~~~~ ~~~~~~~~~~~~~~~~~~
/Users/arenberg/Developer/Beta/Vision1/Vision1/ContentView.swift:13:7: note: in expansion of macro 'Model' here
final class mapPin {
^~~~~~~~~~~~~~```
Hi,
when inserting an entity with a relationship I get the following runtime error:
Illegal attempt to establish a relationship 'group' between objects in different contexts [...].
The model looks like this:
@Model
class Person {
var name: String
@Relationship(.nullify, inverse: \Group.members) var group: Group
init(name: String) {
self.name = name
}
}
@Model
class Group {
var name: String
@Relationship(.cascade) public var members: [Person]
init(name: String) {
self.name = name
}
}
It can be reproduced using this (contrived) bit of code:
let group = Group(name: "Group A")
ctx.insert(group)
try! ctx.save()
let descriptor = FetchDescriptor<Group>()
let groups = try ctx.fetch(descriptor)
XCTAssertFalse(groups.isEmpty)
XCTAssertEqual(groups.count, 1)
XCTAssertTrue(groups.first?.name == "Group A")
let person = Person(name: "Willy")
person.group = group
ctx.insert(person)
try ctx.save()
(See also full test case below).
Anybody experiencing similar issues? Bug or feature?
Cheers, Michael
Full test case:
import SwiftData
import SwiftUI
import XCTest
// MARK: - Person -
@Model
class Person {
var name: String
@Relationship(.nullify, inverse: \Group.members) var group: Group
init(name: String) {
self.name = name
}
}
// MARK: - Group -
@Model
class Group {
var name: String
@Relationship(.cascade) public var members: [Person]
init(name: String) {
self.name = name
}
}
// MARK: - SD_PrototypingTests -
final class SD_PrototypingTests: XCTestCase {
var container: ModelContainer!
var ctx: ModelContext!
override func setUpWithError() throws {
let fullSchema = Schema([Person.self,
Group.self,])
let dbCfg = ModelConfiguration(schema: fullSchema)
container = try ModelContainer(for: fullSchema, dbCfg)
ctx = ModelContext(container)
_ = try ctx.delete(model: Group.self)
_ = try ctx.delete(model: Person.self)
}
override func tearDownWithError() throws {
guard let dbURL = container.configurations.first?.url else {
XCTFail("Could not find db URL")
return
}
do {
try FileManager.default.removeItem(at: dbURL)
} catch {
XCTFail("Could not delete db: \(error)")
}
}
func testRelAssignemnt_FB12363892() throws {
let group = Group(name: "Group A")
ctx.insert(group)
try! ctx.save()
let descriptor = FetchDescriptor<Group>()
let groups = try ctx.fetch(descriptor)
XCTAssertFalse(groups.isEmpty)
XCTAssertEqual(groups.count, 1)
XCTAssertTrue(groups.first?.name == "Group A")
let person = Person(name: "Willy")
person.group = group
ctx.insert(person)
try ctx.save()
}
}
I have multiple classes that are contained within one another.
Initializing class A(no other classes as properties) has no issues, but the moment that it tries to initialize class B, which takes class A as a property, an EXC_BAD_ACCESS error is thrown while attempting to set the first property value.
Class A:
import Foundation
import SwiftData
@Model
public class Service: Identifiable, Equatable, Codable {
@Attribute(.unique)
public var id: UUID
public var title: String
public var price: Int
public var stripePriceId: String
public var servicePhoto: String
public var serviceLength: Int
public var category: [String]
init(id: UUID, title: String, price: Int, stripePriceId: String, servicePhoto: String, serviceLength: Int, category: Array<String>) {
self.id = id
self.title = title
self.price = price
self.stripePriceId = stripePriceId
self.servicePhoto = servicePhoto
self.serviceLength = serviceLength
self.category = category
}
}
Class B:
import Foundation
import SwiftData
import SwiftUI
@Model
public class ServiceOrder: Identifiable, Codable, Equatable {
public var id: UUID
@Relationship(.noAction)
public var service: Service
public var quantity: Int = 1
public var subtotal: Int { return service.price * quantity }
public init(id: UUID = UUID(), service: Service, quantity: Int) {
self.id = id
self.service = service
self.quantity = quantity
}
public func getValue<T>(for key: KeyPath<ServiceOrder, T>) -> T {
return self[keyPath: key]
}
// This is where the error is being thrown. The custom setValue and getValue methods were added to every class to mitigate an 'Ambiguous use of __Value()' error, but that's for another thread
public func setValue<T>(for key: ReferenceWritableKeyPath<ServiceOrder, T>, to newValue: T) {
self[keyPath: key] = newValue
}
// This was added to see if following the Builder pattern might mitigate the issue by ensuring that all properties were initialized before being passed in, it did not change anything
class ServiceOrderBuilder {
private var id: UUID?
private var service: Service?
private var quantity: Int?
init() {
}
init(id: UUID = UUID(), service: Service, quantity: Int = 1) {
self.id = id
self.service = service
self.quantity = quantity
}
func setId(id: UUID = UUID()) -> ServiceOrderBuilder {
self.id = id
return self
}
func setService(service: Service) -> ServiceOrderBuilder {
self.service = service
return self
}
func setQuantity(quantity: Int = 1) -> ServiceOrderBuilder {
self.quantity = quantity
return self
}
func build() -> ServiceOrder? {
guard let id = id, let service = service, let quantity = quantity else {
return nil
}
return ServiceOrder(id: id, service: service, quantity: quantity)
}
}
Here's where I'm doing the initialization of everything. I'm trying to just create sample data.
import SwiftData
import Foundation
@MainActor
public let previewContainer: ModelContainer = {
do {
var container = try ModelContainer(
for: [Event.self, Braider.self, Queue.self, QueueSlot.self, Cart.self, ServiceOrder.self],
ModelConfiguration(inMemory: true)
)
var context = container.mainContext
var serviceSampleData: [Service] = [
Service(
id: UUID(),
title: "Event Braid",
price: 20, stripePriceId: "",
servicePhoto: "",
serviceLength: 15,
category: []),
...,
]
serviceSampleData.forEach { service in
context.insert(service)
}
// This is where the error is thrown, no issue initializing the above Services
var serviceOrder1: ServiceOrder = ServiceOrder(
service: serviceSampleData[0],
quantity: 1)
context.insert(serviceOrder1)
// ...continue building higher classes/Models that utilize the lower ones
return container
} catch {
print("Failed to create container")
do {
return try ModelContainer(
for: [Event.self],
ModelConfiguration(inMemory: true)
)
} catch {
fatalError()
}}
}()
I assumed that this was a SwiftData issue, as I mentioned in response to this post but looking back this issue has been happening for 7+ years, so now I'm assuming I'm doing something incorrect.
Consider the Categories class below.
Without the @Model line, Xcode has no problems.
As soon as the @Model line is added, Xcode protests that the class does not conform to Decodable or Encodable.
Apparently the @Model macro expanded macro prevents the implied boilerplate that the systems adds when the Codable protocol is added to the class.
I've filed feedback (FB12444837) for this. Does anyone have suggestions that will let me avoid the boilerplate? This is the simplest class in my schema, I've got 12 other classes with many more variables.
@Model
class Categories: Identifiable, Codable {
// MARK: Identifiable
var id: UUID { return uuidKey }
// MARK: - Properties
@Attribute(.unique) var uuidKey: UUID = UUID()
var dateCreated: Date = Date()
var dateModified: Date = Date()
var dateRealm: Date? = nil
var uuidUser: UUID = UUID()
var uuidFamily: UUID = UUID()
var myName: String = ""
}
I am working in Xcode 15 (beta) migrating to SwiftData and am having a hard time figuring out how to form a search predicate for my one to many model. The desired result is to return a query that returns only the articles and sections where the article "search" field contains the string a user is searching for.
Here is my current model:
@Model
class SectionsSD {
@Attribute(.unique) var id: String
var section: String
var rank: String
var toArticles: [ArticlesSD]?
init(id:String, section: String, rank: String) {
self.id = id
self.section = section
self.rank = rank
}
}
@Model
class ArticlesSD {
var id: String
var title: String
var summary: String
var search: String
var section: String
var body: String
@Relationship(inverse: \SectionsSD.toArticles) var toSection: SectionsSD?
init(id: String, title: String, summary: String, search: String, section: String, body: String) {
self.id = id
self.title = title
self.summary = summary
self.search = search
self.section = section
self.body = body
}
}
In CoreData I was able to do the following in my code to form and pass the search predicate ("filter" being the user input search text, "SectionsEntity" being my old CoreData model):
_fetchRequest = FetchRequest<SectionsEntity>(sortDescriptors: [SortDescriptor(\.rank)], predicate: NSPredicate(format: "toArticles.search CONTAINS %@", filter))
I can't find any examples or info anywhere that explains how to form a predicate in SwiftData to achieve the same search results. I can't seem to find a way to represent toArticles.search properly assuming the capability is there. Here is what I've tried but Xcode complains about the $0.toArticles?.contains(filter) with errors about "Cannot convert value of type 'Bool?' to closure result type 'Bool'" and "Instance method 'contains' requires the types 'ArticlesSD' and 'String.Element' (aka 'Character') be equivalent"
let searchPredicate = #Predicate<SectionsSD> {
$0.toArticles?.contains(filter)
}
_sectionsSD = Query(filter: searchPredicate)
I've tried $0.toArticles?.search.contains(filter) but Xcode can't seem to find its way there like it did using CoreData
Any suggestions and examples on how to form a predicate in this use case would be appreciated.
I get the following error when I try to add the modelContainer to the Window
SwiftData/DataStoreCoreData.swift:119: Fatal error: Unable to determine Bundle Name
@Model class AModel {
@Attribute(.unique) var id:String
var date:Date
var b:[BModel]
init() {
self.id = UUID().uuidString
self.date = Date()
self.b = []
}
}
@Model class BModel {
@Attribute(.unique) var id:String
var date:Date
init() {
self.id = UUID().uuidString
self.date = Date()
}
}
struct MainView: View {
@Environment(\.modelContext) private var db
@State private var a:AModel = AModel()
var body: some View {
VStack {
}
.onAppear {
a.b.append(BModel())
print(a.b)
}
}
}
// CRASH :
@Model class AModel {
@Attribute(.unique) var id:String
var date:Date
var b:[BModel]
/**
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
{
get {
_$observationRegistrar.access(self, keyPath: \.b)
return self.getValue(for: \.b)
}
set {
_$observationRegistrar.withMutation(of: self, keyPath: \.b) {
self.setValue(for: \.b, to: newValue)
}
}
}
*/
init() {
self.id = UUID().uuidString
self.date = Date()
self.b = []
}
}
@Model class BModel {
@Attribute(.unique) var id:String
var date:Date
init() {
self.id = UUID().uuidString
self.date = Date()
}
}
Not sure what I'm doing wrong here. I'm taking this opportunity to add persistence to an app that hadn't had it yet.
I followed the session advice but I get this crash when running it:
SwiftData/BackingData.swift:201: Fatal error: expected attribute to be Codable
The crash is on this line:
modelContext.insert(RentSplitDataModel())
The object being created and inserted there is simple and the compiler confirms it does conform to Codable:
https://github.com/KyLeggiero/Rent-Split-for-iOS/blob/feature/MVP/Shared/Model/RentSplitDataModel.swift
Hi all,
I have tried everything (that i'm aware of) but i cannot seem to get around this error.
My app is setup using only SwiftData and is basically a custom version of the apps shown in SwiftData docs. I have done the following to try to debug:
Deleted app from device
Clean and re-build
Delete derived data and rebuild
I have tried reverting changes made to the @Models i have define
Every combination of the above steps + rebuilding
Im at a loss. No clue how to resolve this.
Some weird observations. This error only occurs after the first context.insert call. The first is successful and data is properly persisted across app launches. All subsequent attempts to insert result in the following error:
Error Domain=NSCocoaErrorDomain Code=134020 "The model configuration used to open the store is incompatible with the one that was used to create the store." UserInfo={NSAffectedObjectsErrorKey=<NSManagedObject: 0x2824004b0> (entity: Track; id: 0x2807a7de0 <x-coredata:///Track/tB9B77486-8F60-4F63-848D-D1C5CC67BA526>; data: {
createdAt = "2023-07-02 23:45:45 +0000";
name = Test;
})}
I have a WidgetExtension using SwiftData (same ModelContainer setup used in the WatchApp:
@main
struct Watch_Widget: Widget {
let kind: String = "Watch_Widget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
Watch_WidgetEntryView(entry: entry)
.modelContainer(for: [Model1.self, Model2.self])
}
.configurationDisplayName("App")
.description("descriptions")
}
}
Here's the same model used in the widget view:
struct Watch_WidgetEntryView : View {
@Query var models: [Model1]
var body: some View {
let gaugeVal = models.count
Gauge(value: gaugeVal,
in: 0...60) {
Text("min")
} currentValueLabel: {
Text(String(Int(gaugeVal)))
}
.gaugeStyle(.accessoryCircular)
.widgetLabel {
Text("\(models.count) models")
}
.containerBackground(.fill.tertiary, for: .widget)
}
}
When I build the WidgetExtension, the following error was thrown and the CK data isn't loaded properly:
CloudKit setup failed because there is another instance of this persistent store actively syncing with CloudKit in this process.
Hi, I get this error Thread 1: Fatal error: Illegal attempt to use a nil as an Attribute - type + CustomType when trying to use SwiftData on one of my main views, I wasn't seeing this error when I was working with other data types but some reason this started happening all of a sudden. Any clues as to what it might be?
Hi everyone,
I'm new to programming and I've been experimenting with Apple's SwiftData. I've run into an issue I can't seem to resolve.
I'm creating a personal relationship manager app where I have a Relation model and an Interaction model. Relation has a one-to-many relationship with Interaction. I'm using SwiftData's @Model and @Relationship property wrappers to define these models and their relationship. I've taken inspiration from Apple's sample code, that can be found here:
Adopting SwiftData for a Core Data app
(WWDC23 Session: "Migrate to SwiftData")
The relevant parts of the models look something like this:
@Model
final class Relation {
...
@Relationship(.cascade, inverse: \Interaction.relation)
var interactions: [Interaction] = []
...
}
@Model
final class Interaction {
...
var relation: Relation?
...
}
In my SwiftUI view, I'm adding a new Interaction to a Relation like this:
private func AddItem() {
withAnimation {
let newInteraction = Interaction(...)
modelContext.insert(newInteraction)
newInteraction.relation = relation
relation.interactions.append(newInteraction)
}
}
When I add a new Interaction like this, everything seems to work fine during that app session. I can see the new Interaction in my app's UI. But when I quit the app and relaunch it, the new Interaction is gone. It's as if it was never saved.
I've double-checked my code and as far as I can tell, I'm using SwiftData correctly. My usage aligns with the sample code provided by Apple, and I'm not getting any errors or warnings. I think that this issue is not related to SwiftData being in Beta, because Apple's sample code works perfectly fine.
I have a few questions:
Is there something I'm missing about how to properly save models using SwiftData?
Is there a specific step or method I need to call to persist the changes to the Relation and Interaction objects?
Is there a way to debug what's going wrong when SwiftData attempts to save these changes?
Any help would be greatly appreciated.
Thank you in advance!
Louis
SwiftData #Predicate performs $0.string1 == string2 as case sensitive. Searching for Taylor Swift returns results, but not for taylor swift.
In Core Data we did this with ==[c]
How do we do this in SwiftData?
when I run my project, I get this error and the simulator open app and show blank page.
I don't understand because I have to add some stores after I entry the app. but now I cannot entry it.
I used VersionedSchema and SchemaMigrationPlan
In Xcode 15 beta 3 and iOS 17 beta 3, my app with SwiftData keeps generating error:
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<NSManagedObject 0x2811cb390> setValue:forUndefinedKey:]: the entity dataSets is not key value coding-compliant for the key "(null)".'
I have a model:
@Model final class Station: Sendable {
...
var dataSets: [String]
}
The error seems to occur when saving entities of the model in SwiftData. There is no entity named "dataSets". Only a property in the above model. What's the null key in the error?
In one of the WWDC 2023 videos a presenter said in passing that you can connect SwiftData to whatever complicated setup you have. I haven't been able to find any documentation or connection points for this though.
Obviously I can fetch the data myself and call context.insert(...) for every item, but what about refreshes, deletes, inserts, updates? Where can I wire up these external calls and their responses?
Thanks!