In CloudKit dashboard, record type User has a field called Boards of type Reference (List), and record type Board has a field called User of type Reference. This is a sample of code indicating how I am attempting to link a Board to a User:
CKReference *reference = [[CKReference alloc] initWithRecord:userRecord action:CKReferenceActionDeleteSelf];
[boardRecord setObject:reference forKey:#"User"];
If I NSLog(#"%#", [boardRecord objectForKey:#"User"]); before and after setting the object, I get a result of (null) before, and after it is a CKReference object with the same recordName as the User. But in CloudKit dashboard, the Board does not update its User field, and the User does not have a reference added to its Boards reference list, so when I subsequently query the User for associated Boards I do not receive any results. I cannot understand why the reference is correctly set according to what is logged, but does not actually get saved.
Here is the code used to save to iCloud:
+ (void)saveEntity:(CloudKitEntity *)entity completion:(void (^)(NSError * _Nonnull))completion {
[[self privateDatabase] saveRecord:entity.record completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) {
if (completion) {
completion(error);
}
}];
}
Logging the error revealed an error that "Server Record Changed", which I think means the server version of the record is newer than the local version. But I don't know what to do about this since I have just cleaned/rebuilt, deleted the app and rebuilt/reinstalled and received the same error.
The solution was to fetch both of the records again using their recordIDs, then set create the CKReference and set it using the re-fetched objects, and then save the re-fetched object. Why it needs to be this complicated I have no idea.
Related
I am working with ios project in which I delete database at time of logout but when I try to relogin with another user I get error as following:
NSSQLiteErrorDomain = 522
My code on logout is as following:
NSURL *storeURL = [[self contentStorageDirectory] URLByAppendingPathComponent:#"*****.sqlite"];
[[[NSFileManager alloc] init] removeItemAtURL:storeURL error:nil];
self.managedObjectContext = nil;
self.managedObjectModel = nil;
self.persistentStoreCoordinator = nil;
[self getManagedObjectContext];
😫 deleting the database when logout ?. Maybe your trying to delete all previous logged in user data. You must read a little more about CoreData. You can nil the context, but persistence store coordinator should not be touched because have a reference to the model working with.
how can you getManagedObjectContext, when the store is assigned to nil 😵
take a look at your model, your entity´s relations have something called Delete Rules, could be Deny, Nullify, Cascade and No Action.
from apple :
A relationship's delete rule specifies what should happen if an
attempt is made to delete the source object.
so what you should try is delete all user data after logout.
if you want to debug every movement on the underlying engine of CoreData put this con your Run scheme, on the arguments passed on launch
-com.apple.CoreData.SQLDebug 1
I am new to iOS development and just got started using swift. I am using Cloud Kit. I am trying to fetch and save all the records in a record type called Puppets. It is an extremely small text dataset out will need updating.
How can I fetch and save all records from one Record Type, using swift?
Here is the code I got from the Documentation:
CKDatabase *publicDatabase = [[CKContainer defaultContainer] publicCloudDatabase];
CKRecordID *artworkRecordID = [[CKRecordID alloc] initWithRecordName:#"Puppets"];
[publicDatabase fetchRecordWithID:artworkRecordID completionHandler:^(CKRecord *artworkRecord, NSError *error) {
if (error) {
// Error handling for failed fetch from public database
}
else {
// Display the fetched record
}
}];
As #rmaddy already responded, if you want to query a recordType for multiple records you can use a CKQuery. Even better would be a CKQueryOperation. You will have more control over the query. You also have to be aware that the number of records returned is limited (usually to 100). If you want to read more records, then you have to start an other query where you use the cursor that is returned by the first query.
Here you can see some sample code how EVCloudKitDao snippet
I am trying to retrieve an object from the managed object context and edit a value or two after it has been backed up to the cloud. In particular, I want to save an id provided by the cloud server.
To get the object, I'm trying to retrieve it using its managedObjectID.
I have tried:
Contacts *object = [_managedObjectContext objectWithID:_moID];
and
Contacts *object = [self.managedObjectContext objectRegisteredForID:_moID];
where moID is the managedObject id.
I then follow this up with
object.cid = #99; //ie I set it equal to some number
In both cases, although Contacts is an NSManagedObject class, it throws a yellow warning:
'Incompatible point types initializing contacts with an expression of type NSManagedObject'.
If Instead of calling it Contacts *object, I call it NSManagedObject *object, it no longer throws the warning but then will not allow me to say object.cid as it no longer knows what a cid is. In this case it throws red error:
Property cid not found on object of type NSManagedObject.
Can anyone suggest proper object type. Thanks in advance for any suggestions.
By looking at the method definition, you'll see that returned instance type is just NSManagedObject * as the warning said. What you need to do is just to cast the type appropriately:
Contacts *object = (Contacts *)[_managedObjectContext objectWithID:_moID];
and
Contacts *object = (Contacts *)[self.managedObjectContext objectRegisteredForID:_moID];
Note, your Contacts suppose to be a subclass of NSManagedObject.
Update 21/7/2014: I have found a workaround for the problem described here. I have created a separate NSManagedObjectContext called downloadContext. The downloaded serialized objects are put in that separate context. I then iterate through the response array and check if the object already exists in the MR_defaultContext (using the primary key).
If it does I update the properties from the downloaded object with the same primary key. It is not possible to simply copy the whole object and thus overwriting all properties of the existing object (because the updated object and the old one are in separate contexts). Instead I copy all properties of the downloaded object into an NSDictionary (dictionaryWithValuesForKeys) and then use setValuesForKeysWithDictionary to overwrite the properties of the old object. The same has to be done with the child objects.
If the object is new, I create a new object and copy the properties in the same way. I will need to check if this solution is efficient enough or if it will occupy too much memory in case there are many objects to download (first sync). The alternative is to create my own serialiser that decides on the basis of primary keys if to update an existing object or to create a new one.
For this issue, please note that I have seen the discussion on the relatedByAttribute key setting. It doesn't help in this case since this might only avoid duplicates of child relationship objects ( I haven't checked if there are duplicates). This is about the parent object duplicates.
In my project, I use AFNetworking, MMRecord and MagicalRecord together as dependencies. The situation is as follows:
I create a response serializer from the AFMMRecordResponseSerializer class which I simply set to my subclass object of the AFHTTPSessionManager. With this session manager I send a GET-request to the server. The response object already contains serialized NSManagedObjects (they are actually a subclass of MMRecord). I have set the MMRecordEntityPrimaryAttributeKey to each of the NSManagedObject's intended primary key. I think that the problem lies with MagicalRecords though, because it simply ignores MMrecord's primary attribute key setting.
After the initial GET-request with the persistent store still empty, there is no modification/creation date predicate. In case there are already persisted objects, the GET-request contains a predicate retrieving only objects after the last mod date from the server. So the updated object is persisted but the older object is not overwritten. When I display the titles of the objects in a table view I can see that the older version and updated version of an object are both displayed. Important: the table view is populated only from core data, not directly from the responseObject (containing the serialized objects).
Below is the most important code.
Thanks for any hints in the right direction
Dominik
-(void) fetchNotesFromServer
{
NSLog(#"fetchNotesFromServer called");
self.sessionManager.responseSerializer = [self getSerializerForEntityName:#"NAS_Notes" AndEndPointPathComponent:#"DBFetchNotes.php?"];
[self.sessionManager GET:[self getFetchNotesExtension] parameters:nil
success:^(NSURLSessionDataTask *task, id responseObject)
{
if (responseObject )
{
NSLog(#"fetch all notes response: %#", responseObject);
self.latestNotes = (NSMutableArray *)responseObject;
if ([self.latestNotes count] > 0)
{
[self saveDownloadedNotesInContext:[NSManagedObjectContext MR_defaultContext]];
}
}
} failure:^(NSURLSessionDataTask *task, NSError *error)
{
NSLog(#"NSerror %#", error);
}];}
-(void) saveDownloadedNotesInContext:(NSManagedObjectContext *)context{
[context MR_saveToPersistentStoreWithCompletion:^(BOOL success, NSError *error) {
if (success) {
[self.delegate didFinishNotesSync:self];
}
else if (error) {
NSLog(#"error saving to persistent store %#", error);
}
}];
}
I'm using Magical Record 2.1 to handle the data persistence in my app. If I create a new entity, set some it's attributes and save, it works fine. However, later, if I fetch that entity, update it's attributes and save, subsequent fetches have the new data until I terminate the app and restart. During the new app session the old data reappears.
This is how I create a new entity:
self.localContext = [NSManagedObjectContext MR_defaultContext];
self.theNewListing = [Listing MR_createInContext:self.localContext];
I'm using MRDefaultContext having read this blog post: http://saulmora.com/2013/09/15/why-contextforcurrentthread-doesn-t-work-in-magicalrecord/
In this case my main attribute is a dictionary, and I set it like this:
NSMutableDictionary *tempDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:#"A description", #"slFieldDescription", etc, etc, nil];
self.theNewListing.dataDictionary = tempDictionary;
This is how I save it:
[self.presentingViewController dismissViewControllerAnimated:YES completion:^(void) {
[self.localContext MR_saveToPersistentStoreWithCompletion:^(BOOL success, NSError *error){
if(!success) {
NSLog(#"%#", error);
}
else {
[self.thePresentingVC refreshCollectionViews:nil];
}
}];
}];
I display my data in a collection view, and at this point everything looks fine. If I terminate and restart the data is still there.
If I fetch the entity again and update the attributes like this:
NSMutableDictionary *newTempDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:#"A new description", #"slFieldDescription", etc, etc, nil];
self.theNewListing.dataDictionary = newTempDictionary;
Then save using the same save code as above, and then update my collection view using the code below, all looks good.
self.listingsArray = [[NSMutableArray alloc] initWithArray:[Listing MR_findAllSortedBy:#"dateListed" ascending:NO]];
[self.mainCollectionView reloadData];
That is, until I quit the app and restart.
If you're wondering, I'm using FTASync, and this only supports MR 2.1, which is why I haven't upgraded to the latest version.
Thanks!
Not sure about MR, nor why you would need it. If that framework can give you the main context, just call the native Core Data save.
[context save:nil];
Cracked it!!
I noticed that my other attributes were saving, just not this one (this one holds all the data that is presented in the UI) and this led me on another line of investigation.
So, it seems that I needed to be working with immutable dictionaries to store this data as explained here:
Core Data saving problem: can't update transformable attribute (NSArray)