Issue with passing variables to FMDB - iphone

In my app I'm using FMDB to query my sqlite database. I'm passing variables from one view to another and at the end all the selections the variables are filled with the values selected. On the results page I can show these values in label's fine. But the moment I pass them to FMDB to query the database I don't get any values returned. It crashes and says that the array is 0 which I know it's not.
Code sample below.
- (NSMutableArray *) getChoices
{
NSMutableArray *choices = [[NSMutableArray alloc] init];
FMDatabase *db = [FMDatabase databaseWithPath:[Utility getDatabasePath]];
[db open];
NSString *capsChoiceOne = #"CHOICE 1";
NSString *capsChoiceTwo = #"CHOICE 2";
NSString *capsChoiceThree = #"CHOICE 3";
NSString *capsChoiceFour = #"CHOICE 4";
FMResultSet *results = [db executeQueryWithFormat:#"SELECT * FROM allitems WHERE choice1=%# AND choice2=%# AND choice3=%# AND choice4=%#"
,capsChoiceOne,capsChoiceTwo,capsChoiceThree,capsChoiceFour];
while([results next])
{
Choices *choice = [[Choices alloc] init];
choice.result = [results stringForColumn:#"result"];
[choices addObject:choice];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Your Result"
message:choice.result
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
[db close];
return choices;
Now the above code will bring back the result in the Alert View. But the moment I change one of the values to a variable it crashes and say's there was 0 results in my array. I've put the code below for how I'm inserting the variable.
NSString *capsChoiceOne = self.choiceOneSelected.uppercaseString;
the self.choiceOneSelected.uppercaseString contains the same as the hard coded version but doesn't work.
Any help would be grateful.
Thank you

Check whether self.choiceOneSelected.uppercaseString; is not nil.
Also your query have some issues, you need to wrap string values inside '
So use:
FMResultSet *results = [db executeQueryWithFormat:#"SELECT * FROM allitems WHERE choice1='%#' AND choice2='%#' AND choice3='%#' AND choice4='%#'"
,capsChoiceOne,capsChoiceTwo,capsChoiceThree,capsChoiceFour];

Related

sqlite3 and fmdb nested FMResultSet is possible?

I'm trying to iterator through a master detail sort of tables and I'd like to populate the master/detail structures as I go. Apparently when I nest result sets I get a BAD Access exception:
FMDatabase *db = self.database;
[db open];
db.traceExecution = YES;
db.logsErrors = YES;
FMResultSet *rs = [db executeQuery:#"select group_id, label from main.preference_group order by group_id"];
while ([rs next])
{
PreferenceGroup *pg = [[PreferenceGroup alloc] init];
pg.group_id = [rs intForColumn:#"group_id"];
pg.label = [rs stringForColumn:#"label"];
pg.translatedLabel = NSLocalizedString(pg.label, nil);
NSMutableArray * prefs = [[NSMutableArray alloc] init];
[prefGroups addObject:prefs];
FMResultSet *rs2 = [db executeQuery:#"select pref_id, label, value from main.preference where group_id = ? order by pref_id", pg.group_id, nil];
while ([rs2 next])
{
Preference * pref = [[Preference alloc] init];
pref.group_id = pg.group_id;
pref.pref_id = [rs2 intForColumn:#"pref_id"];
pref.label = [rs2 stringForColumn:#"label"];
pref.value = [rs2 stringForColumn:#"value"];
pref.translatedLabel = NSLocalizedString(pref.value, nil);
[prefs addObject:pref];
}
[rs2 close];
}
[rs close];
[db close];
In the rs2 (second result set) I get the EXEC_BAD_ACCESS within FMDatabase class.
Is this a restriction of sqlite3/fmdb or am I doing something wrong here?
I just found what I did wrong. I was passing a int as part of the second query. I had to convert it to NSNumber:
FMResultSet *rs2 = [db executeQuery:#"select pref_id, label, value from main.preference where group_id = ? order by pref_id", [NSNumber numberWithInt:pg.group_id], nil];
So that means, YES, sqlite3/fmdb does support nested queries! :-)
I'm using FMDB and SQLITE3 as well, and I find nested queries work:
(I'm not claiming the code hint below is good, never mind the format, please)
Both Master and Detail tables have a column called 'id'
FMResultSet *rso = [database executeQuery:#"select * from master order by id"];
while ([rso next])
{
NSInteger masterId = [rso intForColumn:#"id"];
NSString *q3 = [[NSString alloc] initWithFormat:
#"select * from detail where masterid = %d order by id", masterId, nil ];
FMResultSet *rsa = [database executeQuery:q3 ];
while ([rsa next])
{
NSInteger detailId = [rsa intForColumn:#"id"];
//
// here do something with masterId and detailId
}
}
This was a pleasant surprise, actually. I was halfways expecting to have to first query all master records, and then loop through them in App memory to pick up the details from SQlite3.
But the above construct works fine.

Reading from SQLite - FMDB - Beginner

I am trying to read from a database file (performing a simple select all functions).
I am using FMDB.
Here's how i created the DB;
Pro:~ dd$ sqlite3 db.db
SQLite version 3.7.7 2011-06-25 16:35:41
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> create table cus(id integer primary key, firstname varchar(30));
sqlite> inser into cus(firstname)values('f');
Error: near "inser": syntax error
sqlite> insert into cus(firstname)values('f');
sqlite> select * from cus;
1|f
sqlite>
I copied the file (db.db) to my resource folder in xCode. changed the name of the db file to db.db in the app delegate. Code for my program is exactly yhe same as this tutorial.
Here's the code ;
-(NSMutableArray *) getCustomers
{
NSMutableArray *customers = [[NSMutableArray alloc] init];
NSString * path = [(AppDelegate*)[[UIApplication sharedApplication]delegate]databasePath];
NSLog(#"DB path %# ",path);
FMDatabase *db = [FMDatabase databaseWithPath:path];
[db open];
FMResultSet *results = [db executeQuery:#"SELECT * FROM cus"];
NSLog(#"result %# ",results);
while([results next])
{
NSLog(#"result %# ",results);
Customer *customer = [[Customer alloc] init];
customer.customerId = [results intForColumn:#"id"];
customer.firstName = [results stringForColumn:#"firstname"];
[customers addObject:customer];
}
[db close];
return customers;
}
My problem;
Eventhough there is 1 record in the DB, the result of the Select statement is NULL. Why is this and how can i correct it ?
Assuming that the database was created and imported into the project successfully, try the following:
-(NSMutableArray *) getCustomers
{
NSMutableArray *customers = [[NSMutableArray alloc] init];
NSString * path = [(AppDelegate*)[[UIApplication sharedApplication]delegate]databasePath];
NSLog(#"DB path %# ",path);
FMDatabase *db = [FMDatabase databaseWithPath:path];
if(![db open])
{
NSLog(#"Could not open DB, try again");
return nil;
}
FMResultSet *results = nil;
results = [db executeQuery:#"SELECT * FROM cus"];
NSLog(#"result %# ",results);
while([results next])
{
Customer *customer = [[Customer alloc] init];
customer.customerId = [results intForColumn:#"id"];
customer.firstName = [results stringForColumn:#"firstname"];
NSLog(#"Customer object %#", customer);
[customers addObject:customer];
[customer release];
}
[db close];
return customers;
}
I have had this same problem but was managed to resolve this by setting up the path correctly. So, there could be something wrong in the path specification. Do make sure that your database path is perfect. And as everyone suggests, I recommend you to use error statements to narrow down the issue. Wishes!!

Making selectedSegmentIndex select nothing after selection

I have a UISegmentControl that select nothing through IB, after the user selects the segment it becomes selected. How do i do it so that it doesnot gets selected?
//Show question method
-(void)question:(NSInteger)i
{
// Path to the plist
NSString *path = [[NSBundle mainBundle] pathForResource:#"Question" ofType:#"plist"];
// Set the plist to an array
NSArray *array = [NSArray arrayWithContentsOfFile:path];
//Check the number of entries in the array
NSInteger numCount = [array count];
if(i <numCount)
{ NSDictionary *dict = [array objectAtIndex:i];//load array index 0 dictionary data
self.title = [NSString stringWithFormat:#"Question %d", i+1];//set the nav bar title
quest.text = [dict valueForKey:#"Question"];//Set the Question to storage
ans.text = [dict valueForKey:#"Answer"];//Set the Answer to storage
NSInteger option = [[dict valueForKey:#"NumberOfOption"] integerValue ];//Check options to determine the question type
//check if the option is is a QRCode or Multiple Choices Question
if (option ==0)
{
QRbutton.alpha = 1; //show the QR Code Button If there is no options
OptionsAnswer.alpha = 0;//Hide Option if there is no options
}
else
{
QRbutton.alpha = 0.0;//Hide QR Code Button if there is options
OptionsAnswer.alpha = 1;//Show Option if there is options
[OptionsAnswer setTitle:[dict valueForKey:#"Option1"] forSegmentAtIndex:0];//Set Option Answer Value
[OptionsAnswer setTitle:[dict valueForKey:#"Option2"] forSegmentAtIndex:1];//Set Option Answer Value
[OptionsAnswer setTitle:[dict valueForKey:#"Option3"] forSegmentAtIndex:2];//Set Option Answer Value
[OptionsAnswer setTitle:[dict valueForKey:#"Option4"] forSegmentAtIndex:3];//Set Option Answer Value
[OptionsAnswer addTarget:self action:#selector(OptionAnswerCheck) forControlEvents:UIControlEventValueChanged];//Call action when options is being selected
}
}
else {
//if question is all answered, it will prompt an alert for end game video.
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Well Done"
message:#"You Have Answered All The Questions, Oh Wait A Minute I Heard A Cracking Sound...." delegate:self
cancelButtonTitle:#"OK" otherButtonTitles:nil] autorelease]; [alert show];;
[alert performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:NO];
}
}
//Check if the selected Option is correct
-(IBAction)OptionAnswerCheck
{
//define a persistant location to save which question has been answered
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];//question storages
//pass the value from the selected option to a string
//NSString * selectedTitle = ([OptionsAnswer selectedSegmentIndex] >= 0) ? [OptionsAnswer titleForSegmentAtIndex:[OptionsAnswer selectedSegmentIndex]] :
NSString * selectedTitle = [OptionsAnswer titleForSegmentAtIndex:[OptionsAnswer selectedSegmentIndex]];
NSLog(#"Selected Title = %#",selectedTitle);//test
//check if the selected value is equal to the answers
if ([selectedTitle compare:self.ans.text] ==NSOrderedSame)
{
//Popup to say answer Correct
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Correct!"
message:#"Nice Work, Lets Move On To The Next Question" delegate:nil
cancelButtonTitle:#"OK" otherButtonTitles:nil] autorelease]; [alert show];;
[alert performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:NO];
//increase the question number
[self question:++currentQuestion];
//save increased question
[userDefaults setInteger:currentQuestion forKey:#"currentQuestion"];
}
else
{
//Popup to say answer Wrong
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Incorrect"
message:#"Close! But That's Not Right, Try Another Answer" delegate:nil
cancelButtonTitle:#"Try Again." otherButtonTitles:nil] autorelease]; [alert show];;
[alert performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:NO];
}
//OptionsAnswer.selectedSegmentIndex = UISegmentedControlNoSegment;
}
Just search for setMomentary: in your developer documentation inside Xcode.
I'm not entirely sure what you're asking here, but I think that you want to set the momentary property toYES.
The property is in the inspector of IB as well. (Can't post a screenshot, I'm on my iPhone).

How to declare an NSString with multiple possible values

I want to declare an NSString object to use within an alert, but its actual content depends on various factors, determined by some variable. I'm wondering how best to approach this. In most cases I've done something like this:
- (void)info {
NSString *targetString = [[NSString alloc] init];
switch (self.target) {
case 1:
targetString = #"ONE";
break;
case 2:
targetString = #"TWO";
break;
case 3:
targetString = #"THREE";
break;
default:
targetString = #"";
break;
}
NSString *message = [[NSString alloc] initWithFormat:#"Text: %#", targetString];
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Info"
message:message
delegate:self
cancelButtonTitle:#"Ok!"
otherButtonTitles:nil];
alert.tag = kInfoAlert;
[alert show];
[alert release];
[targetString release];
[message release];
}
However when I run this through the build analyser, I get messages telling me the string is leaking memory:
First of all it says:
Value stored to 'targetString' during
its initialization is never read
Then:
Potential leak of an object allocated
on line 137 and stored into
'targetString'
These 2 comments are at line 136 and 137, where line 136 is
NSString *targetString = [[NSString alloc] init];
An alternative might be to declare the string as
NSString *targetString;
and set it in each case as
targetString = [NSString stringWithFormat:#"ONE"];
etc
Or even allocing the String in each case in order to release it at the end...
Well, what would be the best approach here?
Thanks,
Michael :)
The reason for your memory leak is because you are needlessly allocating a string with this line
NSString *targetString = [[NSString alloc] init];
and then setting it to a literal object. Define targetString as nil because when you set it to another value like targetString = #"ONE" you are no longer referencing the empty string you allocated and that causes a memory leak. As for your approach of the switch case for determining the value that is fine.
I believe this would be enough:
NSString *targetString = nil;
And you don't need to release targetString then.
How about this instead of the switch:
- (NSString*) stringForIndex: (NSUInteger) index
{
NSParameterAssert(index < 4);
id strings[] = {#"none", #"one", #"two", #"three"};
return strings[index];
}

Table crashes when sorting the data multiple times

I have a tableview with a navigationBar with a segmentedControl on the top of the view. I have set up the segmentedControl with buttons that sort the table by either "FirstName" or "LastName". It works perfectly the first 2-4 of times you press the sorting buttons, but then the app crashes.
The debugger and console seem to be of no help finding the source of the bug. Does anyone see any glaring mistakes in my code?
Here is my code below, let me know if you have any questions. Thanks!
- (IBAction)sortingSegmentAction:(id)sender{
NSString *keyToSortBy = [NSString alloc];
if([sender selectedSegmentIndex] == 0)
{
self.sortingSegmentActionPressed = 0;
keyToSortBy = #"FirstName";
}
else if([sender selectedSegmentIndex] == 1)
{
self.sortingSegmentActionPressed = 1;
keyToSortBy = #"LastName";
}
//Create the sort descriptors
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:keyToSortBy ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
//Sort allSubItams by the values set in the sort descriptors
NSArray *sortedArray;
self.sortedArray = [allSubItems sortedArrayUsingDescriptors:sortDescriptors];
//Recreate the data structure by putting the newly sorted items into a dictionary sorted by inital letters.
NSDictionary *eachItemList; //A DICTIONARY FOR PUTTING ALL THE DATA FOR EACH ITEM IN IT'S OWN SECTION
NSMutableDictionary *tempSectionedDictionaryByFirstLetter = [[NSMutableDictionary alloc] init];
for (eachItemList in sortedArray) //eachElementList is a dictionary with a section for each item
{
NSDictionary *aDictionary = [[NSDictionary alloc] initWithDictionary:eachItemList];
NSString *firstLetterString;
firstLetterString = [[aDictionary valueForKey:keyToSortBy]substringToIndex:1];
NSMutableArray *existingArray;
if (existingArray = [tempSectionedDictionaryByFirstLetter valueForKey:firstLetterString])
{
[existingArray addObject:eachItemList];
} else {
NSMutableArray *tempArray = [NSMutableArray array];
[tempSectionedDictionaryByFirstLetter setObject:tempArray forKey:firstLetterString];
[tempArray addObject:eachItemList];
}
[aDictionary release];
[eachItemList release];
}
//Set the data source for the table (sectionedDictionaryByFirstLetter) to tempSectionedDictionaryByFirstLetter.
self.sectionedDictionaryByFirstLetter = tempSectionedDictionaryByFirstLetter;
NSMutableArray *keyArray = [[NSMutableArray alloc] init];
[keyArray addObjectsFromArray:[[self.sectionedDictionaryByFirstLetter allKeys] sortedArrayUsingSelector:#selector(compare:)]];
self.keys = keyArray;
[self.tableView reloadData];
[keyArray release];
[tempSectionedDictionaryByFirstLetter release];
}
Don't release eachItemList at the end of your loop. You did not explicitly allocate it in this context so you shouldn't release it.
The for (object in array) loop gives you a reference to the object in the array, not a copy. By sending a release message to this reference, you are decrementing the retain count of this object while it is still in the array. After a few times (depending on how many times the object has been retained, NSArray for example retains objects when they are added to the array) it's retain count will reach 0, and it will then become deallocated and you'll get crashes regarding unrecognised selectors or EXC_BAD_ACCESS and possibly other kinds of errors.