I am working on a social media app called "Aura Tracker" where people can add/subtract aura from people based off of their actions. I am new to app development so I don't know how to handle user accounts and aura value changing.
Content view code:
import SwiftUI
struct ContentView: View {
var body: some View {
ScrollView{
VStack {
Text("Aura Tracker")
Image(systemName: "sparkles")
.imageScale(.large)
.foregroundStyle(.yellow)
Text("You have "+"#"+" aura")
}
}
}
}
#Preview {
ContentView()
}
Aura_TrackerApp code:
import SwiftUI
@main
struct Aura_TrackerApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
iCloud & Data
RSS for tagLearn how to integrate your app with iCloud and data frameworks for effective data storage
Post
Replies
Boosts
Views
Activity
I'm experiencing a new error in SwiftData since updating to Xcode 16/iOS 17 DB1. When passing in a model (Student) to a view and then displaying an array of Points using ForEach, I get the following fatal error:
SwiftData/ModelCoders.swift:2438: Fatal error: Failed to locate relationship for StringCodingKey(stringValue: "group", intValue: nil) on Entity - name: Point
superentity:
subentities:
storedProperties:
CompositeAttribute - name: type, options: [], valueType: PointType, defaultValue: nil
Properties:
Attribute - name: type, options: [], valueType: String, defaultValue: nil, hashModifier: nil
Relationship - name: outcome, options: [], valueType: Outcome, destination: Outcome, inverseName: nil, inverseKeypath: nil
CompositeAttribute - name: proficiency, options: [], valueType: Proficiency, defaultValue: nil
Properties:
Attribute - name: proficiency, options: [], valueType: String, defaultValue: nil, hashModifier: nil
Attribute - name: date, options: [], valueType: Date, defaultValue: nil, hashModifier: nil
Attribute - name: note, options: [], valueType: String, defaultValue: nil, hashModifier: nil
Relationship - name: student, options: [], valueType: Optional<Student>, destination: Student, inverseName: points, inverseKeypath: Optional(\Student.points)
Attribute - name: group, options: [], valueType: Array<PersistentIdentifier>, defaultValue: [], hashModifier: nil
inheritedProperties:
uniquenessConstraints:
indices:
Xcode flags this line of the macro-generated getter of the outcome property on Point:
@storageRestrictions(accesses: _$backingData, initializes: _outcome)
init(initialValue) {
_$backingData.setValue(forKey: \.outcome, to: initialValue)
_outcome = _SwiftDataNoType()
}
get {
_$observationRegistrar.access(self, keyPath: \.outcome)
return self.getValue(forKey: \.outcome) // Fatal error: Failed to locate relationship for StringCodingKey...
}
set {
_$observationRegistrar.withMutation(of: self, keyPath: \.outcome) {
self.setValue(forKey: \.outcome, to: newValue)
}
}
This worked just fine in iOS 17. Here's a snippet of the Student implementation:
@Model
class Student: Identifiable, Comparable {
var name: String
var number: Int
@Relationship(deleteRule: .cascade, inverse: \Point.student) var points: [Point]
@Relationship(deleteRule: .cascade, inverse: \Mark.student) var marks: [Mark]
@Relationship(deleteRule: .nullify, inverse: \StudentGroup.students) var groups: [StudentGroup] = []
var archived: Bool
}
and the implementation of Point:
@Model
class Point: Identifiable, Comparable {
var student: Student?
var type: PointType
var outcome: Outcome
var proficiency: Proficiency
var group: [Student.ID] = []
var date: Date
var note: String
}
and finally the implementation for Outcome:
@Model
class Outcome: Identifiable, Comparable {
var name: String
var index: Int
var rubric: Rubric?
var proficiencies: [Proficiency]
}
I've tried adding a relationship in Outcome as an inverse of the outcomes property on Points (although this does not make sense in my implementation, which is why I initially did not set a relationship here) and the problem persisted.
Any ideas what this error means and how I might go about fixing it?
After watching the WWDC video on the new history tracking in SwiftData, I started to update my app with this functionality. Unfortunately it seems that the current API in the first beta of Xcode 16 is rather useless in regards to tombstone data.
The docs state that it would be possible to get the data from the tombstone by using a keyPath, there is no API for this however. The only thing I can do is iterate over the values (of type any) without any key information, so I do not know which data is what.
Am I missing something or did we get a half finished implementation? There also does not seem to be any info on this in the release notes.
I'm trying to insert values into my SwiftData container but it crashes on insert context.insert(fhirObject) and the only error I get from Xcode is:
@Transient
private var _hkID: _SwiftDataNoType?
// original-source-range: /Users/cyril/Documents/GitHub/MyApp/MyApp/HealthKit/FHIR/FHIRModels.swift:35:20-35:20
configured as follows:
import SwiftData
import SwiftUI
@main
struct MyApp: App {
var container: ModelContainer
init() {
do {
let config = ModelConfiguration(cloudKitDatabase: .private("iCloud.com.author.MyApp"))
container = try ModelContainer(for: FHIRObservation.self, configurations: config)
UserData.shared = UserData(modelContainer: container)
} catch {
fatalError("Failed to configure SwiftData container.")
}
}
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(UserData.shared)
}
.modelContainer(container)
}
}
My UserData and DataStore are configured as follows:
import SwiftUI
import SwiftData
import os
/// `FHIRDataStore` is an actor responsible for updating the SwiftData db as needed on the background thread.
private actor FHIRDataStore {
let logger = Logger(
subsystem:
"com.author.MyApp.FHIRDataStore",
category: "ModelIO")
private let container: ModelContainer
init(container: ModelContainer) {
self.container = container
}
func update(newObservations: [FHIRObservationWrapper], deletedObservations: [UUID]) async throws {
let context = ModelContext(container)
// [FHIRObservationWrapper] -> [FHIRObservation]
// let modelUpdates = newObservations.lazy.map { sample in
// FHIRObservation(hkID: sample.hkID, fhirID: sample.fhirID, name: sample.name, status: sample.status, code: sample.code, value: sample.value, range: sample.range, lastUpdated: sample.lastUpdated)
// }
do {
try context.transaction {
for sample in newObservations {
let fhirObject = FHIRObservation(hkID: sample.hkID, fhirID: sample.fhirID, name: sample.name, status: sample.status, code: sample.code, value: sample.value, range: sample.range, lastUpdated: sample.lastUpdated)
// App crashes here
context.insert(fhirObject)
}
// for obj in modelUpdates {
//
// }
// try context.delete(model: FHIRObservation.self, where: #Predicate { sample in
// deletedObservations.contains(sample.hkID!)
// })
// try context.save()
logger.debug("Database updated successfully with new and deleted observations.")
}
} catch {
logger.debug("Catch me bro: \(error.localizedDescription)")
}
}
}
@MainActor
public class UserData: ObservableObject {
let userDataLogger = Logger(
subsystem:
"com.author.MyApp.UserData",
category: "Model")
public static var shared: UserData!
// MARK: - Properties
public lazy var healthKitManager = HKManager(withModel: self)
let modelContainer: ModelContainer
private var store: FHIRDataStore
init(modelContainer: ModelContainer) {
self.modelContainer = modelContainer
self.store = FHIRDataStore(container: modelContainer)
}
// MARK: - Methods
func updateObservations(newObservations: [FHIRObservationWrapper], deletedObservations: [UUID]) {
Task {
do {
try await store.update(newObservations: newObservations, deletedObservations: deletedObservations)
userDataLogger.debug("Observations updated successfully.")
} catch {
userDataLogger.error("Failed to update observations: \(error.localizedDescription)")
}
}
}
}
My @Model is configured as follows:
public struct FHIRObservationWrapper: Sendable {
let hkID: UUID
let fhirID: String
var name: String
var status: String
var code: FHIRCodeableConcept
var value: FHIRLabValueType
var range: FHIRLabRange?
var lastUpdated: Date?
}
@Model
public final class FHIRObservation {
let hkID: UUID?
let fhirID: String?
@Attribute(.allowsCloudEncryption) var name: String?
@Attribute(.allowsCloudEncryption) var status: String?
@Attribute(.allowsCloudEncryption) var code: FHIRCodeableConcept?
@Attribute(.allowsCloudEncryption) var value: FHIRLabValueType?
@Attribute(.allowsCloudEncryption) var range: FHIRLabRange?
@Attribute(.allowsCloudEncryption) var lastUpdated: Date?
init(hkID: UUID?, fhirID: String?, name: String? = nil, status: String? = nil, code: FHIRCodeableConcept? = nil, value: FHIRLabValueType? = nil, range: FHIRLabRange? = nil, lastUpdated: Date? = nil) {
self.hkID = hkID
self.fhirID = fhirID
self.name = name
self.status = status
self.code = code
self.value = value
self.range = range
self.lastUpdated = lastUpdated
}
}
Any help would be greatly appreciated!
Hello
I have a swiftUI/swiftData document based app. In this I have created a singleton object using the folllowing in my ContentView struct:
@Query private var persistantStores:[PersistantStateManagerStorage]
@State private var stateManager: PersistantStateManagerStorage?
I then, call the following on the onAppear and onDisappear calls on the top level HStack{} item in my view
.onAppear(){
if let store = persistantStores.first
{
self.stateManager = store // data.first
} else
{
self.stateManager = PersistantStateManagerStorage()
modelContext.insert(stateManager!)
try? modelContext.save()
}
}.onDisappear(){
print("bye bye")
self.stateManager = nil
}
I then wrap my view inside a
if let stateManager = stateManager {}
block to unwrap the optional, and bind to values with a call such as
Stepper("Nudge Amount", value: Bindable(stateManager).nudgeAmount, in: 1...20)
I have to use Bindable(stateManager) rather than $stateManager as the $stateManager doesn't know its been unwrapped
All of this works fine until I close a document, at which point I get a crash with the message "SwiftData/BackingData.swift:124: Fatal error: Unable to get value - no backing Managed Object" which appears to happen when trying to access some of the properties of my persistantStore object (which has been retired)
I assume that my persistantStore object being an optional is part of the problem but I can't work out a better method of doing what I am trying to do
I have a document app built using SwiftData because frankly I'm too lazy to learn how to use FileDocument. The app's title is "Artsheets," and I'm using a document type that my app owns: com.wannafedor4.ArtsheetsDoc. The exported type identifier has these values:
Description: Artsheets Document
Identifier: com.wannafedor4.ArtsheetsDoc
Conforms to: com.apple.package
Reference URL: (none)
Extensions: artsheets
MIME Types: (none)
And the code:
ArtsheetsApp.swift
import SwiftUI
import SwiftData
@main
struct ArtsheetsApp: App {
var body: some Scene {
DocumentGroup(editing: Sheet.self, contentType: .package) {
EditorView()
}
}
}
Document.swift
import SwiftUI
import SwiftData
import UniformTypeIdentifiers
@Model
final class Sheet {
var titleKey: String
@Relationship(deleteRule: .cascade) var columns: [Column]
init(titleKey: String, columns: [Column]) {
self.titleKey = titleKey
self.columns = columns
}
}
@Model
final class Column: Identifiable {
var titlekey: String
var text: [String]
init(titlekey: String, text: [String]) {
self.titlekey = titlekey
self.text = text
}
}
extension UTType {
static var artsheetsDoc = UTType(exportedAs: "com.wannafedor4.artsheetsDoc")
}
I compiling for my iPhone 13 works, but then when creating a document I get this error:
Failed to create document. Error: Error Domain=com.apple.DocumentManager Code=2 "No location available to save “Untitled”." UserInfo={NSLocalizedDescription=No location available to save “Untitled”., NSLocalizedRecoverySuggestion=Enable at least one location to be able to save documents.}
Question:
Hello,
I'm encountering an issue with SwiftData in Xcode. Despite setting up my model classes correctly, Xcode is suggesting additional boilerplate code for handling backing data which I believe should not be necessary. Here are the details:
Context:
I'm working with SwiftData to persist my data models. I've set up my model container and schema correctly, and I'm only persisting final classes. However, Xcode is suggesting the following code for one of my model classes (LoanAccount):
swift
Copy code
@Transient
private var _$backingData: any SwiftData.BackingData = LoanAccount.createBackingData()
public var persistentBackingData: any SwiftData.BackingData {
get {
_$backingData
}
set {
_$backingData = newValue
}
}
static var schemaMetadata: [SwiftData.Schema.PropertyMetadata] {
return [
SwiftData.Schema.PropertyMetadata(name: "loanName", keypath: \LoanAccount.loanName, defaultValue: nil, metadata: nil),
SwiftData.Schema.PropertyMetadata(name: "outstandingBalance", keypath: \LoanAccount.outstandingBalance, defaultValue: nil, metadata: nil),
SwiftData.Schema.PropertyMetadata(name: "currentAssetValue", keypath: \LoanAccount.currentAssetValue, defaultValue: nil, metadata: nil),
SwiftData.Schema.PropertyMetadata(name: "securedAssets", keypath: \LoanAccount.securedAssets, defaultValue: [], metadata: nil)
]
}
required init(backingData: any SwiftData.BackingData) {
_loanName = _SwiftDataNoType()
_outstandingBalance = _SwiftDataNoType()
_currentAssetValue = _SwiftDataNoType()
_securedAssets = _SwiftDataNoType()
self.persistentBackingData = backingData
}
@Transient
private let _$observationRegistrar = Observation.ObservationRegistrar()
struct _SwiftDataNoType {
}
My Model Setup:
Here's a brief overview of my model setup:
swift
Copy code
import Foundation
import SwiftData
@Model
class LoanAccount: LiabilityAccount {
var loanName: String
var outstandingBalance: Double?
var currentAssetValue: Double?
var securedAssets: [SecuredAsset] = []
required init(
id: UUID = UUID(),
institutionName: String,
accountName: String,
accountBalance: Double = 0,
accountOwner: String,
country: String = "UK",
accountCurrency: String,
risk: Int = 1,
accountStatus: String = "Active",
startDate: Date = Date(),
maturityDate: Date = Date(),
dateCreated: Date = Date(),
dateUpdated: Date = Date(),
addressline1: String? = nil,
addressline2: String? = nil,
county: String? = nil,
zipcode: String? = nil,
phoneNumber: String? = nil,
email: String? = nil,
contact1: String? = nil,
contact2: String? = nil,
link: String? = nil,
notes: String? = nil,
accountNumber: String? = nil,
sortCode: String? = nil,
accountFee: Double? = nil,
interestRate: Double? = nil,
loanName: String,
outstandingBalance: Double? = nil,
currentAssetValue: Double? = nil,
securedAssets: [SecuredAsset] = [],
activities: [AccountActivity] = []
) {
self.loanName = loanName
self.outstandingBalance = outstandingBalance
self.currentAssetValue = currentAssetValue
self.securedAssets = securedAssets
super.init(
id: id,
institutionName: institutionName,
accountName: accountName,
accountType: "Loan",
icon: "default_icon",
accountOwner: accountOwner,
country: country,
accountCurrency: accountCurrency,
risk: risk,
accountStatus: accountStatus,
startDate: startDate,
maturityDate: maturityDate,
dateCreated: dateCreated,
dateUpdated: dateUpdated,
addressline1: addressline1,
addressline2: addressline2,
county: county,
zipcode: zipcode,
phoneNumber: phoneNumber,
email: email,
contact1: contact1,
contact2: contact2,
link: link,
notes: notes,
accountNumber: accountNumber,
sortCode: sortCode,
accountFee: accountFee,
interestRate: interestRate,
accountBalance: accountBalance,
activities: activities
)
}
}
Issue:
Xcode is suggesting that I need to add the @Transient backing data code, even though my understanding is that this should be handled automatically by SwiftData when using the @Model attribute.
Request:
Can anyone provide insight into why Xcode is suggesting this code and if there's a configuration or setup step I might be missing? I want to ensure my data models are set up correctly without needing unnecessary boilerplate code.
Thank you!
Using an @FetchRequest in my SwiftUI-redesigned app, I was looking to improve performance of my search and filtering on a List that has potentially a large amount of data. One of the things I was asking about was how to load some data initially and then fetch additional objects as the user scrolls.
In a WWDC lab today, while viewing my code the Apple engineer noted I had set a .fetchLimit on my FetchRequest, and suggested that I should be setting a .fetchBatchSize. It sounded like that was designed to do exactly what I wanted.
However, having attempted this, I can see from the console output that when I set a .fetchBatchSize of 50 and go to the view, it immediately loops through the entire collection of thousands of objects, attempting to load them into memory, and as some contain images this eventually crashes with an out of memory error.
This data is presented in a List. Originally, inside that list there was a ForEach inside that list but the same problem was seen when I removed the ForEach and used:
List(entries) { entry in
if let entryData = viewModel.entryData(for: entry) {
TimelineEntryView(entryData: entryData)
}
}
Can you advise on what might be going on here? I recall that in the previous iteration of my app, in UIKit and using an NSFetchedResultsController I had the exact same symptom: setting a fetch batch size immediately traversed the whole collection in the datastore, leading to terrible performance rather than the hoped-for good performance. So I did not set a batch size then either.
Am I holding this wrong? Is there something that might be triggering this?
When trying to run my document-based iPad app using iPadOS 18 beta and Xcode 16 beta, I get an error like the following after opening a document:
Thread 1: Fatal error: Failed to identify a store that can hold instances of SwiftData._KKMDBackingData<MyProject.MyModel> from [:]
In order to help track down what is going wrong, I downloaded the sample app from WWDC23 session "Build an app with SwiftData" found here: https://developer.apple.com/documentation/swiftui/building-a-document-based-app-using-swiftdata
When I try to run the end-state of that sample code, I get a similar error when running the app on my iPad and creating a new deck:
Thread 1: Fatal error: Failed to identify a store that can hold instances of SwiftData._KKMDBackingData<SwiftDataFlashCardSample.Card> from [:]
Given that the sample project is generating the same error as my own project, is this a problem with SwiftData and document-based apps in general? Or is there a change of approach that I should try?
In Core Data, you can use a pinned query generation to make sure that your app is working from a consistent view of the data store. If you have CloudKit sync turned on, and new changes come in that invalidate relationships, your app won't see them right away as long as it's looking at a pinned query generation.
Since Swift Data doesn't yet support query generations, how do I deal with this issue in Swift Data apps?
For example, let's say I have an address book app. I open a particular contact, and then tap a control on the screen that opens a list of images for that contact. While looking at the images, CloudKit sync retrieves changes made by other devices, which have completely removed the parent contact.
How does my app know this has happened? Suppose the image browser screen needs to refer to the parent contact, or make changes to it, but the contact is no longer there because a background sync removed it.
I am currently working on an app that utilizes SwiftData, and I am planning to integrate CloudKit. My current challenge involves managing a Day object that gets inserted when the date changes to track daily progress. Additionally, I want to prompt users to create a username during the app's onboarding process. I am concerned that this could lead to synchronization issues, such as duplicate entries for the same day or multiple user accounts being created. I assume blocking the app until everything is synced from CloudKit is not a practical solution. Does anyone have any recommendations on how to handle this?
Furthermore, I would like to implement a community feature where users can post content. I noticed that CloudKit offers a public database. Is this database suitable for such a feature, or would I need to develop a separate backend to support it?
Recently, I noticed many crashes reported daily for context.save() in my code. I tried to resolve the issue by refactoring some of the code. My key change is to use context.performAndWait to fetch Core Data objects instead of passing them around.
I refactored the code to pass NSManagedObjectID around instead of NSManagedObject itself because NSManagedObjectID is thread-safe.
However, even after the refactor, the issue is still. The crash log is attached as below.
I'm confused that it doesn't crash on all devices so I have no clue to find the pattern of the crashes. I also enabled -com.apple.CoreData.ConcurrencyDebug 1 on my local dev box, but seems the error cannot be caught.
The new code (after I refactored) still causes the crash:
private func update(movements: [InferredMovement], from visitOutObjectId: NSManagedObjectID, to visitInObjectId: NSManagedObjectID?) {
let context = PersistenceController.shared.container.viewContext
context.performAndWait {
guard let visitOut = try? context.existingObject(with: visitOutObjectId) as? Visit else {
return
}
var visitIn: Visit?
if let visitInObjectId {
visitIn = try? context.existingObject(with: visitInObjectId) as? Visit
}
visitOut.movementsOut.forEach { context.delete($0) }
visitIn?.movementsIn.forEach { context.delete($0) }
for inferred in movements {
let movement = Movement(context: context)
movement.type = inferred.type
movement.interval = inferred.interval
movement.visitFrom_ = visitOut
movement.visitTo_ = visitIn
}
visitOut.objectWillChange.send()
context.saveIfNeeded()
}
}
Note, saveIfNeeded() is an extension function implemented as:
extension NSManagedObjectContext {
/// Only performs a save if there are changes to commit.
@discardableResult
public func saveIfNeeded() -> Error? {
guard hasChanges else {
return nil
}
do {
try save()
return nil
} catch {
defaultLogger.error("Core Data Error: Failed to save context")
return error
}
}
}
Crash context
I can't find the current costs for CloudKit. There used to be a pricing page, but I can't locate it now. Is there an official page that details the current costs for CloudKit?
How do I clear my system data on my macbook as it is taking up a huge portion of my storage?
I have already tried checking if my cache folder in ~/library was too big but it is only 7.42 GB. Also, I checked if time machine was on to find snapshots but it seems I have had it off for a while now and I didn't know. Could this be causing issues and should I have it on from now on? Please help I need more storage.
I’m trying to sync data from the AppData on one device to another device with the same iCloud. It uploads the data to the CloudKit but it doesn’t write the data it fetches from the cloud into the AppData storage.
It should store a timestamp, a few integers, and two lists of integers. The lists are both optional and store numbers. They can have 0 up to 50 numbers in them.
This is the part where it should fetch the records and store them in the AppData
let container = CKContainer(identifier: "iCloud.com.calchunt")
let privateDatabase = container.privateCloudDatabase
let dispatchGroup = DispatchGroup()
var errors: [Error] = []
// Leere Liste zum Speichern neuer Spielsitzungen aus der Cloud
var newGameSessions: [AppModule.GameSession] = []
for gameSession in gameSessions {
let recordIDString = gameSession.id.uuidString // UUID zu String umwandeln
let recordID = CKRecord.ID(recordName: recordIDString)
dispatchGroup.enter()
privateDatabase.fetch(withRecordID: recordID) { (existingRecord, error) in
defer { dispatchGroup.leave() }
if let error = error {
// Fehler beim Abrufen des Records
print("Fehler beim Abrufen des Records aus CloudKit: \(error.localizedDescription)")
errors.append(error)
} else if let existingRecord = existingRecord {
// Record existiert in der Cloud
print("Record mit ID \(existingRecord.recordID.recordName) existiert in CloudKit")
// à berprüfen, ob der Record bereits im AppModule vorhanden ist
if let _ = gameSessions.firstIndex(where: { $0.id == gameSession.id }) {
// Record existiert bereits im AppModule, überspringe das Speichern
print("Record mit ID \(existingRecord.recordID.recordName) existiert bereits im AppModule, überspringe das Speichern")
} else {
// Record existiert nicht im AppModule, füge ihn zur Liste der neuen Spielsitzungen hinzu
let newGameSession = AppModule.GameSession(
id: gameSession.id,
losungszahl: gameSession.losungszahl,
elapsedTime: gameSession.elapsedTime,
currentDate: gameSession.currentDate,
skipped: gameSession.skipped,
skipped2: gameSession.skipped2,
level: gameSession.level
)
newGameSessions.append(newGameSession)
print("Record mit ID \(existingRecord.recordID.recordName) wird zum AppModule hinzugefügt")
}
} else {
// Record existiert nicht in der Cloud
print("Record mit ID \(recordID.recordName) existiert nicht in CloudKit")
}
}
}
dispatchGroup.notify(queue: .main) {
if !errors.isEmpty {
for error in errors {
print("Fehler beim Abrufen der Daten aus CloudKit: \(error.localizedDescription)")
}
} else {
// Füge neue Spielsitzungen zum AppModule hinzu
gameSessions.append(contentsOf: newGameSessions)
// Speichere die aktualisierten Daten im AppStorage
do {
let encoder = JSONEncoder()
let gameSessionsData = try encoder.encode(gameSessions)
GameSessions = gameSessionsData
print("Daten erfolgreich aus CloudKit geladen und im AppStorage gespeichert")
} catch {
print("Fehler beim Codieren und Speichern der Daten im AppStorage: \(error.localizedDescription)")
}
}
}
}
In any SwiftData project with undo enabled, I noticed that repeatedly undoing and redoing a change will cause memory to continuously climb. The larger the change, the more memory is consumed. This is surprising to me.
I confirmed this on iOS and macOS using Xcode 15.4 and 16 beta.
To reproduce, simply add undo/redo functionality to any project and repeatedly undo/redo a change. Memory consumption will climb continuously and will not get released.
In Paul Hudson's SwiftData tutorials, he has an entire section about the numerous tricks required to get SwiftData relationships to work correctly. Does anyone know if SwiftData also requires tricks to get the UndoManager to work correctly?
Hi, when setting up our Core Data stack in our iOS app (manually, with a NSManagedObjectModel/Context & NSPersistentStoreCoordinator) we have reports a rare bug we haven't been able to reproduce.
Occasionally when adding a persistent store we get a NSCocoaErrorDomain 256 error (NSFileReadUnknownError) with NSSQLiteErrorDomain=4618 in the user info. That's SQLITE_IOERR_SHMOPEN , which the SQLite docs describe this way:
I/O error within the xShmMap method on the sqlite3_io_methods object while trying to open a new shared memory segment
It seems to specifically /not/ be a SQLITE_FULL error.
Do you know what type of issue can cause this? Or where/how I can start tracking this down?
I've been building an app with CKSyncEngine based off the documentation and sample code on GitHub. So far it's been working great, but I still have a number of questions I couldn't find the answer to I'd like to know before going into production. Here's a list in no particular order:
When sending changes, are you expected to always send the entire record or just the fields that changed? It's not clear there would even be a way to know the fields that changed since when we have to populate the CKRecord from our local record, we only know the id.
Likewise, when we get a record that changed on the server, do we always get a complete record even if only a single field changed?
Related to that, if a record has asset(s), is the complete asset also returned on every server change even if we already have a copy locally and it hasn't been modified?
If a record does have an asset, is the asset guaranteed to be downloaded and available at the asset.fileURL location by the time CKSyncEngine calls the delegate? If not, is there a way to know it's still downloading and when it will be available?
Is there a way to lazy load assets to avoid unnecessary data fetching?
If there is a failure during sync, for example if I fail to save just one record out of many, how do I recover from that? Is there a way to retry?
Related, is there a way to verify we're completely in sync with the server?
Is there any way to resync besides deleting the state serialization data and doing a complete sync again?
Can I use CKSyncEngine from the main app and the app extensions if they share a database and state serialization. For example, when adding an image from the share extension. Any caveats to that?
Sorry for all the questions, but I want to make sure this is as efficient and reliable as possible. I'm going to request a Lab as well, but it's the lab request form isn't working at the moment so I figured I'd post here in case it's easier to answer async.
Thanks!
– Zach
My main use case is that I have one data source that is obviously user data, and another data source that’s refereence materials shared by all users (but only a few users can mutate).
any insights would be greatly appreciated.
i am developing a SwiftUI App, where i need to work with relatively large amounts of data sets. While processing these data i had some issues with my app crashing randomly. As i was debugging this situation for a while i found out that dataraces were the cause for these crashes. That is why i decided to use an actor for these things..
As the actor takes care of concurrent threads, i was not having any crashes anymore, BUT, now i have to deal with some memory leaks!
i've created a simple demo project to reproduce these leaks.
my view:
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
// @Query private var items: [Item]
var body: some View {
VStack {
Button(action: {
Task { await testImport() }
}, label: {
Text("Import")
})
}
}
}
the function:
func testImport() async {
let actorX = testActor(container: self.modelContext.container)
await actorX.cleanUp()
// create dummy data:
var dummyArray: [Int] = []
for i in 0...1300 {
dummyArray.append(i)
}
await actorX.saveAssets(with: dummyArray)
dummyArray = []
print("Checkpoint")
}
the actor:
actor testActor {
public var modelContainer: ModelContainer
public var modelExecutor: any ModelExecutor
private var context: ModelContext { modelExecutor.modelContext }
public init(container: ModelContainer) {
self.modelContainer = container
let context = ModelContext(modelContainer)
modelExecutor = DefaultSerialModelExecutor(modelContext: context)
}
func cleanUp() {
print("starting cleanup...")
do {
try context.delete(model: Item.self)
print("cleanup: table LocalAsset truncated")
} catch {
print("Failed to clear all LocalAsset data.")
}
}
func saveAssets(with array: [Int]) {
for i in 0..<array.count {
let foo = array[i]
let newItem = Item(timestamp: Date(), dummyInt: foo)
context.insert(newItem)
}
try? context.save()
}
}
And Here's a screenshot of Xcode's Instruments Leak tool:
i hope somebody has any idea how to get rid of those leaks..