I got a @Model class with a property of a codable "Options" struct.
import SwiftData
@Model
final class Project {
var options: Options
}
In this struct I have another property called "resolution" of type Size3D.
import Spatial
struct Options: Codable {
var resolution: Size3D
}
I can initially launch the app and save a project with a custom resolution. Tho when I try to re-launch the app it crashes:
Could not cast value of type 'Swift.Optional<Any>' (0x1fa251d80) to '__C.SPSize3D' (0x1047dcbc0).
Size3D is codable, tho it does not seem to currently be supported by SwiftData.
My current solution is to have a middle type that I store in my options struct like this:
struct Options: Codable {
private struct CodableResolution: Codable {
let width: Double
let height: Double
let depth: Double
var size: Size3D {
Size3D(width: width, height: height, depth: depth)
}
init(_ size: Size3D) {
self.width = size.width
self.height = size.height
self.depth = size.depth
}
}
private var codableResolution: CodableResolution
var resolution: Size3D {
get {
codableResolution.size
}
set {
codableResolution = CodableResolution(newValue)
}
}
}
Note that I'm testing this on the visionOS simulator with Xcode 15.2 on macOS 14.0
Feedback: FB13543953
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 am doing a full transition of an app from CoreData to SwiftData. I was able to follow the online guides and extract SwiftData objects from the CoreData model. Everything seems to work except for my Transformable String Arrays. This app is storing CoreData in iCloud.
In my SwiftData model, I have a variable...
@Attribute(.transformable(by: "NSSecureUnarchiveFromData"))
var arrayofStrings: [String]?
The app is able to read the array of strings, however, the debugger gives me this error eventually crashing due to memory.
'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release
I don't understand, the transformed variable already is 'NSSecureUnarchiveFromData' not 'NSKeyedUnarchiveFromData'. That's the reason why I used NSSecureUnarchiveFromData in my CoreData model because NSKeyedUnarchiveFromData is being phased out. I don't get why the debugger thinks otherwise.
Any thoughts?
My App uses Core Data + CloudKit and requires use of CloudKit to store persistent data, that will be shared between Users. I’d like to establish the User’s identity - like so they can augment their Core Data ‘Player’ entity w/ App specific info. Certain interfaces have been deprecated (‘user discoverability’). I’ve taken to creating a ‘dummy shared zone’ and extracting the ‘owner.userIdentity':
public func establishUser () async -> User? {
let container = cloudKitContainer
// If we store the userUUID then the implication is that the `user` can
// never be deleted; that is fair enough.
let userUUIDKey = "userUUID"
var userIdentity = Optional<CKUserIdentity>.none
do {
//
// We'll store the UUID of the CoreData `User` in the CloudKit `User` record. If there
// is no UUID in CloudKit, then this will be the first time the User has ever started
// the App. We'll create a user and update the CloudKit `User` record
//
let userID = try await container.userRecordID ()
let userRecord = try await container.publicCloudDatabase.record (for: userID)
// If the `userRecord` does not have a `userUUIDKey` then we must create a new `User`.
if nil == userRecord[userUUIDKey] {
// See if the user has the required iCloud account.
let userStatus = try await container.accountStatus()
guard userStatus == .available
else {
print ("JKP: \(#function) accountStatus: \(userStatus)")
return nil
}
//
// Create a `dummyShare` (in a 'dummyZone') so we can access the share's owner
// That owner will have our `userIdentity`
//
do {
let dummyZone = CKRecordZone (zoneName: UUID().uuidString)
let dummyShare = CKShare (recordZoneID: dummyZone.zoneID)
print ("JKP: User: Establish Zone: \(dummyZone.zoneID.zoneName)")
// Save the dummyZone and then the dummyShare (for/in the dummyZone)
let _ = try await container.privateCloudDatabase.save (dummyZone)
let _ = try await container.privateCloudDatabase.save (dummyShare)
// Extract the dummyShare's owner's identity - which is 'us/me'
userIdentity = dummyShare.owner.userIdentity
// Cleanup by deleting the 'dummyShare' and then the 'dummyZone'
let _ = try await container.privateCloudDatabase.deleteRecord (withID: dummyShare.recordID)
let _ = try await container.privateCloudDatabase.deleteRecordZone (withID: dummyZone.zoneID)
}
catch {
print ("JKP: User Establish Error: \(error.localizedDescription)")
}
// Create `newUser` with the `userRecordId`. We'll use this to lookup the
// Core Data User when players appear in a League.
let newUser = User.create (context,
scope: Player.Scope.owner,
name: (userIdentity?.nameComponents ?? PersistenceController.nameDefault),
identification: userIdentity?.lookupInfo
.map { PlayerIdentification.create (lookupInfo: $0) } ?? PlayerIdentification())
… }
Is this how getting the userIdentity is meant to be done (w/o using the deprecated interfaces)? The deprecated interfaces, when warned in Xcode, reference a sample project; that project doesn’t actually use/get the userIdentity.
Also the deleteRecord and deleteRecordZone don’t appear to remove the dummy zone in CloudKit; why?
How does one know when the CloudKit data in a CoreData+CloudKit (NSPersistentCloudKitContainer) has been fully synchronized. UseCase would be a user starts the App on a second device, user deletes then reinstalls the App, another User accepts a share and the share needs to sync.
Is the containerEventChanged Notification for 'import success' the definitive event?
CoreData: CloudKit: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _finishedRequest:withResult:](3403): Finished request: <NSCloudKitMirroringImportRequest: 0x600002345d10> 729A742A-7F3B-42F1-B04C-72705D41FFEF with result: <NSCloudKitMirroringResult: 0x600000c4edc0> storeIdentifier: 79FA5848-A135-41B1-A36A-09F2F914D23D success: 1 madeChanges: 0 error: (null)
As the sync could be time-consuming, is there a way to identify a single CloudKit record?
Hi fellow Swifties,
Been pulling my hair out for 8+ hours and need your help :(
Tried creating 3 models that are cloudkit compatible, but the app crashes every time I try to add an Object to the modelContext.
Error:
Thread 1: EXC_BREAKPOINT (code=1, subcode=0x18ec49d2c)
My guess:
I think it is trying to force unwrap a nil...
Can do:
Insert Item() and Location() into modelContext + syncronizing to cloudkit without any errors.
modelContext.insert(Item())
modelContext.insert(Location())
Can not do:
Insert Object() into modelContext.
modelContext.insert(Object())
Am I not initialising them correctly? Is this approach bad? Or am I experiencing a bug?
Help is much appreciated <3
My 3 models:
@Model
class Item: Identifiable {
var id: UUID = UUID.init()
var name: String = String()
var barcode: String = String()
@Relationship(deleteRule: .cascade, inverse: \Object.item)
var objects: [Object]?
@Attribute(.externalStorage)
var photo: Data?
init(name: String = String(), barcode: String = String(), photo: Data? = nil) {
self.name = name
self.barcode = barcode
self.photo = photo
}
}
@Model
class Location: Identifiable {
var id: UUID = UUID.init()
var name: String = String()
@Relationship(deleteRule: .cascade, inverse: \Object.location)
var objects: [Object]?
var isFavorite: Bool = Bool(false)
var unwrappedObjects: [Object] {
objects ?? []
}
init(name: String = String(), isFavorite: Bool = Bool(false)) {
self.name = name
self.isFavorite = isFavorite
}
}
@Model
class Object: Identifiable {
var id: UUID = UUID.init()
var amount: Int = Int()
var expiryDate: Date? = nil
var location: Location?
var item: Item?
init(amount: Int = Int(), expiryDate: Date? = nil) {
self.amount = amount
self.expiryDate = expiryDate
}
}
Have an app that has a half dozen models. We created a new app version with a new model that has an additional property on one entity.
The options for the persistent store:
do {
let options = [
NSMigratePersistentStoresAutomaticallyOption: NSNumber(value: true),
NSInferMappingModelAutomaticallyOption: NSNumber(value: true)
]
let _ = try persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: options)
} catch {
try? FileManager.default.removeItem(at: storeURL) //Erase old sqlite
// Make new persistent store for future saves
let _ = try? persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: nil)
}
Launch this with data from a previous version. A breakpoint in the catch block never triggers so the migration must have worked.
Since I'm in development, what happens if I run a different branch that doesn't have the new model? I assume the catch block is going to trigger—but it doesn't!
Why doesn't it trigger? Does Core Data essentially ignore the new entity property, so all is well?
I was just surprised ...
Xcode 15.2.
Brand new simulator set up, running 17.2.
Brand new AppleID, and have logged in and accepted terms.
Totally unable to login to iCloud on the Simulator to test iCloud syncing.
There are numerous threads dating back 6 years plus of people having issues logging in to iCloud on the Simulator.
Seems like it's still an issue. I have tried every solution. Existing AppleID. New AppleID. Logged into iCloud on Safari on Desktop and Simulator to check for unaccepted terms. Still get the commonly reported Sign-in page sitting spinning endlessly.
Checked Console for errors and only things that seem possibly related are:
com.apple.shortcuts CloudKitSync info 13:41:04.506714+0000 siriactionsd -[VCCKShortcutSyncCoordinator updateAccountStatusAndUserRecordID]_block_invoke Not fetching current user record ID because iCloud account is not available
Is it really still the case that logging in to iCloud on a Simulator, to test sign on or iCloud sync is still horribly broken, and has been for over 6 years?
I accidentally deleted about 200 records from a CloudKit database that is being used in production. Is it possible to recover these, I have no other backups of the data.
We are considering to use iCloud API on web service.
But we don't have any mobile app on App Store.
In this case, I think it is not possible to use iCloud according to apple's terms of service, but is there any way to use the iCloud on web service only?
We have an iOS App developed in SwiftUI that saves data to CloudKit using NSPersistentCloudKitContainer. If I modify any of this data, the changes are synchronized on other iOS devices without problem.
We also have an OSX App with StoryBoard using NSPersistentCloudKitContainer to share the same data with iOS devices.
Changes made on OSX are updated immediately on iOS devices. But if any data is modified in iOS, it is not updated in the OSX application. Simply by doing Command-Tab twice the OSX app displays the new data.
It appears that CloudKit Push Notifications are not causing the OSX application to detect that there are changes to the data and that it needs to be updated.
In both cases an NSFetchedResultsController is created and a delegate is placed. The iOS one is called but the OSX one is not.
The only difference in the code is that in the iOS app with SwiftUI it is done:
var body: some Scene {
WindowGroup {
InitialTab()
.environment(\.managedObjectContext, DataManager.shared.viewContext)
}
}
but this is not done in OSK since NSView does not have the .environmet method.
Could this be the problem?
What should I do to update data without switching applications on OSX?
Any help would be appreciated,
Thank you so much.
I am in the very early stages of app development using SwiftData, and find myself making frequent changes to my data models. I know ultimately that I would use migration when making schema changes, but at this early point in development that seems like over kill. in fact at this point I have only tested the app with previews. Is there a way to clear whatever the previews are caching when I make a change such as renaming a property? Clean Build folder does not fix this error, any guidance on how to handle this?
Hello, everyone!
I'm currently working in creating a new web app that will replicate the functionalities of an existing iOS and Mac app.
However, since those apps rely on CloudKit to manage all user information, we decided on using CloudKit JS as our backend for our web app. The framework we chose for developing our frontend is React.
The question is, since this documentation only mentions the CloudKit JS usage through a CDN (embedded directly in the HTML file) I wanted to ask:
is there a "suggested" method for using CloudKit JS in a React project other than importing the CDN in the main html file?
in case not, should we use a "traditional" server to access data like they suggest in this thread?
All your help will be very much appreciated.
Best regards, Eduardo
Hello,
I build my app on Unity 2021 using the service Unity Cloud Build. I enabled iCloud key-value storage and it correctly show up in the entitlement file.
Whenever I try to make a cloud save, Unity Logs says that the process was successful but it didn't save anything on the cloud.
I had a look to the logs on my device and I found this strange error:
cloudd(CloudKitDaemon)[804] <Error>: Identity set <private> was expected to have a current key set <private>. Error Domain=securityd Code=-25300 UserInfo={NSLocalizedDescription=<private>}
cloudd(CloudKitDaemon)[804] <Error>: Identity set <private> does not have a current key set. Not using it.
cloudd(CloudKitDaemon)[804] <Error>: Didn't get a service identity from the PCS framework
I searched online but I couldn't find anything informative.
Any suggestions?
In an effort to ensure the safety of my data, I decided to make a backup on iCloud Drive, assuming that this would provide a secure storage solution and an additional copy on my local MacBook Pro. Regrettably, I was not aware that this process would result in the complete loss of all data from my Documents folder on my local machine.
Upon reaching out to the iCloud support team, their attempts to assist were unfortunately unsuccessful. I was informed that while there is a slim chance of recovering data by purchasing a larger iCloud Drive, no guarantees could be provided. This revelation left me questioning the reliability and effectiveness of iCloud Drive as a secure data management tool.
Furthermore, the explanation provided by the support team regarding the iCloud Drive synchronization process added to my dismay. It appears that iCloud Drive deletes the local Documents folder and replicates it in the cloud, only to recreate a new copy on the local machine afterward. The inherent risks associated with such a mechanism, especially when dealing with large files, are alarming and raise serious concerns about data integrity.
I strongly advise against relying on iCloud Drive for substantial data storage. Instead, I recommend using an external hard drive with Time Machine activation for a more reliable backup solution. It is disheartening to realize that Apple's touted iCloud Drive, marketed as a secure tool to prevent data loss, may fall short of its promises, potentially leading users into situations like mine.
I urge the iCloud engineering team to reconsider and improve the synchronization process to mitigate the risk of data loss for users. The current system, as I have experienced firsthand, poses significant challenges and lacks the necessary safeguards to protect users from unforeseen circumstances.
Hi all,
I've been struggling a bit with SwiftData, I have a really simple data model but I need to do a fairly complex query over a one-to-many relationship that results in aggregate values. And I want to be able to sort and filter those aggregates. The model looks like this:
@Model
class Category {
var name: String
var items = [Item]()
}
@Model
class Item {
var added: Date
var checked: Date?
}
In a typical use case I'd expect their to be at most around 50 categories and perhaps 20 items per category.
In my UI I would like to be able to sort categories in a couple of ways: by name, by date added and by date checked with a fallback on date added. Also, in my list view I want to be able to list categories and show the checked date of the most recently checked item.
I can think of a couple of solutions here, to start of with I can do all the sorting and filtering client-side. In the list view I could retrieve all the categories and then find the most recently checked item per category and go from there.
Another solution might be to use a transformable column for the items, because I expect there to be only a few of them per category.
The last thing I can think of, that I know how to make is to denormalize the model, and add a checked and added column to the category model. These would have to be updated each time an item is added or checked.
All of these solutions sound suboptimal to me. Ideally I would want to add a checked and added column to the category model that are computed in the database using a query. I previously implemented this simple app in Django (Python), where I could easily implement this particular solution, offloading all the sorting and aggregating to the database.
So my question is if this is possible in SwiftData and how to do it. If it is not possible what would be the preferred solution? Since the expected amount of data is pretty small I could probably get.away with doing it all in code instead of in queries, but that just doesn't feel right.
What is the recommended architecture for a CoreData+CloudKit iOS App that has a large public database?
Assume the public database contains 10,000+ Locations from which a User would select a few as favorites. Totally impractical to mirror the locations such that they appear in the App's CoreData having been synced from the .public CloudKit database. Presumably one uses the CloudKit API to query the .public database and display some subset of locations for the User to select? (The selected locations can then be stored in the Users .private (or perhaps .shared) database.)
How does one configure CoreData + CloudKit for this scenario? Is there another approach?
It is often the case that offline devices can add duplicate entities that needs to be merged when CloudKit syncs. Consider user-created tags. A user might create a Note, and then tag it with a newly created tag “Family.” On a separate offline device, they might create another note, and create another tag also called ”Family.” On device sync, both duplicate ”Family” tags would need to be identified as duplicates based on their name property, merged to a single entity, and their original relationships consolidated to the single merged Tag. And this needs to happen before the CloudKit sync data is presented to the UI in the main context.
With Core Data we have the mechanism to consume relevant store changes described here.
These tools allow us to listen for remote change, then process them appropriately (e.g. remove / merge duplicates) and then merge the changes into the app’s main context. This perfectly solves the problem described in the first paragraph above. Apple provides code using this mechanism for deduplicating tags in a sample app.
Is there a mechanism to solve this deduplication problem using SwiftData technology without implementing and maintaining a parallel Core Data stack?
I'm trying to setup a filter option for some core data records, but I'm not seeing the expected results.
Essentially, what I want is to have multiple sections. Within each section it would be an OR query, and an AND between each section. I've tried setting it up as below.
func getBasicFilter() -> NSPredicate? {
var predicatesBagsLeft : [NSPredicate] = []
var predicatesDrillType : [NSPredicate] = []
var andPredicates : [NSPredicate] = []
print("Filter List")
print(filters)
// Bags Left
if let show = filters["showFullLeft"] {
if show {
print("Predicates : Show Full")
let predicate = NSPredicate(format: "ANY projectColours.fullLeft > 0")
predicatesBagsLeft.append(predicate)
}
}
if let show = filters["showPartialLeft"] {
if show {
print("Predicates : Show Partial")
let predicate = NSPredicate(format: "ANY projectColours.partialLeft > 0")
predicatesBagsLeft.append(predicate)
}
}
// Drill Types
if let show = filters["showSquareOnly"] {
if show {
print("Predicates : Show Square Only")
let predicate = NSPredicate(format: "ANY projectColours.project.drillType = %@", "Square")
predicatesDrillType.append(predicate)
}
}
// Drill Manufacturers - TO DO
// Combine Predicates
if predicatesBagsLeft.count > 0 {
let predicatesForBagsLeft = NSCompoundPredicate(type: .or, subpredicates: predicatesBagsLeft)
andPredicates.append(predicatesForBagsLeft)
}
if predicatesDrillType.count > 0 {
let predicatesForDrillType = NSCompoundPredicate(type: .or, subpredicates: predicatesDrillType)
andPredicates.append(predicatesForDrillType)
}
if andPredicates.count > 0 {
let predicates = NSCompoundPredicate(type: .and, subpredicates: andPredicates)
return predicates
}
return nil
}
It does filter, but doesn't seem to be applying both correctly. I'm testing with a filter of showFullLeft & showSquareOnly, so it should show only squares which have a a fullest > 0
I'm getting 7 results back, but one of them is unwanted. It is square, but it has 0 for both full & partial
When I look at the query core data is using it looks correct
CoreData: sql: SELECT t0.ZMANU, COUNT (DISTINCT t0.Z_PK) FROM ZCOLOUR t0 JOIN ZPROJECTCOLOUR t1 ON t0.Z_PK = t1.ZCOLOUR JOIN ZPROJECTCOLOUR t2 ON t0.Z_PK = t2.ZCOLOUR JOIN ZPROJECT t3 ON t2.ZPROJECT = t3.Z_PK WHERE ( t1.ZFULLLEFT > ? AND t3.ZDRILLTYPE = ?) GROUP BY t0.ZMANU ORDER BY t0.ZMANU
CoreData: details: SQLite bind[0] = 0
CoreData: details: SQLite bind[1] = "Square"
Hi all,
I have an iOS app which uses CloudKit and the standard NSPersistentCloudKitContainer, which I rely on for syncing app data between the user's devices. If the user's iCloud account is full I can see a log message while debugging in Xcode shortly after startup which looks something like this:
error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _requestAbortedNotInitialized:](2183): <NSCloudKitMirroringDelegate: 0x281ddc1e0> - Never successfully initialized and cannot execute request '<NSCloudKitMirroringExportRequest: 0x2841e00f0> 51383346-87BA-44D8-B527-A0B1EE35A0EF' due to error: <CKError 0x282c50db0: "Partial Failure" (2/1011); "Failed to modify some records"; uuid = 7BA17495-4F05-4AF4-A463-C0DF5A823B2E; container ID = "iCloud.com.neufsters.pangram"; partial errors: {
E30B2972-FD4B-4D2A-BD1C-EB6F33F5367D:(com.apple.coredata.cloudkit.zone:__defaultOwner__) = <CKError 0x282c155f0: "Quota Exceeded" (25/2035); server message = "Quota exceeded"; op = FC4D3188D0A46ABC; uuid = 7BA17495-4F05-4AF4-A463-C0DF5A823B2E; Retry after 315.0 seconds>
2FC9A487-D630-444D-B7F4-27A0F3A6B46E:(com.apple.coredata.cloudkit.zone:__defaultOwner__) = <CKError 0x282c52820: "Quota Exceeded" (25/2035); server message = "Quota exceeded"; op = FC4D3188D0A46ABC; uuid = 7BA17495-4F05-4AF4-A463-C0DF5A823B2E; Retry after 315.0 seconds>
903DD6A0-0BD8-46C0-84FB-E89797514D9F:(com.apple.coredata.cloudkit.zone:__defaultOwner__) = <CKError 0x282c513e0: "Quota Exceeded" (25/2035); server message = "Quota exceeded"; op = FC4D3188D0A46ABC; uuid = 7BA17495-4F05-4AF4-A463-C0DF5A823B2E; Retry after 315.0 seconds>
}>
I would like to know how I can get a callback of some sort so I can run code if this CloudKit/CoreData error happens. In particular I'd like to put up some sort of warning to the user letting them know their data isn't going to sync.
Please note that I'm not looking for how to do error handling as a result of a user-initiated CloudKit API call. I'm looking for how to get notified when the background syncing logs errors like the above.
Thanks,
Russ
In our app, there is a scenario where we write and delete approximately 100MB of files to the iCloud server at once using NSPersistentCloudkItContainer(CoreData). While write, read, and delete operations are immediately reflected in the local database, there is a noticeable delay when accessing the CloudKit Database dashboard.
Here is the testing approach we have tried:
Data Insert Test
Prepare around 100MB of data.
Write the data to CoreData on device A.
When launching the app on device B, it takes about 5 minutes for the data to be fully synchronized.
Data Deletion Test
Remove all the added data on device A (immediately reflected in local storage.)
After performing step 1, leaving device A idle takes about 3 minutes for the deletion to be reflected on device B.
if the app is deleted on device A after step 1, deletion information does not reach device B. Upon reinstalling the app on device A, the deleted data reappears on device B (synchronized data).
The ongoing occurrence of these issues has raised several questions regarding cloud synchronization and synchronization speed:
Is the synchronization speed of the Testflight app the same as the one received from the AppStore?
Are there traffic limitations per account or device?
Despite different perceived speeds for each account or device, is there any factor influencing synchronization speed other than network conditions?