Does UIManagedDocument handle conflcts - cocoa-touch

Does UIManagedDocument handle row level conflcts for me or do I need to handle those, and his answer was it does handle row level conflicts. Maybe I misunderstood him but I am not seeing this.
So I am going to ask a few key questions in hopes of getting some clarification here, again this is UIManagedDocument
If I have a table Author having first and last name fields what happens if:
A) I have a row with author: 'Jon Do' - it has sync'd to two devices. Then I edit the first name on one device from 'Jon' to 'John' and edit the last name on the other device from 'Do' to 'Doe'. How will iCloud and UIManagedDocument handle this? Will I get some sort of a notification that I need to respond to in order to handle the conflict? I tried the following code but I never get the notificiation:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(_ondocumentStateChangedNotification:) name:UIDocumentStateChangedNotification object:self.openedDoc];
B) What if I add a different author to the two devices so that there are now two rows to be merged will UIManagedDocument handle that for me or do I need to do something myself? If I need to do something myself what do I need to do? I tried signing up for the NSPersistentStoreDidImportUbiquitousContentChangesNotification but that doesn't ever seem to come through for me either. Which context do I sign up against? I tried this:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(_onPersistentStoreDidImportUbiquitousContentChanges:) name:NSPersistentStoreDidImportUbiquitousContentChangesNotificationobject:self.openedCaddy.managedObjectContext.parentContext];
Also do I need to handle the NSManagedObjectContextDidSaveNotification myself for a UIManagedDocument or does UIManagedDocument handle that for me?
Any and all help is greatly appreciated I am really struggling here. I adopted iCloud and UIManagedDocument at the launch of iOS 5 and fumbled my way though the lack of documentaiton then but managed to ship a product but now I want iCloud to really work not simply do a winner takes all approach to document sync.

I figured it out. UIManagedDocument does handle row level conflicts.
The reason I never saw any name:NSPersistentStoreDidImportUbiquitousContentChangesNotificationobject notifications is because I was not setting the moc's persistent store options properly. To get core data to start syncing you have to set at a minimum the following options: NSPersistentStoreUbiquitousContentNameKey & NSPersistentStoreUbiquitousContentURLKey.
When I first started using iCloud 2 years ago I always assumed that the call to setUbiquitous:itemAtURL:destinationURL:error: did this for me and was equivalent but it's not. setUbiquitous:itemAtURL:destinationURL:error: simply tells iCloud to sync the files at that URL, it does nothing with core data - think dropbox. To get the core data portion of UIManagedDocument to sync those two options need to be set.
I have started a github repo that clarifies some of this and makes getting up and running with UIManagedDocument much easier. http://github.com/dtrotzjr/APManagedDocument
Hopefully this helps someone else. I feel rather dumb not realizing this sooner but when I first implemented iCloud sync for iOS 5 2 years ago the documentation was sparse and iCloud didn't work very well so I assumed my behavior was normal.

Related

Prevent NSDocument saving in temporary dictionary

I have an app with subclass of NSDocument that has overridden method writeToURL:(NSURL *) ofType:(NSString *) error:(NSError **) which saves data at given NSURL location, but also can save additional file (with appended .my2ext) with debug information. Previously it worked well (I created the app several years ago), but now I see that instead of user selected location the method gets some temporary directory:
file:///var/folders/yv/gwf3_hjs0ps7sb3psh3d0w3m0000gn/T/TemporaryItems/(A%20Document%20Being%20Saved%20By%20MyApp%202)/myfilename.myext
Then, as I understand, the framework relocates the main file (at given url), but the additional file gets lost. So, can I somehow obtain the user selected path to save directly into it? Or prevent using temp directories at all?
I've already turned off the SandBox mode, but this didn't help. I also know that I can use "File Package" approach, but my app is created for a few people only, so, there is not interest in good production approach, only in simplicity.
I tried to google any possible solution, but found nothing helpful or just related. Even the documentation says nothing about using temporary directories! So, I decided to override different NSDocument methods. After several experiments I almost lost hope, but then I found that the method
saveToURL: ofType: forSaveOperation: delegate: didSaveSelector: contextInfo: provides real, user selected location. And this finally solved the problem.

How to test iCloud synchronisation between Cocoa app and iOS app

My problem is simple:
I'm now developing Cocoa application which stores data in iCloud (key value store). In order to test iCloud synchronisation I've build a simple iOS app, and installed it in my device.
I did iCloud initialisation in cocoa app:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dataUpdatedFromCloud:) name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification object:[NSUbiquitousKeyValueStore defaultStore]];
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
and selector:
+(void)dataUpdatedFromCloud:(NSNotification *)notification {
NSLog(#"dataUpdatedFromCloud: %#", [[NSUbiquitousKeyValueStore defaultStore] objectForKey:#"theKey"]);
}
in order to catch data when it comes from iCloud.
I do everything same in iOS app, with extra action when I store data in NSUbiquitousKeyValueStore:
// Script 1
[[NSUbiquitousKeyValueStore defaultStore] setDictionary:#{#"id":#1} forKey:#"theKey"];
My testing scenario is:
Run cocoa application in Xcode
Run iOS app in my device
In iOS app I have a button, which calls Script 1
Here I expect dataUpdatedFromCloud is called in 10-20 seconds on Cocoa app, but it doesn't!!!
Am I doing everything correct, or maybe I understood something incorrectly?
Well, probably, but reasons it might fail include:
The iCloud container ID is not exactly the same on the two apps. On the one hand this is obvious, but on the other it's the easiest thing to screw up, so check again.
The iCloud container ID is OK but the provisioning profile doesn't actually enable iCloud in both apps. You should check ubiquityIdentityToken in both apps to verify this.
The value has already been synced, for example, when the app wasn't running. The notification you're looking for only gets posted if the change happened while the app was running and if the new value is different than the old one. Since you're always setting #1 as the value, it's entirely possible that the value has already been received and that new attempts aren't changing anything. For testing, it might help to replace #1 with something like #([NSDate timeIntervalSinceReferenceDate]) just to make sure you get a different value every time.
iCloud is temporarily confused/down/broken/just plain slow. Although 10-20 seconds is reasonable, there's no performance guarantee. If it shows up after 30 seconds, or even 30 minutes, it's still working as advertised.
I've had some success with gummed up iCloud sync by resetting iCloud's documents and data as described here: http://support.apple.com/kb/HT5824
Sometimes iCloud gets confused about what it has already synced and where—testing, particularly when you're making repetitive identical updates, makes that more likely in my experience.

Only receiving "File Written To" notifications from VDKQueue regardless of activity

I am trying to implement VDKQueue but only get ‘VDKQueueFileWrittenToNotification’ back as the notification regardless of the file activity in the watched folder. Deletes, file size changes all report back as this same message.
I think everything is set up OK, but maybe not…
[self.theQueueWatcher setDelegate:self];
self.theQueueWatcher.alwaysPostNotifications=YES;
[self.theQueueWatcher addPath:self.hotFolderPath notifyingAbout:VDKQueueNotifyDefault];
This is on 10.8.2.
Does anyone know if anything underlying in the OS has changed which would cause this? Or what I am missing?
After contacting the author of VDKQueue, he helpfully(seems like a nice guy) pointed out the purpose of kQueue, and therefore VDKQueue, was to watch an individual file for changes etc, not a folder as I was doing. So now starts the voyage into FSEvents which Bryan recommended was the best way to achieve this task.
Thanks Bryan.
Hope someone else finds this useful.

ALAssetsGroup becomes invalid

I'm using AssetsLibrary framework for saving assets to a specific album (ALAssetsGroup).
Since I'm using the ALAssetsGroup (for the album where I want to save the assets) quite often, I figured it would be wise to retain it, so I don't have to iterate (asynchronously) through the groups each time I need it.
When retrieving/creating the album everything shows up correctly (for valueForProperty:), but the group seems to invalidate itself after some time, and all its properties will return nil. Also, addAsset: won't work on it, so I have to search for the album again (this time it's searched by its URL, but it's still asynchronous).
Is there a way around this (to keep the ALAssetsGroup valid)?
This is happening on iOS 5, and the library (ALAssetsLibrary) is retained as well.
I am using a setter to make sure that I don't reset the group myself.
You need to add an observer for the ALAssetsLibraryChangedNotification for your ALAssetsLibrary and upon receiving it re-query for any of your cached AL* objects. That will be posted immediately before the vended AL* objects become invalid.

Storing objects in iOS for later use

My app is pulling in JSON data from our web service. In all instances up til now, the information would be retained in memory or just refreshed on the fly with no need to retain anything locally other than the login token used in the API. With one of the new features we are adding, we will be taking in a group of locations, 26 max, with long, lat, radius, and name.
I also need to add 1-2 fields to this data to create a larger object. So my question is, what would be the best way to store this type of data in the iOS filesystem? Currently I have been using the NSUserDefaults, but that seems sort of limited or ill advised for larger amounts of data. Maybe not.
This data will need to be retrieved, changed or edited, and resaved. All of this while still retaining the ability to pull any of those 26 objects. Thank you in advance for reading and helping out.
For such a small amount of data (26 items) I suggest archiving.
Save to plist using NSKeyedArchiver/NSKeyedUnarchiver. Read your data from the delegate's didFinishLaunchingWithOptions, and listen for UIApplicationWillResignActiveNotification to save it.
A NSUserDefaults is a plist with features designed to store user preferences. It's often used instead a regular plist to save a couple of lines of code, which I think it's a bad idea because you get an additional complexity unrelated to your task.
If you want the login to be protected against someone stealing the device and performing forensics use the Keychain. You may want to use a wrapper and read some articles, comment if you are interested.
If you look for more features see Best way to store data on iphone but doesn't seem to be the case now.
Some code to get you started... Register to call save on app resign:
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(saveMyData)
name:UIApplicationWillResignActiveNotification
object:nil];
On each object of the graph/dictionary/whatever you want to archive implement NSCoding:
- (void)encodeWithCoder:(NSCoder*)coder {
[coder encodeObject:myIvar forKey:kmyIvar];
}
- (id)initWithCoder:(NSCoder*)coder {
if((self = [super initWithCoder:coder])) {
self.myIvar = [[coder decodeObjectForKey:kmyIvar] retain];
}
return self;
}
Check out this guide on core data. It's the best way to store data locally on your device. It's a native cocoa API and it is bindings compatible. Plus, you can choose whether to store data as XML, SQLite, and Binary.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/cdProgrammingGuide.html
For anything remotely large, I would use this.
I had this same question and just figured out a much better solution.
What you could do is just store the JSON string as your NSUserDefault. Then, when you reload the app use the same method (or framework utility) you used to map the JSON string to your objects the first time. This way you can still take advantage of the ease of NSUserDefaults.
If you're using RestKit to manage your web services it gets even easier. The answer on this post shows how to use RestKit's JSON parser to map from JSON to your object.
Deserializing local NSString of JSON into objects via RestKit (no network download)