What is the best way to use swift data to store one off data models? For example an application state that you want to be persisted across application launches.
The only examples I've seen use arrays of items which wouldn't work for having just one application state.
Is it possible to query just one item?
Posts under wwdc2023-10154 tag
32 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
I did manage to save my Entities to CloudKit with SwiftData but the default database is the private database. I need to store some Entities in the private and other Entities in the public CloudKit database. How do I manage that with SwiftData? With CoreData I always used different configurations for both private and public and added the entities to one or the other.
Hi,
re: the SwiftData session "Create an app with SwifData" (https://developer.apple.com/videos/play/wwdc2023/10154)
I noted the mention of generating sample preview data. In CoreData, I had a similar need and this was achieved by creating a shared in-memory context; fairly straight forward.
The example given for SwiftData was decorated with @MainActor and then added to the target project, then in the #Preview closure, the modelContainer is amended to point to the preview data. Anyways, my problem is that I'm receiving an error when trying to render the preview in question:
main actor-isolated let 'previewContainer' can not be referenced from a non-isolated context
I suppose my issue is not having used the MainActor wrapper before and being unclear on what's possibly going wrong here (or if it's not just a "me" problem).
Can anyone help?
Did anyone successfully used transformable in SwiftData to store UIColor or SwiftUI Color type?
@Attribute(.transformable) var color: UIColor
All demo code from Apple that I know lacks view models.
Every time model data is directly injected into the view and even modified from the view via the new @Bindable macro.
Is Apple not using ViewModels and therefore MVVM at all?
I understand it might not really be required for the small demos, but it would still be helpful to understand, e. g. how the new @Model, @Observable, @Binding fit into the MVVM model.
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)
}
}
I was able to add a new card. I added 3 cards. When I clicked on second card or third card, it showed the first card only.
Must be some index issue with view. Please let me know. Thanks!
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()
}
}
Don't see a link to download the Xcode project, starter and complete?
How can I get it?
Thanks
Bob
Hello everyone!
I'm trying to run this project by Apple, but it doesn't let me, I'm trying to learn more about to swiftData...
https://developer.apple.com/documentation/coredata/adopting_swiftdata_for_a_core_data_app
On the code it says next:
`@MainActor #Preview {
AddBucketListItemView(trip: .preview)
.modelContainer(PreviewSampleData.container)
}
But it show me next error,
Type 'Trip' has no member 'preview'
anybody knows how can I solved, thank you.
When trying to delete the element from my list, I always got error in my model.
get {
_$observationRegistrar.access(self, keyPath: \.id)
return self.getValue(for: \.id) <-- ERROR: Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1a949aefc)
}
Because I am new in development, I don't know how to solve it.
@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()
}
}
Hello there,
I would like to bring attention to an issue I'm facing with SwiftData while attempting to create my own document type and a document-based app. It appears that there is a lack of support for imported types, and I'm unsure if this is a result of the beta version or an intentional omission.
To reproduce the problem, please follow these steps:
Create a document-based app with SwiftData enabled.
Define your document type as "test" in the info.plist file.
Include the "test" type in the list of exported type identifiers.
Add a commonly used type such as "pdf" or "txt" to the list of imported type identifiers.
Within the app's body, include a DocumentGroup with the content type set as "test."
Currently, the app is capable of opening and creating new documents with the "test" type. However, I am unable to open common file types, such as images or text files. This limitation is a significant concern since the app often needs to handle these general file formats rather than being limited to its specific format.
Thank you for your attention to this matter. I appreciate any assistance you can provide in resolving this issue.
Best regards,
Jeremy Vizzini
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
In the WWDC presentations, it was mentioned that you could have multiple modelContexts within one modelContainer. Does anyone know of any examples of a project with multiple modelContexts?
Specifically, I'm wondering how you distinguish between the two different modelContexts when doing things like @Query. I'd like to keep different sets of data in separate modelContexts and only Query the one that I want to pull from for a particular view.
Thanks in advance!
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?
I set my active schema to SwiftDataCardSampleEnd, open ContentView.swift, and bring up the Canvas. When I click the little refresh button I get the follow error from Canvas:
== DATE:
Friday, July 14, 2023 at 9:02:26 AM Eastern Daylight Time
2023-07-14T13:02:26Z
== PREVIEW UPDATE ERROR:
SchemeBuildError: Failed to build the scheme ”SwiftDataFlashCardSampleEnd”
linker command failed with exit code 1 (use -v to see invocation)
Link SwiftDataFlashCardSample (arm64):
ld: Undefined symbols:
unsafeMutableAddressor of self #1 : SwiftDataFlashCardSample.Card in SwiftDataFlashCardSample.Card.creationDate.init : Foundation.Date, referenced from:
SwiftDataFlashCardSample.Card.creationDate.init : Foundation.Date in Card.o
unsafeMutableAddressor of self #1 : SwiftDataFlashCardSample.Card in SwiftDataFlashCardSample.Card.back.init : Swift.String, referenced from:
SwiftDataFlashCardSample.Card.back.init : Swift.String in Card.o
unsafeMutableAddressor of self #1 : SwiftDataFlashCardSample.Card in SwiftDataFlashCardSample.Card.front.init : Swift.String, referenced from:
SwiftDataFlashCardSample.Card.front.init : Swift.String in Card.o
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I've made a similar preview in another project where I define a model and I try to use that model in a SwiftUI Canvas preview. I get a very similar error. Basically Canvas is saying it can't find the symbols for the properties on the type.
Is there something I'm missing here? Some hidden build setting I need to flip. Or is this just busted?
Hi all,
I am trying to render my SwiftUI views that uses SwiftData classes using sample data using the approach shown in the example code of wwdc2023-10196:
@MainActor #Preview {
TripsWidgetEntryView()
.modelContainer(PreviewSampleData.container)
}
Unfortunately this seems no longer valid.
Indeed I get this error:
I then tried to remove the @MainActor as suggested, but the error in then moved to another level:
What do you suggest to be the best approach to have back my preview working?
I am using Xcode Beta 4 - 15A5195m
I've noticed when using @Model instead of @Observable that it bumps the CPU-usage of the app with 10%, since I'm reading values on every frame of render. This seems to cause a deep lookup. Is there a way to read the objects/attributes in memory somehow?
Anyone know the SwiftData equivalet of the CoreData lifecycle methods. and how do we override them or do something similar? eg:
awakeFromInsert()
willSave()
didSave()
willTurnIntoFault()
prepareForDeletion()