As of 3.2 it appears that sqlite files are being cached. I discovered that when replacing a sqlite database file and loading its contents, stale data is returned from the overwritten database file. The contents of the new file are correct (as verified by NSFileManager contentsAtPath and on relaunch the new file is loaded correctly. A workaround is to use a new unique name rather than overwriting the existing name, suggesting that the files are being cached by name. This behavior occurs in both the simulator and on iPad (I assume iPhone as well). This is a real headache. Anyone know how to disable this "feature"?
As of 3.2 it appears that sqlite files are being cached. I discovered that when replacing a sqlite database file and loading its contents, stale data is returned from the overwritten database file.
Do you have an actual reproducible test case? I use sqlite3 for my 3.2 app and I'm not seeing this result at all. I assume be "overwritten" you mean that you are completely destroying the db and rewriting it, such as downloading it from a remote server. Do you close and reopen the database as well? If not your handle to the database will still point to the old file, even if it does not appear to exist on disk anymore.
Quote:
Originally Posted by ghoover
....occurs in both the simulator and on iPad (I assume iPhone as well).
Yes, I am downloading from a remote server and passing the data in like this:
close ensures that all sqlite handles are closed - I have verified that new handles are being created after the data is saved. The interesting part is if you uncomment the line that creates a new unique path, it works fine. And the code has worked on all prior SDK versions.
I ended up getting creative with file naming -- I append the date to the filename. You can then normalize on exit (or launch) (or just track the new name you're using, which is what I do).
I have been having this exact problem, can you post the code that you are using to get past this problem? Also, you mention appending time and date to the SQLite file as it is uploaded, how would you go about that? And then how would you go about removing it when the file is downloaded again??
Any assistance would be greatly appreciated, I have been looking everywhere for the past 2 weeks for this solution.
Just append the date to the path. You can determine how fine a resolution you need to avoid conflicts (ie. day/hour/minute/second) and then format the date accordingly. This is the code I use (Formatters is a class I use for formatting numbers and dates - you can use NSDateFormatter).
Looking at your original code above, I am assuming that 'path' is actually the variable that holds the default location to the applicationDocumentDirectory, is this correct?
Basically what I am trying to accomplish is;
I have two IBAction's, one is upload and the other is download.
By looking at the code above, it doesn't seem comLete, no defaultDocDirectory/tempDirectory, and how are you accessing the actual .sqlite file? Sorry if this all seams a bit noob, but i am to some degree and I really need to figure this out. Would it be easier if I posted both IBActions here? I am sure many others have the same questions.
Ok, here's a routine for getting the app support directory. I stick it in a category of NSFileManager.
Code:
enum
{
DirectoryLocationErrorNoPathFound,
DirectoryLocationErrorFileExistsAtLocation
};
NSString * const DirectoryLocationDomain = @"DirectoryLocationDomain";
- (NSString *)applicationSupportDirectory {
NSString *executableName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleExecutable"];
NSError *error;
NSString *result = [self findOrCreateDirectory: NSApplicationSupportDirectory
inDomain: NSUserDomainMask
appendPathComponent: executableName
error: &error];
if (error) {
NSLog(@"Unable to find or create application support directory:\n%@", error);
}
return result;
}
- (NSString *)findOrCreateDirectory:(NSSearchPathDirectory)searchPathDirectory
inDomain:(NSSearchPathDomainMask)domainMask
appendPathComponent:(NSString *)appendComponent
error:(NSError **)errorOut
{
//
// Search for the path
//
NSArray* paths = NSSearchPathForDirectoriesInDomains(
searchPathDirectory,
domainMask,
YES);
if ([paths count] == 0)
{
if (errorOut)
{
NSDictionary *userInfo =
[NSDictionary dictionaryWithObjectsAndKeys:
NSLocalizedStringFromTable(
@"No path found for directory in domain.",
@"Errors",
nil),
NSLocalizedDescriptionKey,
[NSNumber numberWithInteger:searchPathDirectory],
@"NSSearchPathDirectory",
[NSNumber numberWithInteger:domainMask],
@"NSSearchPathDomainMask",
nil];
*errorOut =
[NSError
errorWithDomain:DirectoryLocationDomain
code:DirectoryLocationErrorNoPathFound
userInfo:userInfo];
}
return nil;
}
//
// Normally only need the first path returned
//
NSString *resolvedPath = [paths objectAtIndex:0];
//
// Append the extra path component
//
if (appendComponent)
{
resolvedPath = [resolvedPath
stringByAppendingPathComponent:appendComponent];
}
//
// Check if the path exists
//
BOOL exists;
BOOL isDirectory;
exists = [self
fileExistsAtPath:resolvedPath
isDirectory:&isDirectory];
if (!exists || !isDirectory)
{
if (exists)
{
if (errorOut)
{
NSDictionary *userInfo =
[NSDictionary dictionaryWithObjectsAndKeys:
NSLocalizedStringFromTable(
@"File exists at requested directory location.",
@"Errors",
nil),
NSLocalizedDescriptionKey,
[NSNumber numberWithInteger:searchPathDirectory],
@"NSSearchPathDirectory",
[NSNumber numberWithInteger:domainMask],
@"NSSearchPathDomainMask",
nil];
*errorOut =
[NSError
errorWithDomain:DirectoryLocationDomain
code:DirectoryLocationErrorFileExistsAtLocation
userInfo:userInfo];
}
return nil;
}
//
// Create the path if it doesn't exist
//
NSError *error;
if (![self createDirectoryAtPath: resolvedPath
withIntermediateDirectories: YES
attributes: nil
error: &error]) {
if (errorOut) {
*errorOut = error;
}
return nil;
}
}
if (errorOut)
*errorOut = nil;
return resolvedPath;
}
You have a variety of options for performing the actual download. I use curl in my apps, but NSURLConnection, NSURLDownload, etc are viable options. Check google for tutorials on using these.
The result of your download will be an NSData object. You can write that to your target file where the file path is the application support directory with the dated filename appended to the end.
For example:
Code:
NSString *path = [[self applicationSupportDirectory] stringByAppendingPathComponent: filename];
BOOL ok = [fileManager createFileAtPath: path
contents: data
attributes: nil];
Reading and writing the sqlite file requires using the sqlite C api. There may be cocoa wrappers available but I haven't investigated that. There are plenty of good online sources for using the sqlite api. You'll need to understand SQL commands as well.