When my iOS app is started for the first time, it initializes a few entities with default data. The same process is completed for every device of the same user when the app is first installed on the device. This leads to a problem with Simperium, because, even if I have a constraint on one of the entity's attributes, it create duplicates. How can avoid that from happening? Is there a way to make Simperium skip objects locally initialized ? Or to prevent it from inserting duplicates?
Would setting the simperiumKey of the entity to a custom constant value which is the same for all the devices of the user fix the issue? I mean, would that prevent Simperium from importing the very same entity from different devices that would produce duplicates and sync conflicts ?
Because I tried that option and it looks like the buckets on Simperium Server are ok, but when I dispose the view and I open it again the entities are not there anymore. When I restart the app they are back again... it looks like they desappear to reappear after resetting the app.. very strange.
UPDATE
The problem is that I get duplicated entities if I try to insert the same entity from different device. For example. When I setup my app for the first time, I have a function that initializes CURRENCY entities with codes and other things. The same operation would be done from a different device if the user decides to install the app on a new one. In this case, because the new device will initialize again the CURRENCY entities, those info will generate duplicates and conflict errors. I need a way to make Simperium understand that the entities locally inizialized in the devices must not be duplicated. I would remove the inheritance from SPManagedObject in order to stop Simperium from syncing the entities, but in their turn they have relations with other entities and that would definitively create problems with Simperium, because it will try to sync entities which have relations with objects not inheriting from SPManagedObject. Hope you now have a more clear idea.
After reading various posts on this subject I've understood that not signing out (that means: not calling signOutAndRemoveLocalData) and setting custom simperiumKey-s would prevent data duplication. I tested this solution and it apparently looks good. My app needs Simperium to sync data with other devices of the same user, but it mainly works with the local CoreData database. I hope this solution is good for this scenario.
Related
I'm using NSPersistentCloudKitContainer to synchronise data between different devices with CloudKit. It works perfectly well with a new project, however when I'm using it with old projects the old data which was added with NSPersistentContainer does not synchronise.
What I would like to achieve is to synchronise old data that was added with NSPersistentContainer after changing it to NSPersistentCloudKitContainer. Is it possible?
I've found a solution that works for my Core Data database - and mine is quite complex with multiple many-to-many relationships (A surgery/anaesthesia logbook app called Somnus)
I started by creating a new attribute for all my Core Data Entities called sentToCloud and setting it to FALSE by default in the Core Data model.
On the first load for an existing user:
Fetch request using the predicate "sentToCloud == FALSE" for each Entity type
Change sentToCloud to TRUE for each Object then save the MOC
This triggers NSPersistentCloudKitContainer to start syncing
I've done this in order of 'priority' that works for my database, assuming the iCloud sync sessions match the order in which Core Data is modified. In my testing this seems to be the case:
I first sync all child (or most child-like) Entities
Then sync their parents, and so on, up the tree
I sync the Object the user interacts with last, once everything else is in place so the relationships are intact and they don't think their data is borked while we wait for NSPersistentCloudKitContainer to reconnect all the relationships
I also leave any binary data (magically turned into a CKAsset behind-the-scenes) to last as it's not the most important part of my database
My database synced successfully from my iPad to iPhone and all relationships and binary data appear correct.
Now all I need is a way to tell the user when data is syncing (and/or some sort of progress) and for them to turn it off entirely.
ADDENDUM
So I tried this again, after resetting all data on the iCloud dashboard and deleting the apps on my iPhone & iPad.
Second time around it only synced some of the data. It seems like it still has a problem dealing with large sync requests (lots of .limitExceeded CKErrors in the console).
What's frustrating is that it's not clear if it's breaking up the requests to try again or not - I don't think it is. I've left it overnight and still no further syncing, only more .limitExceeded CKErrors.
Maybe this is why they don't want to sync existing data?
Personally, I think this is silly. Sometimes users will do a batch process on their data which would involve updating many thousands of Core Data objects in one action. If this is just going to get stuck with .limitExceeded CKErrors, NSPersistentCloudKitContainer isn't going to be a very good sync solution.
They need a better way of dealing with these errors (breaking up the requests into smaller requests), plus the ability to see what's going on (and perhaps present some UI to the user).
I really need this to work because as it stands, there is no way to synchronise many-to-many Core Data relationships using CloudKit.
I just hope that they're still working on this Class and improving it.
I am currently having an issue with Simperium on iOS with CoreData. Upon launching the app for the first time, Simperium tries to sync with CoreData.
Sometimes it will work 100% correctly, and other times it will set some of the relationships to "nil" despite the Simperium data on the server NOT saying that. This is then NEVER fixed unless I re-install the entire application. And then I roll the dice again to see if the relationships are linked properly on startup.
I cannot find a pattern in this. The relationships that are nil are completely random. Sometimes this entity over here will have nil relationships, and then the next time I try it a different entity will have nil relationships.
All relationships are optional and there is nothing wrong with my CoreData file.
Has anybody had an issue like this? I found the exact same issue in a bug from 2014, but it's been forever since then.
Exact same issue I am having can be found here: https://github.com/Simperium/simperium-ios/issues/250
Side Note: If you read that issue, he also clarifies that he has a problem with editing the CD file WHILE Simperium is syncing, causing nil relationships. Has anybody confirmed this happening? If so this might be my problem.
It's highly likely that this glitch is caused by the scenario you've described (editing the CD file while Simperium is Sync'ing).
Core Data deals, internally, with locks to maintain data integrity. Accessing the sql storage directly might result in data corruption (i'm assuming you're editing the file via either a Firefox plugin, in the simulator, or accessing the file via a Filesystem API).
Please, try to reproduce the issue without accessing the Core Data's sqlite file directly (always go thru NSPersistentStoreCoordinator / NSManagedObjectContext).
If you do succeed, and there's effectively a bug, we'd love to get it fixed! (In which case, please, open an issue the main repository, including as many details as possible).
Thanks for your interest in Simperium!
I have a rather big CoreData Model which suddenly behaves weirdly. There are three relevant entities:
User
Story
Group
User has a to-many relationship named stories to Story objects and a to-many-relationship groups to Group objects. Both also have an appropriate inverse. I use mogenerator to generate convenience accessors for all the properties and relationships. Both are ordered to-many relationships.
Now it sometimes happens that when I ask the user object for its groups (like user.groups) that I actually get an NSOrdered set of XNGStory objects. In the few cases that I have observed this they were the same objects that are returned by user.stories. The two sets weren't the same (different pointers), but their contents were (I checked by calling array] valueForKeyPath:#"objectID.URIRepresentation"] on them and got the same URIs in the same order both cases).
It feels like there is some way in my app that stores stories in the groups relationship but I checked the code and there are only Groups-related classes touching groups relationship and only story-related classes touching the story-relationship. Also there is no interaction or relationship between the two they are used in completely separate parts of the app, so I have no idea how this kind of data corruption can happen.
Is there any reason why such a data corruption might happen? I would have expected CoreData to complain when I store objects in the wrong kind of relationship accidentally.
One more thing: So far we were only able to reproduce it on iOS 10.
Things we tried:
We suspected a memory issue first (because an object of the wrong type turns up where it's not supposed to be), but running with Address Sanitizer and NSZombies enabled didn't reveal any issues. Also, once the issue occurs it persists over app restarts and using the debugger to poke around in the objects also shows a consistent(ly wrong) data model so it's not just one pointer that's wrong.
Deactivated WAL (issue still exists)
Looked at sqlite file (both with and without WAL) after crash with Core Data Editor: It displays Group-objects for the groups relationship, so all seems to be good on the sqlite-database-level -> Somehow the relationships get mixed up when loaded from the file.
As best I can tell, this seems to be an iOS 10 bug.
We have a similar problem with an app that uses Core Data - a User object has a relationship Addresses (to Address objects) and Friends (to other User objects).
For some reason under iOS 10 Core Data occasionally chooses to return the Address objects in the Friends relationship.
We've regression tested back to iOS 9 and this issue doesn't occur.
There's an open bug on Open Radar for the same issue - 26826183. Interestingly we've only started seeing this issue under 10.0.2 but that Radar report was based on an early iOS 10 Beta.
We have tested this extensively and also conclude that it is an iOS 10 bug. It also does not seem to be related to any of our code, just the data model. We were able to recreate the issue in an example project using our data model, Xcode 8's code generation and iOS 10's new NSPersistentContainer (before it broke using mogenerator and the "old way of setting up a CoreData stack").
To reproduce the issue we do the following:
At startup, if there is no User-object, create a User-object and add 10 Group objects to it and save.
Then add a Story to it, still on the main-context, and save.
Kill the app (via iOS swipe-up or simply via pressing "stop" in Xcode)
Start the app again. On startup, fetch the User object, add a Story object to to the stories relationship and save.
Check the user.groups relationship on a background or main context. It contains XNGStory objects now, although it should still have XNGGroup objects.
I filed a Radar with the example project. OpenRadar is here (without the example project, as it contains our data model).
The issue seems to be highly dependent on the data-model setup. It occurs when all-but-on relationships on our User entity are ordered. If all are ordered, it doesn't occurs, neither does it occur if two or more are unordered.
I m making an app with CoreData and iCloud integration. I went through the Apple Documentation to adapt my already working app to integrate iCloud synchronization.
Inserting objects and deleting seems to be working fine. When I delete the app and reinstall it on my phone all synchronized data are correctly restored.
My issue is when I modify an NSManagedObject. The context which I used to query CoreData to fetch my object does not detect any changes on my objects when I modify a field. Therefore the context is not saved. If I try to force saving context even when no changes are detected, nothing is saved.
I went through stack oververflow and found that the context must have a stalenessInterval set to 0.0. This did nothing to my app. Do you have any idea on what could be wrong ?
I noticed that the context carried by the NSManagedObject seems different than the one I used to fetch data. If I call save method on this context, nothing happened either.
I am completely lost, since I thought it would ba as easy as inserting and deleting objects.
Thanks for your help !
(ps: I code with Swift but even Objective C code is acceptable as answer :) )
I was actually trying to use two databases whereas I should have used configurations to separate entities saved to the cloud and those saved locally.
For those who wants more information on CoreData with iCloud i suggest to go through this video from Apple WWDC 2012 which helps a lot getting into this subject.
I've read in another answer that Simperium should be able to handle Core Data lightweight migrations fine. However, I'm currently struggling with the simple case of adding an (optional) property to an existing entity.
To make the issue a bit easier to follow, let's go through an example. Let's say that my previous app version is 1.0 and a new version 1.1 introduces a new property foo on a Core Data entity.
Now, let's consider this scenario:
Device A and B both run version 1.0 and are attached to the same Simperium sync account, both up-to-date.
Device A upgrades to version 1.1, the property is created in the data base, and the user adds some data to the new foo property. This data is correctly synced to the Simperium backend and foo is visible in the web data browser.
Device B (still on version 1.0, i.e. without foo) syncs with Simperium. At this point I see Simperium warning: applyDiff for a member that doesn't exist, which is understandable because foo does not exist. Everything still expected and fine here.
Now device B upgrades to 1.1. When starting the app for the first time, Core Data creates the new foo column. However, now that the foo property is there, Simperium still does not pull its data from the backend, so devices A and B don't see the same data for foo!
I understand why this happens (Simperium has discarded the change the first time, because the property wasn't there yet, and then doesn't apply the same change again later when it could). However, I think this is quite problematic and effectively makes even the simplest changes to the data model very risky. Am I missing anything here? What would be a safe way to add a new property to an entity?
Sorry it took us a while to reply (please, by all means, feel free to open an issue on Github, anytime!).
For the time being, there are few caveats when it comes to Migrations.
I've filed this issue, to properly handle the New Attributes scenario, and this issue to deal with attribute renames.
Right now, although the protocol is resilient, and sync'ing should recover on its own (in the eventuality in which something happens, and a client goes off sync, ie. migrations), an attribute rename will cause glitches.
For now, i'm afraid that the easiest // safest way to deal migrations, would be to create another bucket, that would have the desired set of attributes.
Sorry about this inconvenience, we'll be iterating over this!