My app requires data from a sqlite database. It will ship with a version of this database, but I need to update it on a regular basis (most likely once a month). Typically I've been sending updates for other parts of my app as XML through a bunch of webservices I've set up, but this particular database I'm working on now is pretty large (about 20-30 MB), and I'm getting timeout errors when I try to send it that way.
I tried putting the database on my companies server, then downloading it into an NSData
object. I then saved that data object to my app's Documents directory. When I restart my app, I get an error saying "File at path does not appear to be a SQLite database". Code for this is below.
// Download data
NSURL *url = [NSURL URLWithString:@"http://www.mysite.net/download/myDatabase.sqlite"];
NSData *fileData = [[NSData alloc] initWithContentsOfURL:url];
NSLog(@"%@",fileData); // Writes a bunch of 8 character (hex?) strings
// Delete old database
NSError *error;
NSURL *destination = [app applicationDocumentsDirectory];
destination = [destination URLByAppendingPathComponent:@"myDatabase"];
destination = [destination URLByAppendingPathExtension:@"sqlite"];
NSLog(@"destination = %@",destination);
[[NSFileManager defaultManager] removeItemAtPath:[destination path] error:&error];
// Save into correct location
[fileData writeToURL:destination atomically:YES];
//[NSFileManager defaultManager] createFileAtPath:[destination path] contents:fileData attributes:nil]; // I also tried this, it doesn't work either.
Is there a standard procedure for updating a large database that an app will use? I get the feeling that what I'm trying to do is incorrect and that there is a totally different way to do this, but I can't figure out what.
UPDATE:
I've tried a couple things to try to find the problem, but nothing is getting me any closer. I put the database I'm trying to download from my server onto a USB drive and then onto the mac I'm developing on, and into the Documents folder for my app in the Simulator. When I run my app by transferring the database this way, it runs without any problem and I can see all my data.
Conversely, I completely removed my app from the simulator and re-ran it, so it would create my database from scratch. I transferred this newly-made database from my mac onto my server with the USB drive. Once the file was on my server, I tried to run my "download update" in the code block above, but it still crashes the next time I start my app with the "File at path does not appear to be a SQLite database" error message.
From these tests, I'm confident that the problem is not with the database I'm trying to download. Something in my "download update" code is corrupting the database, but I still haven't been able to figure out why, nor have I found an acceptable alternate method to get my updates out to my users once my app launches.
UPDATE 2:
I've been doing some more searching, and I've learned that a user will need to put in a username and password to access any file on my server. I now believe that the NSData
I'm getting back from my code above is actually an authentication challenge, or something related to that, and that's why when I save it with the NSFileManager
it's not being recognized as a sqlite DB.
The simplest solution I've found to get through the authentication is here. When I try to do NSData *fileData = [NSData dataWithContentsOfURL:url options:NSDataReadingMapped error:&error];
(after changing my url
as suggested in the link, of course), I get an error NSCocoaErrorDomain Code=256
, which is just unknown error. My fileData
is nil
after doing this. I've noticed this post is quite old, so I don't know if it is still supported.
I also found a different solution to the authentication problem here using an NSURLConnection
instead of dataWithContentsOfURL
. This is much more recent, but there is some more advanced looking syntax used in that example that I haven't really seen before. I was able to pretty much copy-paste from the example, and my project will build without errors, but I don't know the proper syntax for using the fetchURL:withCompletion:failure:
routine from my UIViewController
. I tried
NSError *error;
NSData *fileData;
ExampleDelegate *download = [[ExampleDelegate alloc] init];
[download fetchURL:url withCompletion:fileData failure:&error];
but that gives a build error for sending an incompatible pointer. I think I can't pass a straight NSData
and NSError
as ExampleDelegateSuccess
and ExampleDelegateFailure
objects respectively, even though that what they are typedef
ed from. I probably have to do something to fileData
and error
to convert them, but that typedef
ing is one of the "advanced looking syntax" I was referring to above, and I don't really know what to do.
It's not included in his example, but I found another NSURLConnectionDelegate
method called connection:didReceiveAuthenticationChallenge:
here that I think I can just add into the code from the example, and that should solve my authentication issue. I feel if I just knew how to call fetchURL
I'd be set. Does anyone have any advice on what I need to do to get fileData
and error
into that call?
See Question&Answers more detail:
os