My application's main window shows a SplitViewController. On the left is a TableView that displays the names. On the right is a DetailsView consisting of 2 textviews that displays the name and age.
This is the SQLite database and 'USERS' table i have created for my application.
-(NSString *)dataFilePath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
return [documentsDir stringByAppendingPathComponent:#"Mydata.sqlite"];
}
- (void)viewDidLoad
{
[super viewDidLoad];
if(sqlite3_open([[self dataFilePath] UTF8String], &database) != SQLITE_OK)
{
sqlite3_close(database);
NSAssert(0, #"Failed to open database.");
}
char *err;
NSString *sql = #"CREATE TABLE IF NOT EXISTS USERS" "(ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT, AGE INTEGER);";
if(sqlite3_exec(database, [sql UTF8String], NULL, NULL, &err) != SQLITE_OK)
{
sqlite3_close(database);
NSAssert(0, #"Failed to create table.");
}
}
I want to populate the TableView with the names. When a name is selected in the table, the textviews on the right side of the SplitViewController will display the selected name as well as the age.
May i know how to do this?
There are a number of questions in there. From the sounds of it you want to know how to load the values, how to display them in a tableview, how to send a message from the tableView to the mainview and then how to display the content in your main view.
First off create a model object for the 'User' object. Then in the user class write a static method that you can call that returns all the users in an array.
Once you have the array, use it to populate the tableView using the standard 'cellForRowAtIndexPath' method. Once the user taps on a cell, pass the selected user object over to the main view via a delegate method and then finally display its various details (name, age, etc) in your relevant labels that you will have in there.
If any of that is unclear let me know and I can post a code snippet to help you out.
EDIT: So.. code snippet for creating an object and loading it into the tableViewController (only writing the .ms):
For the User class
#implementation User
#synthesize pk, name, age;
#pragma mark - Public Instance Methods
+ (NSArray *)getAllUsers
{
sqlite3 *db = **However you get your db conn**
sqlite3_stmt *statement = nil;
NSMutableArray *userArray = [NSMutableArray array];
NSString *fullQuery = #"SELECT * FROM User";
const char *sql = [fullQuery UTF8String];
if(sqlite3_prepare_v2(db, sql, -1, &statement, NULL)!=SQLITE_OK)
NSAssert1(0, #"Error preparing statement '%s'", sqlite3_errmsg(db));
else
{
while(sqlite3_step(statement) == SQLITE_ROW)
{
User *currentUser = [[User alloc] init];
[User setPk:[NSString stringWithUTF8String:(const char*)sqlite3_column_text(statement, 0)]];
[User setName:[NSString stringWithUTF8String:(const char*)sqlite3_column_text(statement, 1)]];
[User setAge:[NSString stringWithUTF8String:(const char*)sqlite3_column_text(statement, 2)]];
[userArray currentUser];
[currentUser release];
}
}
sqlite3_finalize(statement);
sqlite3_close(db);
return [NSArray arrayWithArray:userArray];
}
Now for your table View Controller:
- (void)init
{
self = [self init];
if (self)
{
[self setUserArray:[User getAllUsers]];
}
}
- (void)dealloc
{
[super dealloc];
[userArray release];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [userArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
[cell setSelectionStyle:UITableViewCellSelectionStyleBlue];
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
}
User *currentUser = [userArray objectAtIndex:indexPath.row];
[[cell textLabel] setText:user.name];
return cell;
}
Hopefully that gives you an idea of how to proceed. If you are confused about how to proceed I recommend that you read up on the 'Model View Controller' Programming methodology and how table views work. Its a bit of work to start off with but you'll get there.
Hope that helps :)
Related
When I searching and then select row that opens only the first letter (for example A.Others letters don't open. NSLog and breakpoint not helping. I don't understand what is the problem.
#synthesize propertyList, letters, filteredNames, searchController , arrayPlace;
- (void)viewDidLoad {
[super viewDidLoad];
............
filteredNames = [[NSMutableArray alloc]init];
searchController = [[UISearchController alloc]init];
self.searchController.searchResultsUpdater = self;
NSString *path = [[NSBundle mainBundle] pathForResource:#"names" ofType:#"plist"];
self.propertyList = [NSDictionary dictionaryWithContentsOfFile:path];
self.letters = [[self.propertyList allKeys] sortedArrayUsingSelector:#selector(compare:)];
}
#pragma mark - Table view data source
.......
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"cell bg1.png"]];
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
if (tableView.tag == 1){
NSString *letter = self.letters[indexPath.section];;
NSArray *keyValues = [[self.propertyList[letter] allKeys] sortedArrayUsingSelector:#selector(compare:)];
cell.textLabel.text = keyValues[indexPath.row];
} else{
cell.textLabel.text = filteredNames[indexPath.row];
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSString *keyTitle = cell.textLabel.text;
NSDictionary *peopleUnderLetter = [self.propertyList objectForKey:self.letters[indexPath.section]];
__block NSDictionary *selectedPerson = nil;
[peopleUnderLetter enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
if ([key isEqualToString:keyTitle]) {
selectedPerson = obj;
*stop = YES;
}
}];
if (selectedPerson) {
DetailViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"DetailViewController"];
// Push the view controller.
[self.navigationController pushViewController:vc animated:YES];
[vc setDictionaryGeter:selectedPerson];
}
}
And :
#pragma mark Search Display Delegate Methods
-(void)searchDisplayController:(UISearchController *)controller didLoadSearchResultsTableView:(UITableView *)tableView {
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
}
-(BOOL)searchDisplayController:(UISearchController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[filteredNames removeAllObjects];
if (searchString.length > 0) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains [search] %#", self.searchBar.text];
for (NSString *letter in letters) {
NSArray *matches = [[self.propertyList[letter] allKeys]filteredArrayUsingPredicate:predicate];
[filteredNames addObjectsFromArray:matches];
}
}
return YES;
}
Search bar fails and he does select row after searching
If you want more information just say it to me by answers and I will edit my question and then you will edit your answer
Please explain again clearly. You search using any alphabet, it shows the result which has only "A". Is this what you're trying to say ? If so, then remove the above code and try the below approach :-
Drag a search bar into the view controller and set its delegate to self (You'll find its property in the storyboard's delegate property
to the view controller).
Add UISearchBarDelegate in the .h file that will take care of automatically calling the appropriate methods of the search bar of
which the delegate is set to self.
Use the below method to detect the search. You can filter the NSArray here and reload the table.
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
}
I'd recommend you to go through some basic tutorials about iOS development before getting deeper. All the best and I hope it helps you...
Screenshot
I have made a table where depending on which cell you click on you will be sent into a new scene (detailviewcontroller). For example if you click on the cell with the text Thailand you will be sent to ThailandDetailViewController (scene). Everything works until you use the searchbar (look under - (void)tableView).
-When some countries get outfiltered (because of the searchfunction) the reaming countries will go higher in the table and acquire a lower count. Which leads to that they will lead to the wrong detailviewcontroller (scene).
A friend of mine said to me that I should use objectAtIndex within my array, didnt really catch what he meant with that.. And make a switch on the cell.textLabel.text (didnt really follow him)
Here is my .m file:
- (void)viewDidLoad
{
[super viewDidLoad];
self.mySearchBar.delegate = self;
self.myTableView.delegate = self;
self.myTableView.dataSource = self;
totalStrings = [[NSMutableArray alloc] initWithObjects:#"America",#"Austria",#"Canada",#"France",#"Germany",#"Greece",#"Malaysia",#"Mexico",#"Netherlands",#"Poland",#"Russia",#"Singapore",#"Thailand",#"Ukraine", nil];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.row) {
case 0: [self performSegueWithIdentifier:#"Segue0" sender:self];
break;
case 1: [self performSegueWithIdentifier:#"Segue1" sender:self];
break;
//and so on
default: break;
}
}
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if(searchText.length == 0){
isFiltered = NO;
}
else
{
isFiltered = YES;
filteredStrings = [[NSMutableArray alloc]init];
for (NSString *str in totalStrings){
NSRange stringRange = [str rangeOfString:searchText options:NSCaseInsensitiveSearch];
if(stringRange.location !=NSNotFound) {
[filteredStrings addObject:str];
}
}
}
[self.myTableView reloadData];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *Cellidentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:Cellidentifier];
if (!cell) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:Cellidentifier];
}
if (!isFiltered) {
cell.textLabel.text = [totalStrings objectAtIndex:indexPath.row];
}
else //if it's filtered
{
cell.textLabel.text = [filteredStrings objectAtIndex:indexPath.row];
}
return cell;
}
Big thank you in beforehand!!
Well, you can have a custom class to store the area and the segue index like this:
#interface SegueVO : NSObject
#property (nonatomic, strong) NSString *area;
#property int segueIndex;
-(id)initWithArea:(NSString *)area andSegueIndex:(int)index;
#end
#implementation SegueVO
-(id)initWithArea:(NSString *)area andSegueIndex:(int)index
{
self = [super init];
if (self)
{
self.area = area;
self.segueIndex = index;
}
return self;
}
#end
You will then store your ares in the totalStrings array like this:
[[NSMutableArray alloc] initWithObjects:[[SegueVO alloc] initWithArea:#"America" andIndex:0],....
Of course you can create a factory method to cut down on initialisation code.
Now you can work out what segue to activate like this:
NSArray *arrayToUse = totalStrings;
if (isFiltered)
arrayToUse = filteredStrings;
[self performSegueWithIdentifier:[#"Segue"
stringByAppendingString:[NSString stringWithFormat:#"%i",
[arrayToUse[indexPath.row].segueIndex]] sender:self];
Hope this helps.
You could easily solve this problem by storing a custom object in your table's data model instead of an NSString. That object would contain the label to display plus the name of the segue to activate once selected.
It's another question why you'd want a totally different view controller for different data. I suppose these are different kinds of data that need different ways to deal with them.
I'm trying to use a global NSMutableDictionary from a dispatch queue. However, the items keep coming back NULL.
What I'm trying to do is access an external json file with a dispatch_queue, then populate a UITableView with this info.
Here's what I have
vc.h:
#interface viewcontroller {
NSMutableDictionary *jsonArray;
}
vc.m:
#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) //1
#define jsonTest [NSURL URLWithString:#"http://www.sometest.com/test.php"]
-(void)viewDidLoad {
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL:
jsonTest];
[self performSelectorOnMainThread:#selector(fetchedData:)
withObject:data waitUntilDone:YES];
// if I run the log here, I can access jsonArry and the log prints correctly
NSLog(#"City: %#", [jsonArray objectForKey:#"city"];
});
}
-(NSMutableDictionary *)fetchedData:(NSData *)responseData {
NSError *error;
jsonArray = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
return jsonArray;
}
/********************* Table formatting area **********************/
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.ipTable) {
if ([ipArray count] == 0){
return 1;
} else { // meta table
return [ipArray count];
}
} else { // IP Meta Data
return [jsonArray count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == self.myTable) {
NSString *CellIdentifier = NULL;
if ([ipArray count] == 0) {
CellIdentifier = #"No Cells";
} else {
CellIdentifier = #"IP Cell";
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if ([ipArray count] == 0)
{
[cell.textLabel setText:NSLocalizedString(#"None Found", nil)];
return cell;
} else {
IPAddr *theip = [ipArray objectAtIndex: [indexPath row]];
NSString *theipname = [theip ipName];
if ([theipname isEqualToString:#""]) {
[cell.textLabel setText: [theip ipNum]];
[cell.detailTextLabel setText:NSLocalizedString(#"noName", nil)];
} else {
[cell.textLabel setText: [theip ipName]];
[cell.detailTextLabel setText: [theip ipNum]];
}
return cell;
}
} else { // meta table
static NSString *CellIdentifier = #"metaCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// jsonArray content would go here to fill the cells.
/******************** something here to fill the cells using jsonArray ********************/
return cell;
}
} // END UITAbleViewCell
If I access the jsonArray inside the queue, it returns fine and prints the log for the city.
However, if I try to use it outside the queue, it returns NULL.
I'm trying to figure out what is happening, any ideas?
I need to use jsonArray in different methods in the same view, so I need it to be global.
I am fairly sure that the problem is that the data source methods (numberOfRowsInSection,
cellForRowAtIndexPath) are called before the background thread has finished and
filled the jsonArray. Therefore you have to reload the table view when the background
thread has finished:
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_async(kBgQueue, ^{
NSData *data = [NSData dataWithContentsOfURL:jsonTest];
NSError *error;
NSArray *tmpArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
dispatch_sync(dispatch_get_main_queue(), ^{
// Assign new data to data source and reload the table view:
jsonArray = tmpArray;
[self.metaTableView reloadData];
});
});
}
So the table view would be empty initially, and reloaded later when the data has
arrived.
Try to call the other method(which is using your jsonarray) through nsnotification...I am not sure there might some other ideas/ways of doing this.But i am presenting what i have in my mind.
Put this code inside your fetchedData method,
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(someMethod:) name:#"JSonDownloaded" object: jsonArray];
[[NSNotificationCenter defaultCenter] postNotificationName:#"JSonDownloaded" object: jsonArray];
-(void)someMethod:(NSNotification *)nspk
{
NSLog(#"%#",nspk.object);
//Only after this you can able to access the jsonArray.
}
Don't forget to unregister the observer.
jsonArray is just an instance variable, but not a property. Thus, assigning an object to it does not retain it, and the object may be released as soon as the program returns to the run loop.
I suggest replacing the iVar by #property (strong) NSMutableDictionary *jsonArray; and #synthesize jsonArray;, and assigning the object to it by self.jsonArray = ...
EDIT (see comment of Martin R below):
Thus, if you are not using ARC, assigning an object to it does not retain it, and the object may be released as soon as the program returns to the run loop.
In this case, I suggest replacing the iVar by #property (retain) NSMutableDictionary *jsonArray; and #synthesize jsonArray;, and assigning the object to it by self.jsonArray = ...
This seems to be a very simple thing, but I don't know how to do it.
At moment I have rootView with the continents populated:
The code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
cell.accessoryType=UITableViewCellAccessoryDisclosureIndicator;
//Continent is the class. continents is an array to store data
Continent *cont=[self.continents objectAtIndex:[indexPath row]];
cell.textLabel.text=cont.continentName;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Continent *cont=[self.continents objectAtIndex:[indexPath row]];
SelectedContinent* selectedContinent=[[SelectedContinent alloc] initWithNibName: #"SelectedContinent" bundle: nil];
//NSLog(#"%#",cont.continentName);
[self.navigationController pushViewController: selectedContinent animated: YES];
[selectedContinent setTitle:cont.continentName];
[selectedContinent setContinentID:cont.continentID];
[selectedContinent release];
}
I created a new viewcontroller with nib file. Gave him tableView. And now I need to populate the countries from SQLite file. I made a DBAccess class which supposed to do all db manipulations and for every continent was written special method. Then it came to my mind that writing many methods is the stupid idea and the universal method came to world:
-(NSMutableArray*)getTheCountriesEurope:(int)continentID
{
NSMutableArray* euCountriesArray=[[[NSMutableArray alloc]init]autorelease];
NSString* sqlContinents = [NSString stringWithFormat:
#"SELECT countries.countryID,countries.countryName\
FROM countries\
WHERE countries.relativeToContinentID=%i"
,continentID];
/*const char* sqlContinents="SELECT countries.countryID,countries.countryName\
FROM countries\
WHERE countries.relativeToContinentID=?";*/
sqlite3_stmt *statement;
int sqlResult = sqlite3_prepare_v2(database, sqlContinents, -1, &statement, NULL);
if ( sqlResult== SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
Country *countryObj = [[Country alloc] init];
char *countryName = (char *)sqlite3_column_text(statement, 1);
countryObj.countryID = sqlite3_column_int(statement, 0);
countryObj.countryName = (countryName) ? [NSString stringWithUTF8String:countryName] : #"";
[euCountriesArray addObject:countryObj];
NSLog(#"%#",countryObj.countryName);
[countryObj release];
}
sqlite3_finalize(statement);
}
else
{
NSLog(#"Problem with the database:");
NSLog(#"%d",sqlResult);
}
return euCountriesArray;
}
anyway, this method also has issues and I was told to implement int sqlite3_bind_int(sqlite3_stmt*, int, int); method as well.
Well, at moment my problem is that I can't to catch the continentID value from RootViewController class I need it to feed to method this value as the parameter.
Have I done right when assigned countryID this way?
[selectedContinent setTitle:cont.continentName];
[selectedContinent setContinentID:cont.continentID];
in the RootViewController. If yes, then how to obtain that's variable's value?
At moment when I push another view I see only Title (At least it shows right)
SOS
You need to assign the values before you push the view.
[selectedContinent setTitle:cont.continentName];
[selectedContinent setContinentID:cont.continentID];
[self.navigationController pushViewController: selectedContinent animated: YES];
I am stuck in iOS application that works on SQLite. I have created a Coffee table with different types of coffee names and prices along with the coffee ID which acts as the primary key. In SQLite I could run the SQL queries and got the correct answers. But while getting the coffee names in iOS table view, I got nothing except the blank table and section title. While debugging the code I found that while(sqlite3_step(selectstmt) == SQLITE_ROW) is not working as sqlite3_step(selectstmt) returns 101 value (i.e. SQLITE_DONE) instead of SQLITE_ROW (i.e value 100) even if I tried with sqlite3_reset() before executing sqlite3_step().
I could not figure out how to solve it.
Here is my code of coffee.m
#import "Coffee.h"
#import "SQLAppDelegate.h"
static sqlite3 *database = nil;
static sqlite3_stmt *deleteStmt = nil;
static sqlite3_stmt *addStmt = nil;
#implementation Coffee
#synthesize coffeeID, coffeeName, price, isDirty, isDetailViewHydrated;
- (void) dealloc {
[price release];
[coffeeName release];
[super dealloc];
}
+ (void) getInitialDataToDisplay:(NSString *)dbPath{
SQLAppDelegate *appDelegate = (SQLAppDelegate *)[[UIApplication sharedApplication] delegate];
if(sqlite3_open([dbPath UTF8String], &database)==SQLITE_OK){
const char *sql = "select CoffeeID, CoffeeName from Coffee";
sqlite3_stmt *selectstmt = nil;
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL)== SQLITE_OK){
sqlite3_reset(selectstmt);
//NSLog(#"Macro %d",sqlite3_step(selectstmt));
while(sqlite3_step(selectstmt) == SQLITE_ROW){
NSInteger primaryKey = sqlite3_column_int(selectstmt,0);
NSLog(#"The P key is %d:",primaryKey);
Coffee *coffeeObj = [[Coffee alloc] initWithPrimaryKey:primaryKey];
coffeeObj.coffeeName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
coffeeObj.isDirty = NO;
[appDelegate.coffeeArray addObject:coffeeObj];
[coffeeObj release];
}
}
}
else
sqlite3_close(database);
}
- (id) initWithPrimaryKey:(NSInteger ) pk{
[super init];
coffeeID = pk;
NSLog(#"%d",coffeeID);
isDetailViewHydrated = NO;
return self;
}
+ (void)finalizeStatements {
if(database)
sqlite3_close(database);
}
#end
+++++++++++++ RootViewController.m +++++++++++++
\#import "RootViewController.h"
\#import "Coffee.h"
#implementation RootViewController
\#pragma mark -
\#pragma mark View lifecycle
\- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = self.editButtonItem;
appDelegate = (SQLAppDelegate *)[[UIApplication sharedApplication]delegate];
self.title = #"Name List";//Coffee List;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
\#pragma mark -
\#pragma mark Table view data source
// Customize the number of sections in the table view.
\- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
\- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"Rows %d",[appDelegate.coffeeArray count]);
return [appDelegate.coffeeArray count];
}
// Customize the appearance of table view cells.
\- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
Coffee *coffeeObj = [appDelegate.coffeeArray objectAtIndex:indexPath.row];
cell.textLabel.text = coffeeObj.coffeeName;
return cell;
}
+++++++++++++ SQLAppDelegate.m +++++++++++++
\#import "SQLAppDelegate.h"
\#import "RootViewController.h"
\#import "Coffee.h"
#implementation SQLAppDelegate
#synthesize window;
#synthesize navigationController;
#synthesize coffeeArray;
\#pragma mark -
\#pragma mark Application lifecycle
\- (void) copyDatabaseIfNeeded {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSString *dbPath = [self getDBPath];
// NSLog(#"%#",dbPath);
BOOL success = [fileManager fileExistsAtPath:dbPath];
NSLog(#"%d",success);
if(!success){
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"CoffeeDetail.sqlite"];
success =[fileManager copyItemAtPath:defaultDBPath toPath:dbPath error:&error];
if(!success)
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
\- (NSString *)getDBPath{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDir = [paths objectAtIndex:0];
//NSLog(#"dbpath %#",[documentDir stringByAppendingPathComponent:#"SQL.sqlite"]);
return [documentDir stringByAppendingPathComponent:#"CoffeeDetail.sqlite"];
}
\- (void) removeCoffee:(Coffee *)coffeeObj{
//Delete from DataBase
[coffeeObj deleteCoffee];
//Delete from the array
[coffeeArray removeObject:coffeeObj];
}
\- (void)applicationDidFinishLaunching:(UIApplication *)application {
[self copyDatabaseIfNeeded];
NSMutableArray *tempArray =[[NSMutableArray alloc]init];
self.coffeeArray = tempArray;
[tempArray release];
[Coffee getInitialDataToDisplay:[self getDBPath]];
NSLog(#"Array =%#", self.coffeeArray);
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
}
\- (void) deleteCoffee{
if(deleteStmt == nil){
const char *sql = "delete from Coffee where coffeeID = ?";
if(sqlite3_prepare_v2(database, sql, -1, &deleteStmt, NULL) != SQLITE_OK)
NSAssert1(0,#"Error while creating the delete Statement. '%s'", sqlite3_errmsg(database));
}
sqlite3_bind_int(deleteStmt, 1, coffeeID);
if(SQLITE_DONE != sqlite3_step(deleteStmt))
NSAssert1(0,#"Error while deleting. '%s'", sqlite3_errmsg(database));
sqlite3_reset(deleteStmt);
}
I think in deleteCoffee you're trying to use a variable that isn't set (database).
In my sqlite apps I have this method:
-(sqlite3 *) getNewDBConnection{
sqlite3 *newDBconnection;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"data.sqlite"];
// Open the database. The database was prepared outside the application.
if (sqlite3_open([path UTF8String], &newDBconnection) == SQLITE_OK) {
//NSLog(#"Database Successfully Opened");
} else {
NSLog(#"Error in opening database");
}
return newDBconnection;
}
and then in the methods that access the DB, I have something like:
- (void) deleteCoffee{
sqlite3 *database = [self getNewDBConnection]; //see this line
if(deleteStmt == nil){
const char *sql = "delete from Coffee where coffeeID = ?";
if(sqlite3_prepare_v2(database, sql, -1, &deleteStmt, NULL) != SQLITE_OK)
NSLog(#"Error while creating the delete Statement. '%s'", sqlite3_errmsg(database));
}
sqlite3_bind_int(deleteStmt, 1, coffeeID);
if(SQLITE_DONE != sqlite3_step(deleteStmt))
NSLog(#"Error while deleting. '%s'", sqlite3_errmsg(database));
sqlite3_reset(deleteStmt);
}