Overwrite with remote .plist a local .plist file

2019-04-02 12:32发布

问题:

My application loads a local list.plist file at launch.

Then it has a refreshTable button which fetch a remote version of the .plist file from my website.

App Launch
    local list.plist loads
       user hits refreshList button
          local list.plist is overwritten by remote list.plist
             local list.plist updated until remote list.plist updates again

Method to initialize data:

    //Remote data 
    list = [[NSMutableArray alloc] initWithContentsOfURL:[NSURL URLWithString:@"http://mywebsite.com/list.plist"]];
    NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)] autorelease];
    sortedList = [[list sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]] retain];

    //Local data
    NSString *localPath = [[NSBundle mainBundle] pathForResource:@"list" ofType:@"plist"];
   localList = [[NSMutableArray alloc] initWithContentsOfFile:localPath];
    NSSortDescriptor *localDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)] autorelease];
    localSortedList = [[localList sortedArrayUsingDescriptors:[NSArray arrayWithObject:localDescriptor]] retain];

This is the method to refresh:

- (void) refreshTable:(id)sender

{  
   //Remote .plist 
   list = [[NSMutableArray alloc] initWithContentsOfURL:[NSURL URLWithString:@"http://mywebsite.com/list.plist"]];
    NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)] autorelease];
   sortedList = [[list sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]] retain];
    [self.tableView reloadData];
    //now write remote plist to local plist
}

After i downloaded the remote plist how can i write over the local plist?

I was thinking to empty the local array containing the local plist and fill it with the remote array and i did it this way:

I solved in the way i thought:

 //Remote .plist 
    list = [[NSMutableArray alloc] initWithContentsOfURL:[NSURL URLWithString:@"http://phillipapps.com/mntlion/list.plist"]];
    NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)] autorelease];
    sortedList = [[list sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]] retain];

    NSLog(@"list: %@",list);
    [localList removeAllObjects];
    [localList addObjectsFromArray:list];
    localSortedList = [[localList sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]] retain];
    NSLog(@"locallist: %@",localList);
    [self.tableView reloadData];

It works, but how can i write over localList with the contents of list?

回答1:

so … after a view hours in chat we got the problem solved.

- (NSArray*)readPlist
{
    NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *plistPath = [[documentPaths lastObject] stringByAppendingPathComponent:@"localFile.plist"];

    NSFileManager *fMgr = [NSFileManager defaultManager];
    if (![fMgr fileExistsAtPath:plistPath]) {
        plistPath = [[NSBundle mainBundle] pathForResource:@"template" ofType:@"plist"];
    }
    return [NSArray arrayWithContentsOfFile:plistPath];
}

- (void)writePlist:(NSArray*)arr
{
    NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *plistPath = [[documentPaths lastObject] stringByAppendingPathComponent:@"localFile.plist"];

    NSFileManager *fMgr = [NSFileManager defaultManager];
    if (![fMgr fileExistsAtPath:plistPath])
        [fMgr removeItemAtPath:plistPath error:nil];

    [arr writeToFile:plistPath atomically:YES];
}

initializing the ivar:

self.plistArray = [self readPlist];

and after loading the new plist from the server you have to call this code:

self.plistArray = [NSArray arrayWithContentsOfURL:[NSURL URLWithString:@"http://mywebsite.com/list.plist"]];
[self writePlist:plistArray]; // e.g. for caching
[self.tableView reloadData];


回答2:

I do almost the exact thing you're looking for in an application of mine. You can't write to the NSBundle so the steps are something like this:

  1. Try to load plist from cache directory, if it succeeds go to 3.

  2. Load plist from bundle

  3. Check if loaded plist is up to date (or, trigger further steps by the press of a button)

  4. Download new plist

  5. Save to cache directory

The code looks something like this:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cache = [paths objectAtIndex:0];

ratios = [NSDictionary dictionaryWithContentsOfFile:[cache stringByAppendingPathComponent:@"devices.plist"]];
if (ratios == nil) {
    ratios = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"devices" ofType:@"plist"]];
}

NSString *device = [[UIDevice currentDevice] machine];
NSDictionary *d = [ratios objectForKey:device];
if (d!=nil) {
    pixelInchRatio = [[d objectForKey:@"pixelInchRatio"] doubleValue];
    bezelWidth = [[d objectForKey:@"bezelWidth"] doubleValue];
    bezelHeight = [[d objectForKey:@"bezelHeight"] doubleValue];
} else if (fetch) {
    [[[[RulerDimensionDownload alloc] init] autorelease] startDownload];
}

Then in the downloader it saves like so:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cache = [paths objectAtIndex:0];

[[NSFileManager defaultManager] createDirectoryAtPath:cache withIntermediateDirectories:YES attributes:nil error:nil];
[ratios writeToFile:[cache stringByAppendingPathComponent:@"devices.plist"] atomically:YES];

Some explanations:

The first few lines in each sample, the paths and cache definition, simply get the location of the cache directory for the application. I use this to store the most up-to-date version I'm my plist.

So, in the loading code, I first try to load the plist from the cache directory. If this fails (ratios is null) I then load it from my application bundle (this is the version of the plist that I ship with the app).

After that, I check to see if the plist has the information needed. (the plist has a definition for each device type. If the device isn't in the plist then I know I need to try to update the plist)

If the plist is out of date I start the download using a class I wrote: RulerDimensionDownload. Once it completes the download I save the file into the cache directory. Then, next time the plist needs to be loaded it will be loaded first and the shipped plist will never be looked at. (I also send a notification with the new plist)