sorix / cloudcore Goto Github PK
View Code? Open in Web Editor NEWFramework that enables syncing between iCloud (CloudKit) and Core Data
License: MIT License
Framework that enables syncing between iCloud (CloudKit) and Core Data
License: MIT License
Hi,
Here is the failing scenario :
firstname = 'John', lastname = 'Doe'
firstname = 'Jane', lastname = 'Doe'
firstname = 'Jane'
So on CloudKit, we will never have the property set during NSManagedObject creation, here lastname = 'Doe'
.
I'm not sure how it could be fixed apart by always updating all properties.
I often get crashes with this error:
Can't form Range with upperBound < lowerBound
it happens in ObjectToRecordConverter.swift
on line 73:
private func convertOperations(from objectSet: Set<NSManagedObject>, changeType: ManagedObjectChangeType) -> [ObjectToRecordOperation] {
var operations = [ObjectToRecordOperation]()
for object in objectSet {
// Ignore entities that doesn't have required service attributes
guard let serviceAttributeNames = object.entity.serviceAttributeNames else { continue }
do {
let recordWithSystemFields: CKRecord
if let restoredRecord = try object.restoreRecordWithSystemFields() {
switch changeType {
case .inserted:
// Create record with same ID but wihout token data (that record was accidently deleted from CloudKit perhaps, recordID exists in CoreData, but record doesn't exist in CloudKit
let recordID = restoredRecord.recordID
recordWithSystemFields = CKRecord(recordType: restoredRecord.recordType, recordID: recordID)
case .updated:
recordWithSystemFields = restoredRecord
}
} else {
recordWithSystemFields = try object.setRecordInformation()
}
var changedAttributes: [String]?
// Save changes keys only for updated object, for inserted objects full sync will be used
if case .updated = changeType { changedAttributes = Array(object.changedValues().keys) }
let convertOperation = ObjectToRecordOperation(record: recordWithSystemFields,
changedAttributes: changedAttributes,
serviceAttributeNames: serviceAttributeNames)
convertOperation.errorCompletionBlock = { [weak self] error in
self?.errorBlock?(error)
}
convertOperation.conversionCompletionBlock = { [weak self] record in
guard let me = self else { return }
let cloudDatabase = me.database(for: record.recordID, serviceAttributes: serviceAttributeNames)
let recordWithDB = RecordWithDatabase(record, cloudDatabase)
me.convertedRecords.append(recordWithDB) // <--- here Can't form Range with upperBound < lowerBound
}
operations.append(convertOperation)
} catch {
errorBlock?(error)
}
}
return operations
}
I have the delegate like the example implemented on my app
Disabled internet to see what the app does, currently if I disconnect the device from the internet I get no error at least, that I can see, witch is a problem because I needed to warn the user
Is there a way to view an warning or error when trying to sync?
Hello.
I have one transformable type of attribute in my core data table for UIImage. Is it possible to sync transformable type ?
Thanks
i try to add an entity to my coredata. it has an relationship to-many to another entity. but on this place i just want to add the first entity. now i have an empty list of relationships.
so i get error:
SaveToCloudDidFailed: <CKError 0x1c4a49300: "Invalid Arguments" (12/2006); server message = "cannot use an empty list to initialize a new field (field 'episodes' in record type 'ManagedPodcast' should be more precise)";
i tried to add an dummy object to start with no empty list but then i got another error:
SaveToCloudDidFailed: <CKError 0x1c045a4f0: "Invalid Arguments" (12/1007); "Invalid list of records: Cycle detected in record graph">
Sync with Public DB would be awesome!
Awesome project Sorix, can't say how much I appreciate this 😍
But I ran into a tricky bug, which I hardly know what happened due to Xcode crashes with my app when the bug triggers.
I've tried to comment out any coding using CloudCore, the app runs fine, so the problem must comes from CloudCore;
And I put breakpoints in willSave
and didSave
in CoreDataListener
, when this code is executed slow, interestingly my app did not crash, I wasn't able to locate the problem either;
The bug doesn't trigger every time I save to Core Data, if I create a new object to save, all is well; If I modify the value of an existing object and then save, boom! It crashes;
That's all I observed, I'll keep digging into the problem.
Hello.
I am getting this kind of error when I am trying to sync binary data(UIImage)
CloudCore error detected in module Optional(CloudCore.Module.saveToCloud): <CKError 0x60400084e790: "Batch Request Failed" (22/2024); server message = "Atomic failure"; uuid = B008C63B-4A3F-4255-85EB-A94CA7D58420; container ID = "iCloud.com.barapp.demo">
52062DA4-6B3F-48B0-87BC-DF19090B5572:(CloudCore:defaultOwner) = <CKError 0x604000842700: "Invalid Arguments" (12/2006); server message = "invalid attempt to set value type ASSETID for field 'image' for type 'TblItem', defined to be: BYTES"; uuid = B008C63B-4A3F-4255-85EB-A94CA7D58420>
... 1 "Batch Request Failed" CKError's omited ...
}>
When I try to save in CoreData a record with a transformable field using UIImage, there is an error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Objects of class UIImage cannot be set on CKRecordValueStore'
How can I solve it?
I have some entities with non-optional properties. When I set up Cloudcore to sync my entities, it seems to work fine (but the error might already be there). Adding new Objects to Core Data and saving the changes also works fine.
Now when I delete the App and install it again, I'm waiting for the sync from iCloud to populate my database. However, I sometimes get validation errors from Core Data because the fetched records have nil values on non-optional properties. This seems a bit strange to me, because they should only be synced, if they were saved locally and therefor passed Core Data validation.
Just ran through setup and I am attempting to get it started. However I have run into a major problem.
The first entity that I create (conforms to your documentation) which has a To Many relationship fails to sync due to the following error:
SaveToCloudDidFailed: <CKError 0x1cc242e20: "Invalid Arguments" (12/2006); server message = "cannot use an empty list to initialize a new field (field 'personToBook' in record type 'Person' should be more precise)"; uuid = 3039A5Q3-BFC1-48AF-A442-35A982C5B466; container ID = "iCloud.com.com.bookMaster">
SaveToCloudDidFailed: <CKError 0x1c8247860: "Partial Failure" (2/1011); "Failed to modify some records"; uuid = 3039Q5F3-BFC1-48AF-A442-35A982C5B466; container ID = "iCloud.com.com.bookMaster"; partial errors: {
969F05Q4-DA54-4D21-B87A-9F73F0F0BD77:(CloudCore:defaultOwner) = <CKError 0x1cc241140: "Invalid Arguments" (12/2006); server message = "cannot use an empty list to initialize a new field (field 'personToBook' in record type 'Person' should be more precise)"; uuid = 3039A5F3-BFC1-48AF-A442-35A982C5B466>
}>
If I understand this correctly it is having issues because I did not set a value for the to-many relationship. HOWEVER there can never be a value because if I am creating the parent object to the relationship there cannot be a sub object yet.
Am I missing something here? Seems like a MAJOR problem
It would be awesome if public database could be implemented!
Hello.
When I delete any rows(object) from my core data table than it is not deleting from iCloud server. Any solution ?
Thanks.
Any idea when Swift 4.2 will be supported?
Hi Sorix,
is it save to call
CloudCore.tokens.saveToUserDefaults()
in applicationDidEnterBackground
?
Because Apple does not guarantee to call applicationWillTerminate
every time.
Hi there, thanks all your efforts for this framework. Here I may test out a bug? or something I missed in dealing with this?
This is problem happened by followed detail:
Step 1:
I had 3 entities named B, C, D, all of them were inherited from entity A. In addition, a test entity E was also added.
Step2:
By using CloudCore Framework, I added recordData/recordID attributes in entity E and parent entity A. When I run the app, entity E was sync normal, but nothing happened to Entity B, C, D, E.
Step3:
Then I delete recordData/recordID attributes from parent entity A, and add them to B, C, D. When App launched, it crashed, and read: Coredata error, cannot find attributed. Deleted recordData/recordID attributes from parent entity A, B, C, D, app crashed with the same error all the same.
Step4:
Then I tried to migrate data model version. Delete recordData/recordID attributes from parent entity A, and add them to B, C, D, it reads conflict attributes with old model version.
Maybe this situation is rare, but it really happens to me. I even thought this was a dead cycle. Do you have any advices to fix this? Your help would be highly appreciated.
Right now it just thows the orderedSetRelationshipIsNotSupported error.
i was checking if this framework is good from my needs, so I began some test with the example project.
I started the app on two real devices, an iPhone and an iPad, added and deleted some record and all seems work fine.
On the last test i put the iPhone in flight mode, added some record and quit from app, after that I reenabled connectivity and launched app.
The iPad updated consequently but all records where doubled!.
After that the sync did not work anymore as expected.
I think is a bug, because is replicable every time.
I'm trying to get the app to sync in realtime or at least with one or two minutes of interval but it does not sync.
I'm testing on two real devices
It only syncs upon launch
Added the delegate handler like in the example to handle anything I would need
and in the function func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
I added the following code
application.registerForRemoteNotifications()
CloudCore.delegate = delegateHandler
CloudCore.enable(persistentContainer: persistentContainer)
And this function as described in the documentation
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// Check if it CloudKit's and CloudCore notification
if CloudCore.isCloudCoreNotification(withUserInfo: userInfo) {
// Fetch changed data from iCloud
CloudCore.fetchAndSave(using: userInfo, to: persistentContainer, error: nil, completion: { (fetchResult) in
completionHandler(fetchResult.uiBackgroundFetchResult)
})
}
}
Currently using iOS 11+ and Swift 4
The app does upload when changes are made
If NSManagedObject
doesn't have recordID
we need to sync it fully instead of differential sync.
Hi,
I implemented this lib and i noticed this behaviour:
Am I missing something or the lib is not doing what is supposed to do?
is it possible to start a progress to migrate existing data to cloudcore?
at the moment the recordData field is empty. The recordID Field is an already as GUID used field.
Some relations are broken (nullified) in CoreData when you get data from CloudKit.
Sorry for perhaps asking stupid questions, but I'm pulling my hair here and can't get
it to work properly.
I have a simple Application with a tableView that is filled with coreData.
Just one entity called "Person", no relationships, 4 attributes: name, age, recordData, recordID.
I have pulled the CoreData functions in a separate class called persistenceService.
In my AppDelegate I enabled CloudCore like this:
CloudCore.enable(persistentContainer: persistenceService.persistentContainer)
In my viewcontroller I have two functions I use for this:
#1: Fetch CoreData and reload Tableview
func getCoreData() {
// get data from coredata
let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
let sortDescriptor = NSSortDescriptor(key: "recordID", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
do {
self.people = try persistenceService.context.fetch(fetchRequest)
} catch {}
self.tableView.reloadData()
}
#2 Fetch Cloud Data
func fetchCloudData() {
print("fetchAndSave and Reloading Tableview")
CloudCore.fetchAndSave(to: persistenceService.persistentContainer, error: { (error) in
print("FetchAndSave error: \(error)")
}) {
DispatchQueue.main.async {
persistenceService.context.reset()
self.getCoreData()
}
}
}
What works:
I create a new entry, it gets synced to iCloud.
I delete a record in iCloud, call fetchCloudData() and its gone.
I edit a record, call fetchCloudData() and its edited in local storage.
BUT all this only works when I call persistenceService.context.reset() in the fetchCloudData()
function.
Lets say I start the app, CloudCore syncs, and then I try to edit/delete an entry I always get
an error like this:
["conflictList": <__NSArrayM 0x600000648df0>(
NSMergeConflict (0x60000086fd80) for NSManagedObject (0x604000294a00) with objectID '0xd000000000580000 <x-coredata://BB8BBB3E-0F70-4ED2-BDCD-D15AB86480B0/Person/p22>' with oldVersion = 7 and newVersion = 8 and old object snapshot
When I start the app and once call the fetchCloudData() function, it works, when I
uncomment the persistenceService.context.reset() part it gets the same error as above.
can someone please hint me what I'm doing wrong?
I can put the demo app onto a phone and an iPad and see the additions syncing across to the other app but deletions only seem to work one way, from one device to the other but not from the second. Am I expecting the wrong behavior?
Hi, just another one of my dumb questions:
when I start the app, CloudCore initializes the first fetch Operation.
How would I trigger a tableview refresh AFTER the first Fetch Operation?
I got it working when a change notification comes, but at the first start it doesn't work.
In my viewDidLoad() I have the data Refresh function but it always runs BEFORE the first Fetch Operation and so if changes where made from another device, They only get displayed after I manually refresh.
sorry for my dumb questions, but what would be a good way to go on here?
I have done all steps mentioned in the documentation (in app delegate), however the remote notifications are not being called back.
I have enabled the remote notifications in background mode and push notifications in general.
Please help
Thanks
Is there a reason why
fetchRequest.includesSubentities
is always set to false? I am using a relationship to an abstract entity, this will only work if subentries are fetched.
I have recently stumbled upon your repository of CloudCore and I am quite interested in taking advantage of it. However, I can not seem to get the demo app that you have included. I have changed the bundle ID and updated the CloudKit dashboard with the necessary items. However, I keep running into the same error with the zone of ‘CloudCore’ with the error message of ‘Zone Not Found’. I am still pretty new into taking advantage of iCloud and I am not sure as to what I am doing wrong.
Any suggestions would be greatly appreciated.
From: Michael, by e-mail
Hey,
I stumbled upon an error that probably occurs during the transformation from iCloud to CoreData.
It happens during the initial fetchAndSave call during application start.
Error Message: Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for ordered to-many relationship: property = "transactions"; desired type = NSOrderedSet; given type = __NSArrayM; value = (
"<CKReference: 0x1c002d560; recordID=<CKRecordID: 0x1c0229200; recordName=3606B38D-FB8F-49FC-B77C-5FC419B56BDA, zoneID=CloudCore:defaultOwner>>"
).'
I have a Type - Entity, which has a to-many relationship to a Transaction - Entity, called transactions.
It seems to get an array value from fetchAndSave but is expecting an NSSet. I changed the value to an NSOrderedSet in CoreData, but it didn't help.
Any idea what could cause this error ?
Hi,
I just test the framework in a simple app with a tableview.
when I add a entry on one device, the other device gets the change notification and when I reload the tableview the new entry is there, same with deletions.
but when I edit an entry the changeNotification comes, but I only see the change after killing and restarting the app. can someone give me a hint what I'm doing wrong?
Hi,,
I have a crash when running the application with the following code in class CloudCoreConfig.swift:
/// Default value is
CKContainer.default()//
lazy is set to eliminate crashes during unit-tests public lazy var container = CKContainer.default()
Is there a solution to this problem?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.