Post not yet marked as solved
Hey there,
for my recent projects I used CoreData for persistence. One nice feature I use all the time is the ability to create a child context for editing operations.
When editing an entity in a form presented as a sheet, the changes made are only saved into the main context/parent context if the child context (I call this the editing context) is saved. Therefore dismissing an editing view results in destroying the child context and all the changes are discarded. Likewise when saving in the editing view, the changes will be proceeded to the parent context and persisted.
Is there any possibility to get the same or equivalent behavior when using SwiftData. Or is there another way to model this kind of cases?
All the best from Cologne, Germany!
Post not yet marked as solved
Is there a way to use SwiftData without automatic iCloud sync? I’d like to do that manually using my own CloudKit solution or CKSyncEngine. SwiftData automatically picks up any CloudKit containers though and I have not seen an option to disable this behavior. Setting cloudKitContainerIdentifier to nil does still pick the first available CloudKit container.
Just in case this is a bug: FB12276416
Sample:
let configuration = ModelConfiguration(cloudKitContainerIdentifier: nil)
let modelContainer = try! ModelContainer(for: [GamesCollection.self], configuration)
print(modelContainer.configurations)
// [SwiftData.ModelConfiguration(url: …, name: …, sharedAppContainerIdentifier: …, cloudKitContainerIdentifier: Optional("iCloud.CloudKit.com.+++")
Post not yet marked as solved
Just for grins, I tried running the SwiftData generation tool on the existing Core Data model of a non-trivial app I've worked on for a decade or so. It's pretty substantial, and uses some more advanced features, so it seemed like an interesting test case.
One of the warnings that was not reported by the UI, but which showed up in the generated code was quite a few instances of this:
Entity inheritance on entity Bar (parent class Foo) is unsupported in SwiftData.
Now that I'm looking at the code examples more carefully, I see an awful lot of final class, which maybe should have raised a red flag sooner. But to the best of my recollection, none of the sessions outright said that inheritance (was or) was not supported. Core Data has supported this functionality for a long time (maybe since the beginning).
Assuming that this isn't supported in this first seed, are there plans to provide this functionality in the future? By the launch of iOS 17?
(For the record, this model has 93 entities, of which 34 have parent entities. 14 are abstract, which I gather is also not supported. 3 of the entities are both abstract and have parent entities.)
Post not yet marked as solved
Hi,
in the session the following is mentioned:
If a trip already exists with that name, then the persistent back end will update to the latest values. This is called an upsert. An upsert starts as an insert. If the insert collides with existing data, it becomes an update and updates the properties of the existing data.
Nevertheless, if I have a unique constraint on an (String) attribute and try to insert the same again, I end up in the debugger in the generated getter of the attribute:
@Attribute(.unique) public var name: String
{
get {
_$observationRegistrar.access(self, keyPath: \.name)
return self.getValue(for: \.name) // <- here
}
EXC_BREAKPOINT (code=1, subcode=0x1a8d6b724)
Am I missing something? If this is expected behaviour, how should I prevent this crash (other than checking for uniqueness before every insert)?
Thank you!
Cheers, Michael
Post not yet marked as solved
Did anyone successfully used transformable in SwiftData to store UIColor or SwiftUI Color type?
@Attribute(.transformable) var color: UIColor
Post not yet marked as solved
Hey,
I am trying to save an enum in my model with SwiftData but getting a weird error message I do not understand at the moment, and I am pretty sure I am missing something here.
public enum URLScheme: String, Codable {
case https
case http
}
@Model
public class MyModel {
public var urlScheme: URLScheme
}
When I try to save the model I get the following error message in the console:
error: Row (pk = 1) for entity 'MyModel' is missing mandatory text data for property 'https'
I was wondering if I need to tell SwiftData how to save my enum ? My assumption was that I can save any enum if it conforms to Codable. Am I doing something wrong here or is this a beta bug ?
Thanks a lot for helping
Post not yet marked as solved
I am trying to create a document-based (macOS) app using SwiftUI and SwiftData. I'm trying to set up XCTests to validate my model behaviors.
Clearly, I need to set up a container in my test class for my model objects. Failing to do so shows that SwiftData is trying: my app launches into the New Document dialog; if I dismiss it, the app fails with a "failed to find an active container" for my model object.
Any pointer on setting up a container and context within XCTest is welcome. TIA
Post not yet marked as solved
The 'unique' attribute is a really nice feature, BUT. In some of my apps, the unique identifier for an object is a combination of multiple attributes. (Example: a book title is not unique, but a combination of book title and author list is.)
How do I model this with SwiftData? I cannot use @Attribute(.unique) on either the title OR the author list, but I want SwiftData to provide the same "insert or update" logic.
Is this possible?
Post not yet marked as solved
I may not be understanding this right, but when trying to use the new @Environment for what was previously @EnvironmentObject I am unable to use a NavigationPath in a NavigationStack:
@Observable
class AppData {
var presented: NavigationPath = NavigationPath()
}
NavigationStack(path: appData.$presented) {...
Value of type 'AppData' has no member '$presented'
Cannot convert value of type 'NavigationPath' to expected argument type 'Binding'
Moving the $ to the front or removing it does nothing either.
Post not yet marked as solved
I can't find the method to create ModelContainer that appears in the video at 8: 52.
The code in the video is as follows:
let container = ModelContainer(
for: Trip.self,
migrationPlan: SampleTripsMigrationPlan.self
)
This method doesn't seem to exist in Xcode15.
I found other method to create ModelContainer using Schema and Schema MigrationPlan in Xcode15.
public convenience init(for givenSchema: Schema, migrationPlan: (SchemaMigrationPlan.Type)? = nil, _ configurations: ModelConfiguration...) throws
And I tried to create a Schema to use this method, like this:
let container = try! ModelContainer(for: .init([Person.self]), migrationPlan: MigrationPlan.self)
But an error occurred during runtime
SwiftData/ModelContext.swift:177: Fatal error: Container does not have any data stores
SwiftDataDemoApp.swift
How to create a ModelContainer with [PersistentModel] and SchemaMigrationPlan ?
Xcode Version: 15.0 beta (15A5160n)
MacOS: 13.3.1 (a) (22E772610a)
Post not yet marked as solved
I am trying to use SwiftData to perform a Query based on the name of the actor, which is inside a movie model. Here is the Movie model.
@Model
final class Movie {
var title: String
var year: Int
@Relationship(.noAction, inverse: \Actor.movies)
var actors: [Actor] = []
init(title: String, year: Int) {
self.title = title
self.year = year
}
}
Here is my actual Query:
_movies = Query(filter: #Predicate { $0.actors.contains(where: { $0.name.contains(actorName) }) })
But it returns nothing, even though I am passing actorName which exists in the movie.
Post not yet marked as solved
Is it possible to either query a single instance or instruct SwiftData that there should only be a single instance in my model?
I have the following basic structure in a lot of my views to query the first element and use it to display data. There is always only one instance, it is by design.
@Query private var waterData: [WaterData]
var body: some View {
if let data = waterData.first {
EmptyView()
} else {
ContentUnavailableView("Content unavailable", systemImage: "xmark.circle")
}
}
Is there a way to reduce the boilerplate of if let data = ... in each view?
Post not yet marked as solved
When I update a variable inside my model that is marked @Transient, my view does not update with this change. Is this normal? If I update a non-transient variable inside the model at the same time that I update the transient one, then both changes are propagated to my view.
Here is an example of the model:
@Model public class WaterData {
public var target: Double = 3000
@Transient public var samples: [HKQuantitySample] = []
}
Updating samples only does not propagate to my view.
Post not yet marked as solved
In the "old" world we could define a model property as an integer (e.g. "Integer 64") and then have the actual class representing that model define the property as an enum instead and have getters/setters using methods such as primitiveValue(forKey:)
When migrating to use SwiftData I changed the model to be an actual enum type and even though the underlying type of the enum is an integer and therefore supported by the primitive storage it requires me to use Codable on the enum instead and doing so makes it incompatible with the old CoreData model.
Does anyone have any ideas or workarounds here or do you feel it is a bug and should be reported?
enum FooType: Int64 {
case awesome
case super
}
@Model
final class Note {
var type: FooType // ERROR
}
Post not yet marked as solved
How do I delete all the rows in an entity and/or delete the entity all together in SwiftData?
Here is a function I used to delete an entity in core data for reference.
class func deleteEntity(entity: String) {
let fetchRequest1: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: entity)//entity.fetchRequest()
let batchDeleteRequest1 = NSBatchDeleteRequest(fetchRequest: fetchRequest1)
_ = try? PersistanceController.shared.container.viewContext.execute(batchDeleteRequest1)
print("Deleting coredata entity: \(entity)")
}
Post not yet marked as solved
I used Schema and MigrationPlan to initialize the Model Container.
SwiftDataDemoApp.swift
Then I modified the Schema and created a MigrationStage through the custom method, which was configured in the stages array of the SchemaMigrationPlan.
Models.swift
But when I run the app, I don't receive the willMigrate and didMigrate callbacks
Xcode Version: 15.0 beta (15A5160n)
MacOS: 13.3.1 (a) (22E772610a)
Post not yet marked as solved
In CoreData with CloudKit mirroring, only lightweight migration is allowed to database migration. Heavyweight (custom) migration are not recommended.
Are the rules also applied to SwiftData with CloudKit mirroring? (I assume it is true, because cloudkit backend properties are not changed even if SwfitData released)
Overview
I have 2 models: Deparment and Student
Each Department can contain multiple students
Each Student can only be in one Department
I have DepartmentList, tapping on the department should take it to the StudentList which lists all students in the department
Problem
When I use Query in StudentList to filter only students for a specific department id, no students are shown.
Questions:
What should I do to list the students in a department? (see complete code below).
let filter = #Predicate<Student> { student in
student.department?.id == departmentID
}
let query = Query(filter: filter, sort: \.name)
_students = query
Complete code
App
@main
struct SchoolApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for: [Department.self, Student.self])
}
}
Department
import Foundation
import SwiftData
@Model
class Department {
var id: UUID
var name: String
var students: [Student]
init(
id: UUID,
name: String,
students: [Student] = []
) {
self.id = id
self.name = name
self.students = students
}
}
Student
import Foundation
import SwiftData
@Model
class Student {
var id: UUID
var name: String
@Relationship(inverse: \Department.students)
var department: Department?
init(
id: UUID,
name: String,
department: Department? = nil
) {
self.id = id
self.name = name
self.department = department
}
}
ContentView
import SwiftUI
struct ContentView: View {
@State private var selectedDepartment: Department?
var body: some View {
NavigationSplitView {
DepartmentList(selectedDepartment: $selectedDepartment)
} detail: {
if let department = selectedDepartment {
StudentList(department: department)
} else {
Text("no department selected")
}
}
.task {
printStoreFilePath()
}
}
private func printStoreFilePath() {
let urls = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
if let path = urls.map({ $0.path(percentEncoded: false) }).first {
print("Storage: \(path)")
}
}
}
DepartmentList
import SwiftUI
import SwiftData
struct DepartmentList: View {
@Binding
var selectedDepartment: Department?
@Query(sort: \.name)
private var departments: [Department]
@Environment(\.modelContext)
private var modelContext
var body: some View {
List(selection: $selectedDepartment) {
ForEach(departments) { department in
NavigationLink(value: department) {
Text(department.name)
}
}
}
.toolbar {
ToolbarItem {
Button {
addDepartment()
} label: {
Label("Add", systemImage: "plus")
}
}
}
}
private func addDepartment() {
guard let index = (1000..<10000).randomElement() else {
return
}
let department = Department(id: UUID(), name: "Department \(index)")
modelContext.insert(department)
}
}
StudentList
import SwiftUI
import SwiftData
struct StudentList: View {
var department: Department
@Query
private var students: [Student]
@Environment(\.modelContext)
private var modelContext
init(department: Department) {
self.department = department
let departmentID = department.id
let filter = #Predicate<Student> { student in
student.department?.id == departmentID
}
let query = Query(filter: filter, sort: \.name)
_students = query
}
var body: some View {
List {
ForEach(students) { student in
Text(student.name)
}
}
.toolbar {
ToolbarItem {
Button {
addStudent()
} label: {
Label("Add", systemImage: "plus")
}
}
}
}
private func addStudent() {
guard let index = (1000..<10000).randomElement() else {
return
}
let student = Student(
id: UUID(),
name: "Student \(index)",
department: department
)
modelContext.insert(student)
}
}
Post not yet marked as solved
Hi, does anyone know how to get @Relationship in SwiftData to cascade to the CloudKit container? I've managed to get SwiftData classes to show up in CloudKit but they are not related in line with the @Relationship as per the example project:
@Relationship(.cascade)
var bucketListItem: [BucketListItem] = []`
They are simply separate records. As result the cascade of deletes doesn't work.
Any ideas?
Post not yet marked as solved
SwiftData provides a mechanism to periodically implicitly save a ModelContext to persistent storage.
This is an operation that could fail, as evidenced by the explicit save() method being marked as throws.
But what happens if the context attempts to save automatically, and that operation fails? Is there any mechanism for notifying the app? Any way for the app to recognize that it has happened, and to attempt to correct the situation? Presumably, it doesn't just crash (and lose the user's unsaved changes…).