Sharing a plist file using iCloud
Asked Answered
R

2

7

I have a relativley simple app which persists data to a plist file located in the documents folder. The data loads into a UITableView at startup. The user can then edit, delete or add records and any changes get saved back to the plist file.

Now I would like to share this data (the plist file) across devices using iCloud. I have looked at the documentation and my understanding is that I need to create a UIDocument to "manage" the plist file.

I have looked at several iCloud tutorials however they all store a simple string within a property in the UIDocument class, not an entire file (like a plist).

How do I share my plist file (or any other file, for that matter) to iCloud using the UIDocument object?

Would I convert the plist file contents to NSData, then save that in a property in the UIDocument? Should I be using use NsFileWrapper instead?

I seem to be having a difficult time wrapping my head around the UIDocument/iCloud arrangement. I am probably making this more complicated then it really is.

Rumal answered 7/12, 2011 at 1:1 Comment(2)
Did you find an answer for this? I'm looking at doing the same thing and my app sounds very similar to yours. Please let me know if you found a good tutorial.Epiclesis
I am also trying to accomplish this. I have been modifying the tutorial with the NSString, but I can't get the second device to see the data.Tutelage
A
7

Not sure if anybody still needs a solution for that but I found a nice way to get this to work.

Since UIDocument only accepts Data as NSData or NSFilewrapper, I first created a Category for the NSDictionary Class that returns a NSDictionary from NSData. Here's the two files for the Category:

NSDictionary+DictFromData.h:

#import <Foundation/Foundation.h>

@interface NSDictionary (DictFromData)
+ (id)dictionaryWithData:(NSData *)data;
- (id)initWithData:(NSData *)data;
@end

and the NSDictionary+DictFromData.m

#import "NSDictionary+DictFromData.h"
@implementation NSDictionary (DictFromData)

+ (id)dictionaryWithData:(NSData *)data {
    return [[[NSDictionary alloc] initWithData:data] autorelease];
}

- (id)initWithData:(NSData *)data {
    NSString *tmp = nil;

    self = (NSDictionary *)[NSPropertyListSerialization
                            propertyListFromData:data
                            mutabilityOption:NSPropertyListImmutable
                            format:NULL
                            errorDescription:&tmp];

    NSAssert1(tmp == nil,@"Error in plist: %@",tmp);
    return [self retain];
}
@end

(source)

If you now import this Category in your UIDocument Subclass, you can easily load and save your Plist File to your iCloud container.

To load your Plist from iCloud add this to your UIDocument subclass (The Property contents is an NSDictionary):

- (BOOL)loadFromContents:(id)contents
                  ofType:(NSString *)
        typeName error:(NSError **)outError {

    if ([contents length] > 0){
        self.contents = [NSDictionary dictionaryWithData:contents];
    } else {
        self.contents = nil;
    }

    // call some Methods to handle the incoming NSDictionary 
    // maybe overwrite the old Plist file with the new NSDictionary

    return YES;
}

For saving your Data back to the iCloud add this:

- (id)contentsForType:(NSString *)typeName error:(NSError **)outError {
    NSData * plistData = [[[NSData alloc]initWithContentsOfFile:YOUR_PLIST_FILE]autorelease];
    return plistData;
}

If you now call:

[myUIDocument updateChangeCount:UIDocumentChangeDone];

YOUR_PLIST_FILE is getting synchronized. Remember that it takes about 10-15sec for your iCloud Container to update.

Apologete answered 6/10, 2012 at 12:50 Comment(0)
S
1

To use a plist with UIDocument, you can subclass UIDocument and override the following 2 methods with self.myDictionary (your plist) declared as a NSMutableDictionary.

- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError
{    
    if ([contents length] > 0) 
    {
        NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:(NSData *)contents];
        NSMutableDictionary *dataDictionary = [unarchiver decodeObjectForKey:@"data"];

        self.myDictionary = dataDictionary;
        [unarchiver finishDecoding];
        [unarchiver release];
    } 
    else 
    {
        self.myDictionary =  [NSMutableDictionary dictionary];
    }

    return YES;    
}

- (id)contentsForType:(NSString *)typeName error:(NSError **)outError 
{
    NSMutableData *data = [[[NSMutableData alloc] init] autorelease];
    NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
    if( !self.myDictionary )
    {
        self.myDictionary = [NSMutableDictionary dictionary];
    }
    [archiver encodeObject:self.myDictionary forKey:@"data"];

    [archiver finishEncoding];
    [archiver release];
    return data;
}
Sc answered 8/3, 2012 at 4:30 Comment(2)
This doesn't create a dictionary plist it creates an archive plist but is close enough. To create a real plist use NSPropertyListSerialization and you can choose either xml or binary output.Vitals
Also archive plists aren't compatible between Macs and iOS.Vitals

© 2022 - 2024 — McMap. All rights reserved.