Im trying to use an sqlite database in xcode 4.2 and ios5 sdk. The if condition in the code below returns false and the database closes.
In the console the following message is displayed.
[71280:5303] Failed at Prepare Statement in NumberOfOrders. Reason out of memory
2011-12-07 03:14:31.893 Database Closed
2011-12-07 03:14:31.897 ### *** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty
array
2011-12-07 03:14:31.898 ### *** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty
array
Kindly help.
//Open Database connection & assign to pointer dbInstance variable
-(void) OpenDatabase { #try {
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSLog(#"path is %#", path);
NSString *documentDir = [path objectAtIndex:0];
NSLog(#"doc directory is %#", documentDir);
NSString *dbPath = [documentDir stringByAppendingPathComponent: #"MAKitTutorials.sqlite"];
NSLog(#"dbPath is %#", dbPath);
if (sqlite3_open_v2([dbPath UTF8String], &dbInstance, SQLITE_OPEN_NOMUTEX, NULL) == SQLITE_OK) {
NSLog(#"Database Open Succeeds");
} else {
sqlite3_close(dbInstance);
}
} #catch (NSException * e) {
NSLog(#"Failed to open the database. Reason %#",[e reason] );
}
}
//Close Database connection
-(void) CloseDatabase { sqlite3_close(dbInstance);
}
I hit the same problem when tried to use another flag: SQLITE_OPEN_SHAREDCACHE.
Solved by explicitly adding two flags used by sqlite3_open() call:
SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE.
So in your case it's going to be:
SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX.
Related
Intro
I have an objective-C file (.m) in which I'm trying to query local users (through CSGetLocalIdentityAuthority and then print each user via printf (intentionally using C).
What do I expect?
get local users with objective c, print them with C pritnf.
Here's my attempt:
main.m
int main(){
// Query Local Identity Authority
CSIdentityAuthorityRef defaultAuthority = CSGetLocalIdentityAuthority();
CSIdentityClass identityClass = kCSIdentityClassUser;
CSIdentityQueryRef query = CSIdentityQueryCreate(NULL, identityClass, defaultAuthority);
CFErrorRef error = NULL;
CSIdentityQueryExecute(query, 0, &error);
// get query results
CFArrayRef users_queried = CSIdentityQueryCopyResults(query);
//iterate over users
// Create a users array
NSMutableArray * users_list = [NSMutableArray array];
// // number of users
int users_queried_count = CFArrayGetCount(users_queried);
// // iterate users and save to users array
for (int i = 0; i < users_queried_count; ++i) {
CSIdentityRef identity = (CSIdentityRef)CFArrayGetValueAtIndex(users_queried, i);
CBIdentity * identityObject = [CBIdentity identityWithCSIdentity:identity];
[users_list addObject:identityObject];
}
for (NSString *user in users_list) {
// NSLog(#"%#", user);
const char *c_user = [user UTF8String];
printf("%s", c_user);
}
// release
CFRelease(users_queried);
CFRelease(query);
return 0;
}
However, this yeilds the following error:
*** Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[CBUserIdentity UTF8String]:
unrecognized selector sent to instance 0x7fac024161f0'
You can see I'm trying to convert the NSString to a UTF8 String and print it, but it fails (however, NSLogging' it works).
CBIdentity is not a NSString - its a CBIdentity!
If you print it using NSLog it gets converted to a string (through description), but if you message it with UTF8String as you do it does not work.
You've commented out NSLog(#"%#", user);
If that actually gives you what you want then change the add part to
[users_list addObject:[identityObject description]];
Otherwise, note that you are looping CBIdentity and not NSString so change the loop e.g. like so or similar.
for ( CBIdentity * user in users_list ) {
const char *c_user = [[user posixName] UTF8String];
printf("%s", c_user);
}
There is a -fullName property for the class
I have a text file that I can scan (NSScanner) and tag. The results are stored in an array. The structure is two strings, one for english and one for greek.
I want to write an output file that maintains the array structure. I presume I can create a plist file for this purpose.
However, I'm stuck and need help to create this file.
I have a test whether the file was created, but I get this result:
outgoingWords.count: 442
2014-08-12 17:54:17.369 MyScanner[97350:2681695] *** Assertion failure in -[ViewController checkArray], /Users/david/Desktop/Word Scanning/MyScanner/MyScanner/ViewController.m:98
2014-08-12 17:54:17.373 MyScanner[97350:2681695] Failed to set (contentViewController) user defined inspected property on (NSWindow): writeToFile failed
The code I'm using so far is as follows:
-(void)checkArray {
//do stuff to verify the array
long i = outgoingWords.count;
NSString *tempString = [NSString stringWithFormat:#"%li", i];
NSLog(#"outgoingWords.count: %#", tempString); //442
NSArray *tempArray2 = [outgoingWords copy];
NSString *path = [[NSBundle mainBundle] pathForResource:#"/Users/David/Desktop/alfa2" ofType:#"plist"];
BOOL success = [tempArray2 writeToFile:path atomically:YES];
NSAssert(success, #"writeToFile failed");
}
Could someone either identify what I'm missing, or point me to an existing answer I can use (I've looked)..
Many thanks..
edit. I've also tried the approach in this SO question. But get the same result.
NSString *path = [[NSBundle mainBundle] pathForResource:#"/Users/David/Desktop/alfa2" ofType:#"plist"];
NSString *error;
NSData *data = [NSPropertyListSerialization dataFromPropertyList:tempArray2 format:NSPropertyListBinaryFormat_v1_0 errorDescription:&error];
BOOL success = [NSKeyedArchiver archiveRootObject:data toFile:path];
NSAssert(success, #"archiveRootObject failed");
Problem is that you are writing to the bundle, which is not allowed. You should write to a path in your document or other directory, for example:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
filePath = [documentsDirectory stringByAppendingPathComponent:#"FileName.xxx"];
[data writeToFile:filePath atomically:NO];
When we run the checkAndCreateDatabase method it says that the database exists but the sqlite3_error message returns an "out of memory" error before exiting the method. How can this be fixed?
-(void) checkAndCreateDatabase{
NSLog(#"Run Method: checkAndCreateDatabase");
// Check if the SQL database has already been saved to the users phone, if not then copy it over
BOOL success;
// Create a FileManager object, we will use this to check the status
// of the database and to copy it over if required
// Check if the database has already been created in the users filesystem
success = [[NSFileManager defaultManager] fileExistsAtPath:databasePath];
// If the database already exists then return without doing anything
if(success){
NSLog(#"Database Exists");
} else {
// If not then proceed to copy the database from the application to the users filesystem
NSLog(#"Database Does Not Exist");
// Get the path to the database in the application package
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseName];
// Copy the database from the package to the users filesystem
[[NSFileManager defaultManager] copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
NSLog(#"Database Copied To filesystem And Exists");
}
NSLog(#"Method checkAndCreateDatabase Finished Message: %s",sqlite3_errmsg(database));
return;
}
- (void)viewDidLoad {
NSLog(#"Run Method: viewDidLoad");
// Setup some globals
databaseName = #"w2w.db";
NSLog(#"Database Name: %#",databaseName);
// Get the path to the documents directory and append the databaseName
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
NSLog(#"Database Path: %#",databasePath);
// Execute the "checkAndCreateDatabase" function
[self checkAndCreateDatabase];
[super viewDidLoad];
}
Where is the database variable being initialized? I've run into this same error message when I have accidentally passed in an undefined or incorrect parameter to sqlite3_errmsg(). I think this is the default error message.
What can I use to copy a remote database to a local path?
success = [FileManager copyItemAtURL:dbPath toPath:databasePath error: &error];
Obviously the above won't work. Looking at the manual it seems you can copy path to path or url to url. But I've seen example of myapp://path/to/documents/database.sql. Now I'm sorta confused. Can someone possibly point me in the right direction?
This is what I'm doing right now... but it's not overwriting the database or I'm doing things in the wrong order. The file is only a couple of Kbs also.
- (void)createDB {
BOOL success;
NSError *error;
NSData *fetchedData = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://www.dot.com/book/book.sqlite"]];
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [documentsPath stringByAppendingPathComponent:#"book.sqlite"];
[fetchedData writeToFile:filePath atomically:YES];
NSLog(#"%#", filePath);
NSFileManager *FileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *databasePath = [documentsDirectory stringByAppendingString:#"book.sqlite"];
success = [FileManager fileExistsAtPath:databasePath];
if(success)return;
NSString *dbPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"book.sqlite"];
success = [FileManager copyItemAtPath:dbPath toPath:databasePath error: &error];
if(!success)
NSAssert1(0, #"Failed to copy database. Error: %#", [error localizedDescription]);
}
I see by the line,
NSString *dbPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"book.sqlite"];
you are trying to get the db from Resources directory, but your code above actually downloads the sqlite directly to Documents directory NOT resources (which is not writable)
It fails because there is no book.sqlite at resources directory. You don't need to copy the file since it is already present in Documents directory
NSData *data;
data = [self fillInSomeStrangeBytes];
My question is now how I can write this data on the easiest way to an file.
(I've already an NSURL file://localhost/Users/Coding/Library/Application%20Support/App/file.strangebytes)
NSData has a method called writeToURL:atomically: that does exactly what you want to do. Look in the documentation for NSData to see how to use it.
Notice that writing NSData into a file is an IO operation that may block the main thread. Especially if the data object is large.
Therefore it is advised to perform this on a background thread, the easiest way would be to use GCD as follows:
// Use GCD's background queue
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// Generate the file path
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:#"yourfilename.dat"];
// Save it into file system
[data writeToFile:dataPath atomically:YES];
});
writeToURL:atomically: or writeToFile:atomically: if you have a filename instead of a URL.
You also have writeToFile:options:error: or writeToURL:options:error: which can report error codes in case the saving of the NSData failed for any reason. For example:
NSError *error;
NSURL *folder = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:true error:&error];
if (!folder) {
NSLog(#"%s: %#", __FUNCTION__, error); // handle error however you would like
return;
}
NSURL *fileURL = [folder URLByAppendingPathComponent:filename];
BOOL success = [data writeToURL:fileURL options:NSDataWritingAtomic error:&error];
if (!success) {
NSLog(#"%s: %#", __FUNCTION__, error); // handle error however you would like
return;
}
The easiest way, if you want to do this a few times manually instead of coding, is to only use your mouse:
Put a breakpoint one line after your NSdata are fulfilled.
Hold your mouse over your NSData variable, click on the Eye button, and then export.
After that chose the storage details (name/extension/location of the file).
Click on "Save" and you are done!