iCloud Support in an application for Mac OS X Lion - objective-c

Within a Mac OS X (10.7 Lion) Non-Document Based application, I want to include iCloud support so I can share data across other instances of the same application on other macs (not to iOS devices). After surfing around the Apple documentation a bit, I've discovered I should use a key value list storage in iCloud, as the document I want to upload contains only an array of custom objects (that have simple properties such as a name (string), date (date object), ...). This file is the only thing I want to upload to iCloud. Within the application, I have already implemented saving the file to the disk using NSFileManager's - (void)writeData:(NSData*)data toPath:(NSString *)path (or whatever it was, I've forgotten). It is loaded from the file using NSFileManager again (using - (NSData *)dataInFileAtPath:(NSString*)path, or whatever it was). The file is stored in a subdirectory, in the user's Application Support directory. It is saved whenever a new item is added to the array, or an item in the array is modified.
I was wondering if anyone could provide a link to a tutorial, or point me in the right direction, to writing that file to iCloud, then downloading it again on other instances of the same application? All the tutorials and documentation I have found have only been for iOS. Any help will be greatly appreciated!
Thanks in Advance!
Ben

Just use NSUbiquitousKeyValueStore, which is a lot like NSUserDefaults except that it's written to iCloud. You should read the iCloud Storage section of the Mac App Programming guide, but what you need to do may be as simple as enabling entitlements in the Summary tab of your App Target in Xcode, and then doing something like:
NSData *dataToStore = [NSKeyedArchiver dataWithRootObject:yourArray];
[[NSUbiquitousKeyValueStore defaultStore] setData:dataToStore forKey:#"yourKey"];
Then, to retrieve your data, just do
NSData *retrievedData = [[NSUbiquitousKeyValueStore defaultStore] dataForKey:#"yourKey"];
NSArray *retrievedArray = [NSKeyedUnarchiver unarchiveObjectWithData:retrievedData];
The one caveat here is that for this to work with an array of custom objects, these objects must all implement the NSCoding protocol. This is just a matter of implementing two methods; here's a good tutorial.
P.S. You use the same APIs whether you're developing for OS X or iOS, so there's no reason why you couldn't just follow an iOS tutorial for iCloud storage.

Related

Reading NSUserDefaults from helper app in the sandbox

I found some resources on reading the NSUserDefaults of another application.
Objective-C NSUserDefaults caching prevents another app from accurately reading changes
NSUserDefaults: Is it possible to get userDefaults from another app?
Apparently, it's not possible.
However the questions firstly relate to iOS, and secondly the two apps are completely different.
I have a LaunchAtLogin helper app. But it does some other tasks too.
Therefore, the helper app should run always, but only start the main app if the BOOL in the NSUserDefaults is set.
Is there a way I can achieve that?
Since 10.7.4 you can use Application Groups within the sandbox. All applications within the group share the same sandbox. See Application Groups on how to set these up.
It's possible to share preferences between a main app and helper app using Security Application Groups and -[NSUserDefaults initWithSuiteName:]:
Security Application Groups
In order for multiple apps to share a common container, you'll want to set the com.apple.security.application-groups entitlement (in your main and helper app) to a common identifier, such as #"com.company.my-app-suite". See Adding an App to a Group for more information.
User Defaults Suites
As per the Foundation Release Notes for OS X 10.9:
For applications that are part of a Security Application Group, the NSUserDefaults "suite" APIs (-initWithSuiteName:, -addSuiteNamed: and -removeSuiteNamed:) will operate on a suite shared by applications in the group and stored in the group container, if the suite identifier is the identifier of the group.
So you'll want to do something like this in your application delegate (or similar):
- (NSUserDefaults *)sharedUserDefaults {
static NSUserDefaults *shared = nil;
if (!shared) {
shared = [[NSUserDefaults alloc] initWithSuiteName:#"com.company.my-app-suite"];
}
return shared;
}
And use that instead of [NSUserDefaults standardUserDefaults] throughout both your apps.
Apps can share a container directory on iCloud.
From Apple's doc on configuring your iCloud entitlements:
The iCloud Containers field identifies the list of container directories that your app can access in the user’s iCloud storage. (This field corresponds to the com.apple.developer.ubiquity-container-identifiers entitlement.) The strings you add to this list must correspond to bundle identifiers for apps created by your team. Xcode uses the current app’s bundle identifier to specify the first string; you can change this to a different bundle identifier if you want multiple apps to share a main container directory. You can also add additional bundle identifiers for your team’s other apps.

Can I programmatically wipe the application data in applicationDidFinishLaunching:withOptions:?

For testing purposes, I'd like to be able to just reset the application to a clean state. (Similar to what deleting the app from the Simulator / iPhone does).
Assume we have a WIPE_DATA define, if that is set, the app should start as if it has been just installed. Obviously if you know the app, you know where it stores data in NSUserDefaults etc. I was wondering if there was a more generic approach that requires no insight into the specific app and thus would be applicable to any app.
In your app you have two kinds of data - NSUserDefaults settings and files in Documents directory. If you want to wipe all the data to make your application to initial state and settings, you should create e.g. an app delegate method which set initial NSUserDefaults settings and clean application generated files in Documents directory.
As far as I know there is no standart system way to do that.

iCloud NSFileManager API - game score storage

I currently have an iOS application for which I would like to enable iCloud score storage so that users will have their game progress synced across their devices. The users progress is stored in multiple plist files in the sandboxes documents folder.
I read a few articles online about iCloud and the various "helper classes" (NSFileManager, UIDocument, NSFileCoordinator etc.) but am a little confused which one is the right one for me (does NSFileManager do the job or will i need to subclass UIDocument).
The API's are all a bit confusing to me.
You should use iCloud with Key-Value Data Storage. It's by far the simplest and most reliable at the moment, and suits your case perfectly.

Detect when a document has been uploaded or modified in iCloud on iOS app

I am looking for a simple way to detect if a document has been uploaded or modified in iCloud.
The solution I have found for the moment is to poll at given interval the /private/var/Mobile Doc... folder relative to the application and containing iCloud docs, and see if a new document has a recent modified date.
It basically works, but I would like to know if there is some kind of NSNotification, as it is for example in CoreData+iCloud, or if there are other ways I don't know.
Thanks.
A NSMetaDataQuery might be what you are looking for. Have a look at the Standford iOS videos (available at iTunes U). The NSMetaDataQuery is used in the Core Data and iCloud-Sessions to monitor the content of an iCloud directory for changes.

Get droplet path without making app document based

I have a fairly straightforward question about using droplets for my mac application. My app is not a document based app.
Is there any way to get the path of the item that was dropped? How would I go about implementing this if that was the case? I have looked into this, and it doesn't look like I can do droplets at all without making my app document based, which I really don't want to do.
Thanks ahead of time!
You don't have to use NSDocument at all to make a droplet. Just include CFBundleDocumentTypes in your Info.plist (see Storing Document Types Information in the Application's Property List for details) and implement application:openFile: in your app delegate (there are variants of this method for multiple files etc.).