Question

I am trying to wrap my head around iCloud storage. I have read through the iCloud Design Guide and some questions here on SO. Here is my scenario:

I have a class that only has an NSMutableArray...that collection holds my custom objects which all adhere to NSCoding and saving locally works perfectly. That class is MasterList and has the property masterList, brilliant - I know :-p. When I looked on how to start implementing iCloud I thought KVS would be great, since my data footprint is extremely small. When I attempted this, I kept getting:

[NSUbiquitousKeyValueStore setObject:forKey:]: Attempt to insert non-property value <my custom objects>...

After seeing why that was, it seems you can only store scalar and P-List types with KVS. So I moved to using UIDocument and am struggling with it.

Bottom line -

For custom objects, do you have to use UIDocument, or is it possible to use KVS?

and

If I must use UIDocument, did any of you read a tutorial that was simple in nature (maybe stored a few props and loaded them back, maybe in a sample project?) that made it click for you?

Below is my code for using KVS if that helps at all. Not production or anything, just trying to get it to work:

//Load it
+(MasterList *)decodeMasterList:(MasterList *)objectToDecode
{

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

    NSURL *dataFile = [self pathForDocumentsFile:kFilePathAllLists];
    NSString *filePath = [dataFile path];

    objectToDecode = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];

    //First time runnign the app :-)
    if (!objectToDecode) objectToDecode = [[MasterList alloc] init];

    if ([defaults boolForKey:kIsUsingiCloud])
    {
        objectToDecode.masterList = [[[NSUbiquitousKeyValueStore defaultStore] arrayForKey:kiCloudPath] mutableCopy];

        //If we just started and nothing is there
        if (!objectToDecode.masterList)
        {
            objectToDecode.masterList  = [[NSMutableArray alloc] initWithObjects:[CustomList new], nil];
        }
    }

    return objectToDecode;
}

//Save it
+(BOOL)encodeMasterList:(MasterList *)objectToEncode
{
    @try
    {
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        if ([defaults boolForKey:kIsUsingiCloud])
        {
            [[NSUbiquitousKeyValueStore defaultStore] setArray:objectToEncode.masterList forKey:kiCloudPath];
            [[NSUbiquitousKeyValueStore defaultStore] synchronize];
        }

        NSURL *dataFile = [FileSystemHelper pathForDocumentsFile:kFilePathAllLists];
        NSString *filePath = [dataFile path];
        [NSKeyedArchiver archiveRootObject:objectToEncode toFile:filePath];

    }
    @catch (NSException *exception)
    {
        return NO;
    }

    return YES;
}
Was it helpful?

Solution

It's true that the key-value store can only hold property list types. But you mentioned the objects in your array all conform to NSCoding, so that's not a major problem. You just need to apply NSCoding to convert your objects to/from NSData, and store that.

Encode using

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:myObject];

Decode using

NSData *data = // from key-value store
MyClass *myObject = [NSKeyedUnarchiver unarchiveObjectWithData:data];

However if you're using UIDocument, the key-value store is probably not the best way to go. You can also store and sync documents directly in iCloud via NSFileManager and NSMetadataQuery. Apple provides pretty good documentation on this, and it would sync the document in-place instead of requiring you to convert it back and forth. Plus of course, the key-value store has a very low total size limit while documents are only limited by the capacity of the user's iCloud account.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top