UPDATE 11/18/2011 Check the accepted answer. It works and its a life saver!开发者_如何学Python
Hey everyone... so Im trying to figure out how to do this. I have bounced around alot of forums to try and find my answer, but no success. Either their process it too complicated for me to understand, or is just overkill. What I am trying to do is this. I have an XML file within my app. Its a 500k xml file that i dont want the user to have to wait on when loading. SO... I put it in my app which kills the load time, and makes the app available offline. What i want to do is, when the app loads, in the background (seperate thread) download the SAME xml file which MIGHT be updated with new data. Once the xml file is complete, i want to REPLACE the xml file that was used to load the file. Any suggestions or code hints would be greatly appreciated. Thanks in advance!
You could use an NSOperation
to download a file asynchronously.
You can use NSFileManager
to save the file to your application's Documents directory.
Edit:
You are trying to write to a file in your application bundle. You can't write to any files in the bundle -- it's part of the Apple security sandbox model.
You should place the file in your application's Documents directory and write to it there.
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectoryPath = [paths objectAtIndex:0];
NSString* filePath = [documentsDirectoryPath stringByAppendingPathComponent:@"myfile.xml"];
Do you have control over the XML on the server? Instead of downloading the XML file, whether or not it is different, make a SHA1 signature of the file, and compare it with the SHA1 signature of the file you have already cached.
If the signatures are the same, there's no need to burden the user with a second, senseless download (especially on WWAN networks). If the signatures are different, only then would you fire a download request in the background.
I ended up figuring this out. Its an old post but it appears to be getting alot of views still so I thought I'd help some people out. Keep in mind this code is NOT perfect... there are many ways to skin a cat.. and this is how I skinned mine. It works, thats all that matters!
In a nutshell how it works is... The app launches > checks to see if there is a saved "date/ID". It will be in the format of "MMDD". If there is not a saved date/ID it will create a blank one to compare later.
When it compares if the id doesnt match, it will download a new file. Todays date is 11/18/2011 so the ID would be "1118". If you launch the app again today, the IDs will match so the app will parse the local xml file instead of downloading another one. (its a huge time saver for me, my XML file is 2-4 mbs.)
This code will download a new xml file daily, UNLESS the app has already been ran for the day. Then it will use the local file.
////////// applicationDidFinishLaunching ///////////
lastSyncArray = [[NSMutableArray alloc] initWithCapacity:0];
//check documents directory to see if you have a saved "date/ID" from earlier
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [paths objectAtIndex:0];
NSString *fullFileNameSavedLastSyncArray = [NSString stringWithFormat:@"%@/lastSyncArray", docDir];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:fullFileNameSavedLastSyncArray];
if (fileExists == YES) {
// yes you have a saved "date/ID" from earlier
NSArray * temp = [[NSArray alloc] init];
temp = [NSKeyedUnarchiver unarchiveObjectWithFile:fullFileNameSavedLastSyncArray];
for ( int i=0; i<[temp count]; i++ ){
[lastSyncArray addObject:[temp objectAtIndex:i]];
}// end for loop
}
else {
//insert blank data for comparison later
[lastSyncArray addObject:@""];
}
[NSThread detachNewThreadSelector:@selector(checkXMLFile) toTarget:self withObject:nil];
/////////// end applicationDidFinishLaunching /////////////
// own function in App Delegate
-(void)checkXMLFile {
//get current month/day
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit fromDate:[NSDate date]];
NSInteger day = [components day];
NSInteger month = [components month];
NSString * currentTimeString = [NSString stringWithFormat:@"%d%d",month,day];
//check if file exists first
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString * fileName = [NSString stringWithFormat:@"inventory.xml"];
NSString *xmlPath = [documentsDirectory stringByAppendingPathComponent:fileName];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:xmlPath];
if (fileExists == YES) {
//yes there is a saved xml file
NSLog(@"There is a saved file from before");
//compare last sync date and current date. if they mismatch... download new file
NSString * sToCompare = [[lastSyncArray objectAtIndex:0] description];
if ([sToCompare compare:currentTimeString]==0) {
//its a match this has been downloaded today. no need to re download.
}
else {
//clear out old download date and save new
[lastSyncArray removeAllObjects];
//save new date
[lastSyncArray addObject:currentTimeString];
// we are downloading a new xml file and saving it
NSString * pdfURL2 = [NSString stringWithFormat:@"http://myfile.com/inventory.xml"];
NSString *urlString = pdfURL2;
NSURL *url = [NSURL URLWithString:urlString];
NSData * xmlData = [NSData dataWithContentsOfURL:url];
if (!xmlData) {
}
else{
NSLog(@"EXISTS");
NSLog(@"Begin to save xml");
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString * fileName = [NSString stringWithFormat:@"inventory.xml"];
NSLog(@"File to save: %@",fileName);
NSString *xmlPath = [documentsDirectory stringByAppendingPathComponent:fileName];
[xmlData writeToFile:xmlPath atomically:YES];
NSLog(@"XML saved");
}
}
}
else {
NSLog(@"MISSING XML");
//no there is not a saved xml file this must be first load go ahead and download
NSString * pdfURL2 = [NSString stringWithFormat:@"http://myfile.com/inventory.xml"];
NSString *urlString = pdfURL2;
NSURL *url = [NSURL URLWithString:urlString];
NSData * xmlData = [NSData dataWithContentsOfURL:url];
if (!xmlData) {
}
else{
NSLog(@"EXISTS");
NSLog(@"Begin to save xml");
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString * fileName = [NSString stringWithFormat:@"inventory.xml"];
NSLog(@"File to save: %@",fileName);
NSString *xmlPath = [documentsDirectory stringByAppendingPathComponent:fileName];
[xmlData writeToFile:xmlPath atomically:YES];
NSLog(@"XML saved");
}
}
[self sendToParser];
}
- (void)sendToParser{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString * fileName = [NSString stringWithFormat:@"inventory.xml"];
NSString *xmlPath = [documentsDirectory stringByAppendingPathComponent:fileName];
[self parseXMLFileAtURL:xmlPath];
[pool release];
}
- (void)parseXMLFileAtURL:(NSString *)URL //URL is the file path (i.e. /Applications/MyExample.app/MyFile.xml)
{
//you must then convert the path to a proper NSURL or it won't work
NSURL *xmlURL = [NSURL fileURLWithPath:URL];
NSData * data = [[NSData alloc] init];
data = [NSData dataWithContentsOfURL:xmlURL];
// here, for some reason you have to use NSClassFromString when trying to alloc NSXMLParser, otherwise you will get an object not found error
// this may be necessary only for the toolchain
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
// Set self as the delegate of the parser so that it will receive the parser delegate methods callbacks.
[parser setDelegate:self];
// Depending on the XML document you're parsing, you may want to enable these features of NSXMLParser.
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
[parser parse];
[parser release];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespace qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
//your code here
return;
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
//your code here
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespace qualifiedName:(NSString *)qName
{
//your code here
return;
}
- (void)parserDidEndDocument:(NSXMLParser *)parser{
//your code here
}
////////// application will terminate /////////////
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [paths objectAtIndex:0];
NSString *fullFileNameSavedLastSyncArray = [NSString stringWithFormat:@"%@/lastSyncArray", docDir];
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL fileExists fileExists = [[NSFileManager defaultManager] fileExistsAtPath:fullFileNameSavedLastSyncArray];
//check to see we have a last sync array
fileExists = [[NSFileManager defaultManager] fileExistsAtPath:fullFileNameSavedLastSyncArray];
if (fileExists == YES) {
//file exists delete it so we can recreate it here in a sec
[fileManager removeItemAtPath:fullFileNameSavedLastSyncArray error:NULL];
}
if ([lastSyncArray count] >=1) {
[NSKeyedArchiver archiveRootObject:lastSyncArray toFile:fullFileNameSavedLastSyncArray];
}
////// end application will terminate ///////////
精彩评论