I have a weird problem. My app works fine on my device but the sqlite database does not work on the simulator. The file "database.sqlite" exists in the same folder as my apps, it has the same name and the columns names are also correct.
So I assume there is something wrong with the configuration but I don't know what. Can someone please help me out.
Thanks
Here are some posts that seem to address the problem : http://forums.macrumors.com/showthread.php?t=484899
One reason might be because you should ensure the database is copied from your Supporting Files application directory (read only) to library or documents before you use it. Here's an ensurePrepared function from a sample of mine that uses sqlite that does just that. In this case, it's called contacts.db
- (BOOL)ensureDatabasePrepared: (NSError **)error
{
// already prepared
if ((_dbPath != nil) &&
([[NSFileManager defaultManager] fileExistsAtPath:_dbPath]))
{
return YES;
}
// db in main bundle - cant edit. copy to library if !exist
NSString *dbTemplatePath = [[NSBundle mainBundle] pathForResource:#"contacts" ofType:#"db"];
NSLog(#"%#", dbTemplatePath);
NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
_dbPath = [libraryPath stringByAppendingPathComponent:#"contacts.db"];
NSLog(#"dbPath: %#", _dbPath);
// copy db from template to library
if (![[NSFileManager defaultManager] fileExistsAtPath:_dbPath])
{
NSLog(#"db not exists");
NSError *error = nil;
if (![[NSFileManager defaultManager] copyItemAtPath:dbTemplatePath toPath:_dbPath error:&error])
{
return NO;
}
NSLog(#"copied");
}
return YES;
}
Related
I have an iOS app that uses sqlite, the sqlite database file is preconfigured and added in xcode. Everything works great until I need to add a new table named "activities" in this sqlite file, so I added the table for the database file, replaced the database file in xcode, and now while running on either simulator or real iOS device, it keeps complaing "no such table: activities". Even if I delete the previously installed app on my device or simulator doesn't work. What is happening there? By the way, below is the code I deal with the database file everytime I instantiate my DatabaseManager:
- (instancetype)initWithDatabaseFilename:(NSString *)dbFilename {
self = [super init];
if (self) {
// Set the documents directory path to the documentsDirectory property.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
self.documentsDirectory = [paths objectAtIndex:0];
// Keep the database filename.
self.databaseFilename = dbFilename;
// Copy the database file into the documents directory if necessary.
[self copyDatabaseIntoDocumentsDirectory];
}
return self;
}
- (void)copyDatabaseIntoDocumentsDirectory {
// Check if the database file exists in the documents directory.
NSString *destinationPath = [self.documentsDirectory stringByAppendingPathComponent:self.databaseFilename];
if (![[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) {
NSLog(#"file not exists");
// The database file does not exist in the documents directory, so copy it from the main bundle now.
NSString *sourcePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:self.databaseFilename];
NSError *error;
[[NSFileManager defaultManager] copyItemAtPath:sourcePath toPath:destinationPath error:&error];
// Check if any error occurred during copying and display it.
if (error != nil) {
NSLog(#"%#", [error localizedDescription]);
}
} else {
NSLog(#"file is already there");
}
}
Delete database file from document directory through terminal path generated in copyDatabaseIntoDocumentsDirectory method drag and drop database file in project.make sure tables are created through terminal.
I have a newsstand app which has magazines and uses the newsstand framework. I realized there was something wrong when deleting the magazines and/or when downloading them because when I accessed settings/usage my app keeps growing in memory usage when downloading and deleting the same magazine.
Found the issue... when downloading the issue in the delegate method:
-(void)connectionDidFinishDownloading:(NSURLConnection *)connection destinationURL:(NSURL *)destinationURL
I just needed to add something like this at the end:
NSError *error;
[[NSFileManager defaultManager] removeItemAtPath:[destinationURL path] error:&error];
if (error){
NSLog(#"ERROR:%#", error);
}
Even the directory is called "caches" you need to manually delete. Ok problem solved but what about the customers who already download my app and have tons of MBs dead in the cache directory.
I wanted to know how to get this directory and delete everything on it at launch and only once...
I can do it only once using a NSUserdefault but how do I get this directory and delete any zip files in it... an example of this directory and a file within is:
/private/var/mobile/Applications/1291CC20-C55F-48F6-86B6-B0909F887C58/Library/Caches/bgdl-280-6e4e063c922d1f58.zip
but this path varies with the device. I want to do this at launch so I'm sure there are no downloads in progress but any other solutions are welcome, thanks in advance.
Everything that you need is enumerate all files from Caches directory and remove ones that have zip extension:
- (void)removeZipFilesFromCachesDirectory {
static NSString *const kZIPExtension = #"zip";
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *cachesDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSError *error = nil;
NSArray *fileNames = [fileManager contentsOfDirectoryAtPath:cachesDirectoryPath error:&error];
if (error == nil) {
for (NSString *fileName in fileNames) {
NSString *filePath = [cachesDirectoryPath stringByAppendingPathComponent:fileName];
if ([filePath.pathExtension.lowercaseString isEqualToString:kZIPExtension]) {
NSError *anError = nil;
[fileManager removeItemAtPath:filePath error:&anError];
if (anError != nil) {
NSLog(#"%#", anError);
}
}
}
} else {
NSLog(#"%#", error);
}
}
Here's what I need: To have a sqlite file populated with example entities that I made on the iPhone simulator, and then copy that file when the app initially runs for all my users.
What I've done:
I created a bunch of entries within the simulator.
I found the sqlite file attached to my app within the iPhone Simulator iOS folder on the MAC.
From the three files, .sqlite, .sqlite-shm, .sqlite-wal I simply copied the .sqlite file to my xCode project.
When I ran the app, the .sqlite file showed up empty!
How do I fix this?
Thank you!
EDIT:
What significance does the .sqlite-wal and .sqlite-shm have?
Why do they exist and why did not exist prior to iOS7?
first steps R OK but then U have to load the database
U need smth like this:
- (void)copyPreparedDatabase{
__persistentStoreCoordinator = nil;
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"DATABASE.sqlite"];
NSString *storePath = [storeURL path];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"DATABASE" ofType:#"sqlite"];
if (defaultStorePath) {
NSError *error = nil;
if ([fileManager fileExistsAtPath:storePath]) {
[fileManager removeItemAtPath:storePath error:&error];
}
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:&error];
NSDictionary *fileAttributes = [NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey];
if (![[NSFileManager defaultManager] setAttributes:fileAttributes ofItemAtPath:storePath error:&error]) {
}
}
}
And then U call it from - (NSPersistentStoreCoordinator *)persistentStoreCoordinator from
AppDelelate.m
Advice: Do some custom switch like
#define IMPORT_PREPARED_DATABASE
do it like this:
if (![fileManager fileExistsAtPath:storePath] && !IMPORT_PREPARED_DATABASE) { //&& 1==2
[self copyPreparedDatabase];
}
so U can control when to build new prepared database or when to use existing one....
Note:
When U build new prepared database sto simulator, copy database and paste it over the old one...
This tutorial can be a big help.
You made almost everything, but you are missing an important step. You need to copy the sqlite to your xcode project then you need to check in your persistentStoreCoordinator if the sqlite file exists in your Documents area. If not, you need to copy it. Jump to "Doctor, you’re needed in Xcode" section in that tutorial :).
A little embarrassing question, but I can find an answer which works in my case... I need to put some xml file (settings.xml) in order to read some data from it during application runtime.
According to some answers here and not only here, I have putted it here:
~/Library/Application Support/iPhone Simulator/5.0/[AppUUID]/Documents
and I'm trying to use it as follows:
// Loading data from external XML File
NSURL *url = [[NSBundle mainBundle]
URLForResource: #"settings" withExtension:#"xml"];
NSError *err;
if ([url checkResourceIsReachableAndReturnError:&err] == NO){
NSLog(#"FILE NOT FOUND");
}
Result: "FILE NOT FOUND".
I've tried to do put the file under any possible directory in
~/Library/Application Support/iPhone Simulator/5.0/[AppUUID]/ and efect is still the same.
I'm using XCode 4.2
If you are putting the file into the .../Documents folder then you need to use the following code to access it (you are looking for it in the App Bundle, which is a different location altogether):
NSString *docFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filename = [docFolder stringByAppendingPathComponent:#"settings.xml"];
if ([[NSFileManager defaultManager] fileExistsAtPath:filename])
{
// Read file
}
else
{
NSLog(#"settings.xml file not found!");
}
In my game, I'm saving stats of the player in a plist that I store in the Documents directory. I have an empty dictionary of each stats that should be saved named "Default_Stats.plist" so that if it's the first time the app is loaded, it will copy it in the appropriate directory so it could be loaded and overwritten at will. The problem is, every time my app is loaded, it doesn't recognize the "Stats.plist" and overwrite it with the Default Stats, resetting every stats the player have made... And weird enough, it was perfectly working on the simulator, but not on the device. Here's my code :
In this method I read the stats :
- (void) readStatsFromFile{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *statsPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"Stats.plist"];
//Check if the file has already been created
if (![[NSFileManager defaultManager] fileExistsAtPath:statsPath]){
[self createStatsList];
}else{
stats = [[NSMutableDictionary dictionaryWithContentsOfFile:statsPath]retain];
}
}
Here's my creating method :
- (void) createStatsList{
NSString *statsPath = [[NSBundle mainBundle] bundlePath];
statsPath = [statsPath stringByAppendingPathComponent:#"Default_Stats.plist"];
stats = [[NSMutableDictionary dictionaryWithContentsOfFile:statsPath] retain];
[self writeStatsToFile];
}
And my writing method :
- (void) writeStatsToFile{
BOOL ok;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *statsPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"Stats.plist"];
ok = [stats writeToFile:statsPath atomically:YES];
if (!ok) {
NSLog(#"Couldn't write to file");
}else
NSLog(#"Stats written succesfully!");
}
Please help, I really don't understand what's wrong! I hope I've made myself clear enough!
Use filepath instead of absolute path.
Maybe duplicates exist in your mac, which makes exists=true on simulator, but not on device.
The easiest way to check would be to NSLog the paths encountered. Refer to these tools - they allow console logs to be captured for release builds running on your device.
Most likely that your documents directory just doesn't exist - on the simulator you share a documents directory with everyone on the Mac; on the device everyone has his own directory. Use the file manager method
createDirectoryAtURL:url withIntermediateDirectories:YES
to make sure that the directory is there before you try writing there. (I tend to use the URL methods instead of the file path methods).
PS. I'd recommend having one method that returns the path or url that you want. It's a good habit not to duplicate your code again and again.
I would do pretty much that, like everything in one session:
gets the URL for the file in the Document folder;
if the file is not there yet, copies the file from bundle to the Documents folder;
that should be the method for that, I have defined some macros for avoiding mistyping the file's name in the code:
- (NSURL *)statsFileURL {
#define NSStringFromFileNameWithExtension(filename, extension) [(filename) stringByAppendingPathExtension:(extension)]
#define kExtension #"plist"
#define kDefaultStatsFileName #"Default_Stats"
#define kCustomStatsFileName #"Stats"
NSURL *_returnURL = nil;
NSFileManager *_fileManager = [NSFileManager defaultManager];
NSURL *_documentDirectory = [[_fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *_myFileURLInDocumentFolder = [_documentDirectory URLByAppendingPathComponent:NSStringFromFileNameWithExtension(kDefaultStatsFileName, kExtension)];
if ([_fileManager fileExistsAtPath:[_myFileURLInDocumentFolder path]]) {
_returnURL = _myFileURLInDocumentFolder;
} else {
NSURL *_myFileURLInBundle = [[NSBundle mainBundle] URLForResource:kDefaultStatsFileName withExtension:kExtension];
if ([_fileManager fileExistsAtPath:[_myFileURLInBundle path]]) {
NSError *_error = nil;
if ([_fileManager copyItemAtURL:_myFileURLInBundle toURL:_myFileURLInDocumentFolder error:&_error]) {
if (_error == nil) {
_returnURL = _myFileURLInDocumentFolder;
} else {
// some error during copying
}
} else {
// some error during copying
}
} else {
// the file does not esixts at all, not even in the bundle
}
}
return _returnURL;
}
the URL always points inside the Documents folder, so you will have read/write access to the file – or will be nil if some error happens.
after you have the URL, you can restore back to file without any issue, and at some other point in runtime you can override the file for your convenience anytime.
NOTE: you may need to extend this code for a more detailed error handling, I put the comment only the places when you need to worry about potential errors.