Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Local Store vs iCloud Store -> Merging/Backup #4

Closed
peterknapp opened this issue Dec 19, 2013 · 8 comments
Closed

Local Store vs iCloud Store -> Merging/Backup #4

peterknapp opened this issue Dec 19, 2013 · 8 comments

Comments

@peterknapp
Copy link

First of all: Thank you so much for showing me how "easy" it is now to implement iCloud.

I know, that this stack is not a full implementation, but perhaps I can ask two things I see not clear.

If I understand it right, the data of a local store and the iCloud store is totally different. Meaning: By "switching" from a local to an iCloud store or back, I have to create the code for the migration/backup, right? In my case it is a very tiny data model with two very basic entities. So, when the user decides to switch off from iCloud for this App in settings, then he will loose the data as long as I did not implement merge/backup or he switches back to iCloud, correct?

Another thing: How can I determine the stages between "Using local storage: 1" and "Using local storage: 0"? I think, this is also important.

Perhaps you will find time and drop me a line. And again, thanks for the Stack!

@mluisbrown
Copy link
Owner

Hi,
Glad you found the stack useful. Yes, the data in the local store and the iCloud store is totally different. When you switch from local to iCloud or back you will need to do the migration yourself, and handle any duplicates that might occur as a result. You do the migrate (local to iCloud) using migratePersistentStore:toURL:options:withType:error: and including the NSPersistentStoreUbiquitousContentNameKey option in the options dictionary.

Duplicates will occur if you migrate back and forth, since CoreData doesn't know that record A in your local store is the same as record A in the iCloud store, so if you migrate back from iCloud to local, you'll get two copies of record A. How you de-duplicate depends on your data model.

To answer your last question, you can't determine the stages between "Using local storage: 1" and "Using local storage: 0" in any way that's of much use. You can get the notifications of all the internal store changes that Core Data is doing behind the scenes for you, but it's very tricky to be able to act on them in any reliable way, and they're a Core Data implementation detail which could easily change in future iOS versions so you can't depend on them. It would be very helpful if there were an official way to get more information about what was going on.

@peterknapp
Copy link
Author

Hi,

thank you for your message. It really was easy for me (still as a rookie) to implement iCloud functionality to my App with your code.

I think merging is one of the most difficult tasks in syncing. As far as I can see also in various other services this fails quite often. I will choose one of two options: 1) no merging and confront/explain the user this missing option or 2) I will import everything and will mark the "new" data somehow so the user can edit it by himself. It is a small data model so 2) could be possible.

With persistentStoreDidImportUbiquitousContentChanges I am able to detect changes to the store and act accordingly. But my concern is, when I add a new device (fresh install of the App) to my "iCloud chain" I will have to be notified if the iCloud store is used. This could take a while and I want to show the user some kind of a initial loading screen. Is this possible?

@mluisbrown
Copy link
Owner

I have exactly the same problem in my apps. There is a period of time between the user enabling iCloud and the data syncing from the cloud (the time between "Using local storage: 1" and "Using local storage: 0" where nothing appears to be happening. As far as I know there is no way to be notified of what's going on behind the scenes in a reliable way, so it's not possible to show a "loading" screen because you won't know when to get rid of it. It's very annoying.

@peterknapp
Copy link
Author

I figured out a way to do that (but it shows some magic) and I hope I can explain it correctly (and I made a screen cast, see below)

For testing, I designed the UI with a hidden TableView and a spinner in the navigation bar. In the view.m I placed a function where I set the hidden attribute to FALSE and the spinner to stopAnimation besides other things like updating the TableView etc.

I put a notification in storesDidChange when prior to this the notification to storesWillChange was fired. The notification executes the function in view.m which updates the view.

What happens:

  1. I make a fresh install of the app on the iOS simulator
  2. It starts up and there is no UI showing except the the navigation bar with the spinner spinning
  3. storesDidChange is firing
  4. "Using local storage: 1" in log screen
  5. storesWillChange is firing
  6. storesDidChange is firing but now posting the notification to the function in view.m
  7. "Using local storage: 0" in log screen with no change to UI and Spinner is spinning.
  8. Still UI shows hidden TableView and Spinner is spinning...

now the magic happens:
9. after a while without anything firing or changing in the code/app, the UI changes with showing the TableView with correct and full content and the spinner not spinning like I want to. Tadaaa...

I made a screen cast:
http://cl.ly/130T2q271K3A

It seems to me that the TableView somehow magically updates after the iCloud sync even though I told in the code to show the tableview immediately. I expected that the table view would be presented empty...

I wonder, what the magic part is :)

Peter

BTW: Hope you have had a good xmas.

@mluisbrown
Copy link
Owner

Thanks! That's very useful information! I have noticed similar "magic" in my app. On a storedDidChange notification I reinitialise my NSFetchedResultsController and call reloadData on my TableView. This happens when there isn't yet any iCloud data available. However, my TableView does somehow, magically, get refreshed once the iCloud data has all synced, even though I'm not getting any additional notifications (storesDidChange or storeDidImportUbiquitousContentChanges).

I'm going to implement your trick in an update yo my app, definitely makes it much nicer for the user. Thanks again!

PS: Hope you had a great xmas too :-)
Michael.

@awadhawan
Copy link

Hi Michael,

Can you give an example of the migratePersistentStore:toURL:options:withType:error: line? I'm trying to find out what the toURL: argument should be.

Is it the ubiquityContainerURL?

@mluisbrown
Copy link
Owner

Hi awadhawan,

For migrating local to iCloud (assuming you do this somewhere in PersistentStack.m):

__weak NSPersistentStoreCoordinator *psc = self.managedObjectContext.persistentStoreCoordinator;

NSPersistentStore *currentStore = [psc persistentStores][0];
[psc migratePersistentStore:currentStore
                     toURL:self.storeURL
                   options:@{ NSPersistentStoreUbiquitousContentNameKey : @"iCloudStore" }
                  withType:NSSQLiteStoreType
                     error:&error];

For migrating iCloud to local:

__weak NSPersistentStoreCoordinator *psc = self.managedObjectContext.persistentStoreCoordinator;

NSPersistentStore *currentStore = [psc persistentStores][0];
[psc migratePersistentStore:currentStore
                     toURL:self.storeURL
                   options:@{ NSPersistentStoreRemoveUbiquitousMetadataOption : @YES }
                  withType:NSSQLiteStoreType
                     error:&error];

The toURL: argument can be the same in both cases, as Core Data automatically puts an iCloud enabled store in it's own Ubiquity Container directory so there aren't conflicts between local and iCloud.

Also, take a look at this example from objc.io.

Michael.

@awadhawan
Copy link

Thanks a lot Michael. That makes it much simpler to understand since I was wondering how the "switch" would work in regards to turning iCloud on/off.

Excellent work with "PersistantStack". Helped a lot with understanding the simplicity involved in using iCloud with iOS 7.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants