Tutorial on Creating an IOS 5 SQLite Database Application | IOS 5 | iPhone | iPad
By klanguedoc
IOS 5 and SQLite make a powerful combination for building data persistent iPad, iPhone or iPod Touch mobile applications. The IOS 5 SDK provides native support for SQLite through the use of the C programming language. This tutorial will walk you through how to setup a SQLite database application and to read text and images from the database into a scene.
Create the database
To start with, you will need FireFox from Mozilla and the SQLite Database Manager plugin. If you don’t have them, they can be downloaded and installed from the FireFox web site. Once FireFox is installed, install the SQLite Manager from the Add-on Manager.
The SQLite Manager can be launched from the Firefox menu or Tools menu depending on the version you are using (see figure 1).
Click on the new Database button (figure 2) to create a new database. You can give any meaningful name you want. Note, the SQLite extension will be automatically appended. You will be prompted to save the file to the file system, (naturally). Take note where you are saving it because you are going to copy the file later into your project.
Next, click on the new table button (figure 3) to create a new table, again I will leave it up to you to name it something useful. For this tutorial, my named the table wineTbl and I have created four columns: id [primary, autoinc, integer], winename [varchar], winerating [varchar] and wineimage [blob].
For the sake of this tutorial, I will pre-populate the database with some wine entries and images from the web. You can add data by selecting the table and selecting the browse and data tab. To upload a image, click on the paper clip icon next to the blob field. (Figure 4 and figure 5).
Now you can close the database from the Firefox menu and Firefox as well since we won’t need anymore for the tutorial.
Create IOS 5 Project
Launch XCode 4.2 and create a Single-View IOS 5 application. Give it a meaningful name and select Storyboard and ARC. Setup your Git, or not, source control and complete the creation of your project. (figure 6).
Configure SQLite
Expand the Frameworks folder, right click on one of the frameworks and select Show in Finder to open Finder at the Framework location. You will need to add the libsqlite_3.0.dylib file to your project (figure 6), so move up two or three levels (see Go to Enclosing folder in the Finder menu) until you get to the usr folder. Open it and open the lib folder. Scroll down until you find the sqlite_3.0.lib. Drag the file to your Frameworks taking care to NOT copy file into the frameworks, but ONLY create a reference (Figure 7).
Next select the project root, right click and select Show in Finder. Locate your sql database you created in the first part of this tutorial and copy it into the project group where you project header and implementations files are (Figure 8).
Setup DAO Operations
Create a new Group (File | New Group) or from the (Context Menu | New Group). Name it “Model”. Next create two Objective-C implementation files and corresponding header files. Select the Model group and from the File menu or Context menu | select New File. Select the Objective-C node and then the Objective-C class template.
Give your file a name: WineList (if you are following this tutorial), select NSObject as the Subclass and create the file. Repeat the process for the next set of files: MyWineList, (or you can choose a name like WinesDAO). Again select the NSObject as the Subclass and create the file (Figure 9).
For the WineList class create four properties in the WineList.h (header) file, one for each column in the wineTbl (Figure 10):
- wineId
- wine
- rating
- photo
Next open the WineList.m (implementation) file to set up the getter and setter methods. So your WineList should contain four @synthesize statements, one four each property (Figure 11).
- @synthesize wineId;
- @synthesize wine;
- @synthesize rating;
- @synthesize photo;
Create CRUD Operations
Well CRUD is a bit of a stretch. For this tutorial it is really just a R (read) operation. Ok now the application is going to need DAO classes for the CRUD (Read) operations, so if you haven’t already done so, create a new Objective-C class: MyWineLists or whatever you want so long as the declaration and implementation works. For the MyWineLists header file, a sqlite3 object is declared and an NSMutableArray method (figure 11):
- db
- getMyWines
To implement these objects, open the MyWineLists.m file. In this file, the gut if the operations will take place.
To start create the NSMutableArray method getMyWines and add an array pointer variable:
- wineArray
Next declare a NSFileManager object, a NSString object and a Bool object:
- fileMgr
- dbPath
- success
…
NSMutableArray *wineArray = [[NSMutableArray alloc] init];
@try {
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *dbPath = [[[NSBundle mainBundle] resourcePath ]stringByAppendingPathComponent:@"IOSDB.sqlite"];
BOOL success = [fileMgr fileExistsAtPath:dbPath];
...
The dbPath will contain to the filename and path of the SQLite database which will be passed to the fileMgr. If the file is located, success will be true. Next test to see if the file was located and if not log an error. The following operation will attempt to open the database, sqlite3_open before setting up the Select statement and sql3_stmt:
- sql
- sqlStatement
…
if(!success)
{
NSLog(@"Cannot locate database file '%@'.", dbPath);
}
if(!(sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK))
{
NSLog(@"An error has occured.");
}
const char *sql = "SELECT id, Wine, Rating, Photo FROM WineTbl";
sqlite3_stmt *sqlStatement;
if(sqlite3_prepare(db, sql, -1, &sqlStatement, NULL) != SQLITE_OK)
{
NSLog(@"Problem with prepare statement");
}
...
If the database is successfully opened, the sqlite3_prepare will attempt to execute the sqlStatement. If the statement is successfully executed resulting in a result set being returned, then execute a while loop to traverse the result set assigning the values to the NSMutableArray fields.
...
while (sqlite3_step(sqlStatement)==SQLITE_ROW) {
WineList *MyWine = [[WineList alloc]init];
MyWine.wineId = sqlite3_column_int(sqlStatement, 0);
MyWine.wine = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement,1)];
MyWine.rating = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 2)];
const char *raw = sqlite3_column_blob(sqlStatement, 3);
int rawLen = sqlite3_column_bytes(sqlStatement, 3);
NSData *data = [NSData dataWithBytes:raw length:rawLen];
MyWine.photo = [[UIImage alloc] initWithData:data];
[wineArray addObject:MyWine];
}
}
@catch (NSException *exception) {
NSLog(@"An exception occured: %@", [exception reason]);
}
@finally {
return wineArray;
}
...
This pretty much takes care of the of the cRud operations. The next step will involve setting up the UI, creating IBActions and IBOutlets connections. (See figure 12, 13).
Create UI Operations
Start by locating and opening the storyboard file. You should have a single blank scene (View Controller). For this part, four labels (UILabel) are required: one for Wine Name and the value from the database and likewise for the two others: one for Wine Rating and the corresponding value from the database that will be stored in the NSMutableArray. For the images, drag an UIImageView onto the scene. As a final step for the UI, drag a UIToolbar and place it at the bottom of the screen and rename the included button: Next Bottle (Figure 14).
To finish off the app, some code needs to be added to the ViewController header and implementation files. So to setup the IBAction and IBOutlet, open the header file alongside the storyboard by clicking on the Assistant Editor, the face icon in the Toolbar (Figure 14). Start by selecting the first label and dragging a connection line (Ctrl+left mouse button) to the header file between the last curly brace and the @end directive. In the Popup, select IBOutlet and enter a name like: winename. Continue with second label that will contain the rating information. This will also be an IBOutlet and the name will be: winerating. For the image, repeat the same operation as the two preceding ones. This connection will also be an IBOutlet and the name will be : wineViewer. Finally drag a connection line from the button in the Toolbar. This will be an IBAction and the name of the method: GetWineListing. Also add a NSMutableArray object:
- wines
You should have little filled in dot in the margin indicating that connections have been made.
Next open the implementation file. Setup the getter and setters:
…
@synthesize wineViewer;
@synthesize winename;
@synthesize winerating;
@synthesize wines;
…
In the viewDidLoad, which is called when the app is finished initializing itself, add pointers to hold the initial data in the array so the app will display some information and image that is located at index 0.
…
- (void)viewDidLoad
{
MyWineLists * mywines =[[MyWineLists alloc] init];
self.wines = [mywines getMyWines];
[self.wineViewer setImage:((WineList *) [self.wines objectAtIndex:0]).photo];
[self.winename setText:((WineList *) [self.wines objectAtIndex:0]).wine];
[self.winerating setText:((WineList *) [self.wines objectAtIndex:0]).rating];
[super viewDidLoad];
}
...
in the viewDidUnload set your properties to nil to release them from memory
…
- (void)viewDidUnload
{
[self setWineViewer:nil];
[self setWinename:nil];
[self setWinerating:nil];
[super viewDidUnload];
}
...
Finally implement the GetWineListing method, so when the user clicks on the button, the index gets incremented and retrieves the data at the selected index number.
…
- (IBAction)GetWineListing:(id)sender {
static NSInteger currentIndex = 0;
if (++currentIndex == [self.wines count]) {
currentIndex=0;
}else{
WineList *aWine = (WineList *) [self.wines objectAtIndex: currentIndex];
[self.winename setText:aWine.wine];
[self.winerating setText:aWine.rating];
[self.wineViewer setImage:aWine.photo];
}
}
…
Test Your App
Ok, we are done. Click on the Run button to launch your app. After the app is finished initializing you should have from data and image on screen. Click the Next Bottle to get the next listing.
Source Code
Here is the complete source code of the various files that were created.
WineList.m
// // WineList.m // MyWineList // // Created by Kevin Languedoc on 11/25/11. // Copyright (c) 2011 kCodebook. All rights reserved. // #import "WineList.h" @implementation WineList @synthesize wineId; @synthesize wine; @synthesize rating; @synthesize photo; //With ARC, if you selected id, you don't need to dealloc @end
MyWineLists
//
// MyWineLists.h
// MyWineList
//
// Created by Kevin Languedoc on 11/25/11.
// Copyright (c) 2011 kCodebook. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <sqlite3.h>
@interface MyWineLists : NSObject{
sqlite3 *db;
}
- (NSMutableArray *) getMyWines;
@end
WineList.h
//
// WineList.h
// MyWineList
//
// Created by Kevin Languedoc on 11/25/11.
// Copyright (c) 2011 kCodebook. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface WineList : NSObject{
NSInteger wineId;
NSString *wine;
NSString *rating;
UIImage *photo;
}
@property (nonatomic,retain)NSString *wine;
@property (nonatomic, assign) NSInteger wineId;
@property (nonatomic, retain)NSString *rating;
@property (nonatomic, retain) UIImage *photo;
@end
MyWineLists.m
//
// MyWineLists.m
// MyWineList
//
// Created by Kevin Languedoc on 11/25/11.
// Copyright (c) 2011 kCodebook. All rights reserved.
//
#import "MyWineLists.h"
#import "WineList.h"
@implementation MyWineLists
- (NSMutableArray *) getMyWines{
NSMutableArray *wineArray = [[NSMutableArray alloc] init];
@try {
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *dbPath = [[[NSBundle mainBundle] resourcePath ]stringByAppendingPathComponent:@"IOSDB.sqlite"];
BOOL success = [fileMgr fileExistsAtPath:dbPath];
if(!success)
{
NSLog(@"Cannot locate database file '%@'.", dbPath);
}
if(!(sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK))
{
NSLog(@"An error has occured.");
}
const char *sql = "SELECT id, Wine, Rating, Photo FROM WineTbl";
sqlite3_stmt *sqlStatement;
if(sqlite3_prepare(db, sql, -1, &sqlStatement, NULL) != SQLITE_OK)
{
NSLog(@"Problem with prepare statement");
}
//
while (sqlite3_step(sqlStatement)==SQLITE_ROW) {
WineList *MyWine = [[WineList alloc]init];
MyWine.wineId = sqlite3_column_int(sqlStatement, 0);
MyWine.wine = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement,1)];
MyWine.rating = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 2)];
const char *raw = sqlite3_column_blob(sqlStatement, 3);
int rawLen = sqlite3_column_bytes(sqlStatement, 3);
NSData *data = [NSData dataWithBytes:raw length:rawLen];
MyWine.photo = [[UIImage alloc] initWithData:data];
[wineArray addObject:MyWine];
}
}
@catch (NSException *exception) {
NSLog(@"An exception occured: %@", [exception reason]);
}
@finally {
return wineArray;
}
}
@end
kcbViewController
//
// kcbViewController.h
// MyWineList
//
// Created by Kevin Languedoc on 11/25/11.
// Copyright (c) 2011 kCodebook. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface kcbViewController : UIViewController{
NSMutableArray *wines;
}
@property(nonatomic,retain) NSMutableArray *wines;
@property (weak, nonatomic) IBOutlet UIImageView *wineViewer;
@property (weak, nonatomic) IBOutlet UILabel *winename;
@property (weak, nonatomic) IBOutlet UILabel *winerating;
- (IBAction)GetWineListing:(id)sender;
@end
kcbViewController.m
//
// kcbViewController.m
// MyWineList
//
// Created by Kevin Languedoc on 11/25/11.
// Copyright (c) 2011 kCodebook. All rights reserved.
//
#import "kcbViewController.h"
#import "WineList.h"
#import "MyWineLists.h"
@implementation kcbViewController
@synthesize wineViewer;
@synthesize winename;
@synthesize winerating;
@synthesize wines;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
MyWineLists * mywines =[[MyWineLists alloc] init];
self.wines = [mywines getMyWines];
[self.wineViewer setImage:((WineList *) [self.wines objectAtIndex:0]).photo];
[self.winename setText:((WineList *) [self.wines objectAtIndex:0]).wine];
[self.winerating setText:((WineList *) [self.wines objectAtIndex:0]).rating];
[super viewDidLoad];
}
- (void)viewDidUnload
{
[self setWineViewer:nil];
[self setWinename:nil];
[self setWinerating:nil];
[super viewDidUnload];
}
- (IBAction)GetWineListing:(id)sender {
static NSInteger currentIndex = 0;
if (++currentIndex == [self.wines count]) {
currentIndex=0;
}else{
WineList *aWine = (WineList *) [self.wines objectAtIndex: currentIndex];
[self.winename setText:aWine.wine];
[self.winerating setText:aWine.rating];
[self.wineViewer setImage:aWine.photo];
}
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
@end
|
|
GEVEY Ultra S Multi-Network Unlocks CDMA and GSM iPhone 4S iOS 5 5.0.1 5.1 5.1.1
Current Bid: $27.75
|
|
|
GEVEY Ultra S Multi-Network Unlocks CDMA and GSM iPhone 4S iOS 5, 5.0.1
Current Bid: $24.99
|
|
|
AV to TV Cable + Camera Connection Kit SD Card Reader for New iPad 3 & 2 iOS 5.1
Current Bid: $29.72
|
In summary
That pretty wraps it up. In the hindsight, implementing SQLite was very easy. The only thing missing is the operations to create records, updating and deleting them. That as they say is another story, or article in this case. If you need to know how to perform inserts, updates and deletes, check out my article: IOS 5 SDK Database: Insert, Update, Delete with SQLite and Objective-C | C | How-To
Another useful tutorial is : Tutorial on How to Display & Write SQLite Data in Table View Controller & Detail View Controller | iOS 5 App
Comments
this is a great tutorial! I am curious - can you add a search function to this?
Hi Mike. Thanks for your kind comment. Yes you can if it follows standard SQL syntax. You could have a search field that converts the search term(s) into individual tokens and and store those individual terms into an Array (NSArray) and then build your WHERE clause based on the elements of the array. It is not straightforward but it can be done.
Hope this helps and let me know how it works out for you.
klanguedoc
Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'
Getting this error.. any fix?
This error simply means that your array is empty, so your program is trying to read data at the first position, 0, and there is nothing to read. Check to make sure the array you are using is indeed populated with data from the database.
This error simply means that your array is empty, so your program is trying to read data at the first position, 0, and there is nothing to read. Check to make sure the array you are using is indeed populated with data from the database.
unfortunately I am new to xcode, objective-c and coding in general. you wouldn't happen to know of a tutorial that explains what you suggested in more detail?
I would suggest you read The C Programming Language by Dennis Ritchie. It is base of all things Objective-C and even many of the libraries in Apple SDK are in C. Next you could buy/read a book on Objective-C like the Big Nerd Ranch.
Hope this helps
I have the same problem of the guy's comment above. I'va experience with C programming, etc etc. But I can't figure out that problem. I know my array is not being populated; you know why? Im following your code... can you give us a clue?
Hi SirJko,
Can you post your code in the comments so that I can have a look at it and maybe I can spot where the issue is. I would love to help you out.
klanguedoc
Thanks! I finally could fix the error. It was something about the SQL statement.
I would be pretty good to have an entry about merging this tuto with the uitableview's one.
Thanks for your answer and time.
Thanks for the suggestion. I will gladly write a new hub showing how to display contents from a database in an UITableView. I also glad that you were able to get your app working.
Klanguedoc
Nice tutorial, I have followed your tutorial and have got it going. However, when i click the next bottle button it doesnt show the next bottle. Any ideas?
Thanks
Hi ratunolan
Thanks for the kind words.
Did you add a connection and a delegate for the button. You must do this from the Storyboard by opening the header file alongside the storyboard and drag a connection from the button to the header file.
Thanks for your hub.keep adding such content.
Thanks for the support and kind words Maxravi. I am currently working on a couple related Hubs that are similar to this one. Hope to have them published within a week.
Thanks for a good Tutorial.
One bug in the GetWineListing method.
Code should go into IF statement
(if ++currentIndex != [self.wines count])
{
}
Also there should be an ELSE }
currentIndex =0;
}
otherwise "Next Wine" button does not do anything..
Thanks Viji for catching that. Actually there are two ways to write back bit of code; as stated above or remove the first curly brace (see below) so only the first statement will evaluate. Anyway I added an ELSE statement since it make the code easier to read.
if (++currentIndex == [self.wines count]) currentIndex=0;
WineList *aWine = (WineList *) [self.wines objectAtIndex: currentIndex];
[self.winename setText:aWine.wine];
[self.winerating setText:aWine.rating];
[self.wineViewer setImage:aWine.photo];
Thanks again.
This is a great help, any chance you explain how to put all the wine names in a table view then go to a detailed when pressed?
Thanks Mark, you can check out my other tutorial on UITableViews: http://klanguedoc.hubpages.com/hub/IOS-5-How-To-Cr
It will get you going in the direction.
Hope this helps
Klanguedoc
Is there something special I need to do with "Count" or "Group By" sql commands? because I'm missing some records here...
This sql query gives only 1 record while there should be 2: "SELECT Name, COUNT(*) FROM Items, Groups WHERE Groups.Id=Items.GroupId GROUP BY Items.GroupId"
Note: Each group can contain many items.
Ps: Running the same sql query in Firefox's SQL Manager gives 2 results.
Anyone got a clue?
Try taking out the name entity and the group by clause for testing purposes
Hey mate, thanks for the tutorial.
I followed it as it is and in the end, I ran this problem while building the app:
"Thread 1: Program received signal: "SIGABRT"."
What should I do to take care of this problem? Thanks!
Hi Eric,
Thanks for the kind words...
Yes this is one of the GDB infamous messages. A couple of solutions are at hand: First run your app and open the console. There will be a lot of verbose text, but information that is important to you will be in bold. Scroll to the start of the bold text and you should get the exact reason why you are getting this message.
Second: If no apparent reason that make sense is available in step one, the SIGABRT signal will be triggered when there is multiple instances of your app that is running in memory,if if you have exited the Simulator. To fix you can reset Simulator (see menu), you can also close all projects and restart Xcode or restart your computer.
However, for the most part, the first option, check the bold text in the Console, will point to the error message and how to fix it.
Hope this helps and let me know if I can help further.
Kevin
2012-01-19 16:39:39.309 ProjectWine[76694:f803] Problem with prepare statement
2012-01-19 16:39:39.392 ProjectWine[76694:f803] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'
*** First throw call stack:
(0x147d052 0x160ed0a 0x1469db8 0x286a 0xd964e 0x39a73 0x39ce2 0x39ea8 0x40d9a 0x11be6 0x128a6 0x21743 0x221f8 0x15aa9 0x1367fa9 0x14511c5 0x13b6022 0x13b490a 0x13b3db4 0x13b3ccb 0x122a7 0x13a9b 0x2488 0x23e5)
terminate called throwing an exception
That's what I got, is objectAtIndex error caused because I have an empty field in the db? Not sure about the prepare statement one...
Hi Eric,
This simply means that your array that points to the NSMutableArray, is empty. check your code where the array is being populated from the Select statement.
I think this was informative! I bookmarked it since I am starting to develop iOS apps!
Thanks for your kind words. Best of luck and success with your apps, mikeydcarroll67.
I do not have a file named "sqlite_3.0.lib", but I do have a file named "libsqlite_3.0.dylib". Same thing?
Actually, you are supposed to use the "libsqlite_3.0.dylib" file. Thanks for bringing that to my attention. I will correct the text.
Thanks for quick response, klanguedoc!
Glad to help! Enjoy your coding
OK I figured out the databases (I had two separate databases but wanted to read and write to them separately, not through a join function). However, I do get an incomplete implementation, even though it looks like everything is in order.
Also, how did you set up your labels? I am having trouble loading the information into a single view controller.
I finally got it going, which was awesome. I want to be able to read from a sqliteDB to a customed TableViewCell and then a DetailView with images,text,scrollViews do you have a tutorial for that.
I have a couple of tutorials here for that. I am currently finishing a tutorial on DetailTableController. Basically how to display content in a static UITableView.
I have these tutorials that may help you out in the meantime:
http://klanguedoc.hubpages.com/hub/IOS-5-Storyboar
or
http://klanguedoc.hubpages.com/hub/IOS-5-How-To-Di
or
http://klanguedoc.hubpages.com/hub/IOS-5-How-To-Cr
To push data to a DetailViewController, you need to implement a segue and the prepareForSegue method. Something like this code snippet.
(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"NameOfSegueBetweenTableViewControllerAndDetailViewController"])
// Get the detail view controller.
PhraseDetailViewController* destVC =
(PhraseDetailViewController*)segue.destinationViewController;
// Find the correct dictionary from the documents array.
NSIndexPath *cellPath = [self.tableView indexPathForSelectedRow];
UITableViewCell *theCell = [self.tableView cellForRowAtIndexPath:cellPath];
// Assign the URL to the detail view controller and
// set the title of the view controller to the doc name.
destVC.detailItem = theCell.textLabel.text;
destVC.navigationItem.title = theCell.detailTextLabel.text;
i copied the same codes as given and made the same files but i get "Thread 1: Program received signal: "SIGABRT"."
each time i run it i tried all what was given above but cant solve it, am i missing something ? how do i check ? error is like
2012-02-12 22:09:35.847 MyWineList[2183:f803] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[kcbViewController 0x6854be0 setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key wineviewer.'
*** First throw call stack:
(0x1586052 0x1717d0a 0x1585f11 0xa74032 0x9e5f7b 0x9e5eeb 0xa00d60 0x2f391a 0x1587e1a 0x14f1821 0x2f246e 0x199e2c 0x19a3a9 0x19a5cb 0xfaa73 0xface2 0xfaea8 0x101d9a 0xd2be6 0xd38a6 0xe2743 0xe31f8 0xd6aa9 0x1470fa9 0x155a1c5 0x14bf022 0x14bd90a 0x14bcdb4 0x14bcccb 0xd32a7 0xd4a9b 0x2488 0x23e5)
terminate called throwing an exceptionsharedlibrary
I would have to see more of your code because the code has been fully tested and many more have implemented the code as is and works fine. From the error it looks like you are trying to set a key-pair value for a dictionary object. I am more than wiling to help if you could poste your code in the comments. Maybe I can see something.
can i have your email id as cant post the signs great and less then or html here , it show an error,
or if possible i will send the full folder ,
is there any chance i made a mistake in the storyboard in giving the values? this is my first sqlite tutorial and its been over 10 hrs figuring it out i entered the exact guideline over 4-5 times but still get that error, and i cant stop unless i find out whats wrong. regards, my email id is drgipatel@gmail.com
Sure, in the sidebar above, just below the advertisement, there is an e-mail link so you can send me an e-mail. It might take me a day or two to get back because I am working on a book, besides my job so my time is a bit stretched, but I will gladly have a look to see where things are not working. It could be the storyboard. You could try deleting it and creating a new one with the same name otherwise you will have to edit the info-plist file and change the name of the storyboard.
Hello I finally found the problem it was in the Storyboard and adding the controls , after i run i saw the data but when i pressed the next button again some error will check it out tommorow, 2012-02-13 22:41:52.984 MyWineList[2798:f803] Applications are expected to have a root view controller at the end of application launch
also if i wanted to search for all rating = good how do i get it from the database as present in a list form?
Excellent
Hi Klanguedoc
Does SQLite work with Xcode 3? I am writing iOS Apps but I don't have a developer account to dl Xcode 4.
Thanks!
Since SQLite is a C based API it should work any version of Xcode. Before the arrival of Core Data, SQLite was the standard storage facility for iPhone & iPad apps. As long as the lib is available, you shouldn't have any problems.
Great Tutorial, thanks. However I have one question, the get wines button will bring up the next in the sequence, but I also want to implement a previous button. I have tried experimenting with this but I'm stumped by the code to make it go to the previous. Any help please!?
One other question, I need to retrieve one of the items in the table as an NSString. I have an IBAction button and when I press that button I need to retrieve one of the row entries as an NSString.
Thanks for the kind words...
For the previous button, try using rowIndex-1. I haven't tested this but should work.
I am assuming here that when you say retrieve a row from table, you are talking about the database, otherwise please redirect me.
for converting value to NSString, the NSString class many methods available for taking a value as input and converting it to NSString.
Instead of using something like this
NSString * string = [[NSString alloc] init];
you could use this:
NSString * string = [[NSString alloc] initWithFormat:FormatValue from Database column];
Please see this url:
https://developer.apple.com/library/mac/#documenta/Cocoa/Conceptual/Strings/Articles/FormatStrings.html#//apple_ref/doc/uid/20000943
Thanks for this, setting rowIndex-1 worked great for the back button. Only thing I notice is, that after going back, I have to press next twice to move forward again.
For the NSString value, I currently have this :-
-(IBAction)playMovie:(id)sender {
NSError *error;
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *documentsDirectory = [kUADownloadDirectory stringByAppendingPathComponent:@"com.isoftware.illusionist.SlipForce"];
NSLog(@"documents Directory: %@", [fileMgr contentsOfDirectoryAtPath:documentsDirectory error:&error]);
BOOL exists = [fileMgr fileExistsAtPath:documentsDirectory];
if (exists == YES) {
NSString *filepath = [kUADownloadDirectory stringByAppendingPathComponent:@"com.isoftware.illusionist.SlipForce"];
NSString *path = [filepath stringByAppendingPathComponent:@"movie.mp4"];
NSURL *fileURL = [NSURL fileURLWithPath:path];
moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:fileURL];
What I need to do is substitute the @"com.isoftware.illusionist.SlipForce" with an NSString taken from the selected row in the database. I've added a UIButton to the view controller as an IBAction.
I got it figured out, thanks for the pointers every things working now :-) great! Thanks, I've been trying to get this working for hours!
Great Barrowclough,
Glad to hear that you were able to breakthrough to roadblock. I wish you lots of success with your app.
Kevin
Thanks for ur tutorial.......
Your very welcome pavan.
Thanks for the tutorial. Very helpful. I have a program that involves uiviews, uiviewcontrollers, navigation controls, among other things. I have built a large array within these programs and I'm using your example to put the data in these arrays into a sqlite database. It will make my program much more flexible in the long run.
Thanks for your help. I got a small portion of my program running last night reading the database. I will convert the rest of my program during the week and may have questions.
One question I wanted to ask in my last entry before I hit enter. Here it is.
I've been a database programmer for over 20 years and know SQL quit well. I'm leaning to using SQlite but do you see any advantage of using Core Data? I know I can use SQlite as the storage for Core Data but I know and like to use SQL. My program will mostly be doing reads from a database that has about 130 rows and will have about 5 tables that will link to each other. I may have one small bit of functionality where I will need to let the user add data and delete data.
So what do you think? SQLite or Core Data? I don't know Core Data at all. Thanks.
Hi Todd,
That is a good question which i will try answer truthfully. If you are well versed in SQL as you say, then you could simply SQLite as is and embed your SQL query directly in the code. However for more strenuous query and data models as you well know can become quite complex, I would opt for Core Data.
In SQLite instead of having sprocs, you would need to develop functions and add them to them database. what Core Data brings to the table is a sophisticated E/R designer with full ORM capabilities which makes complex data modeling a breeze. In addition using Predicates (NSPredicate),which compares to other on the market like Hibernate, EJB3, or TopLink form Oracle to name a few, you can map your data entities to objects very easily and build very complex queries.
I use both; for light lifting, i use SQLite, but more complex or enterprise class apps, I use Core Data.
I hope this makes things a little clearer.
Kevin
Thanks Kevin. Very helpful. I think for the future features I want to add to my app that I will learn Core Data and use it.
Is there a way I could possible download the source code for this app?
Harry Blackmore
Helle and thank you for this tutorial
I am working on a project and i tried to use your method to get informations from a table, but i have a litte problem that he can't find my Table.
I used sqliteManager for firefox to create the database and i put it into my project !
Can you help me please ?
thank you
*Hello
Hi mra59,
Does your code open the database properly? Can you see if the database variable is set from the Console window?
Kevin
Hi Harry Blackmore,
If you send me an e-mail, see link on this page (near top on the righthand) I can send you a zip of the project.
Kevin
Thank you for your message.
I resolved my problem :
1- i put "NSString *dbPath = [[NSBundle mainBundle]pathForResource:@"IOSDB"ofType:@"sqlite"];" instead of "NSString *dbPath = [[[NSBundle mainBundle] resourcePath ]stringByAppendingPathComponent:@"IOSDB.sqlite"];"
2- To add the database sqlite file, i used the "add files to " and not the drag and drop method
Best regards.
I am glad you were able to find the problem. Thanks for your kind words
Hi, thanks again for the tutorial. I'm trying to implement a previous button. I looked through the messages here and saw one person asking for that. you said try "rowIndex-1" i can't see where to implement that?? I tried changing the currentIndex like this :-
- (IBAction)last:(id)sender {
static NSInteger currentIndex = 0;
if (currentIndex-1 == [self.products count]) {
currentIndex=0;
}else{
productList *aProduct = (productList *) [self.products objectAtIndex: currentIndex];
[self.productName setText:aProduct.title];
[self.productDetail setText:aProduct.detail];
[self.productMovie setText:aProduct.movie];
[self.productPDF setText:aProduct.pdf];
[self.image setImage:aProduct.image];
directory = aProduct.title;
filename = aProduct.movie;
}
}
But that doesn't work properly. I can scroll through next, but when I press the previous button it just returns me to the beginning of the array, not the last entry. Any help would be appreciated! Thanks!
It seems form your code that everything you call the last method, you are setting your row index to 0. The rowIndex that I am referring to is the index of the UITableCell that the user has selected. You would need to get the last rowIndex, maybe set a global variable, so that you button can do a currentIndex = rowIndexGlobalVariable -1. Also I wouldn't declare the currentIndex as static in the method since every time you call the method the variable gets reset to 0. Try implementing in the header
@property(nonatomic, strong) NSNumber * currentIndex;
In the implementation
@synthesize currentIndex;
then you could initialize it in the viewDidLoad or somewhere else like
currentIndex = [[NSNumber alloc] initWithInteger:0];
or
currentIndex = [[NSNumber alloc] initWithInt:0];
Hope this helps
K
Thanks, I think I follow what you mean. Just not 100% sure how to set the global variable and have it retain the last row that was selected.
when app is loaded, in the viewDidLoad method
currentIndex = 0;
.....
Say you have two buttons on your Scene; one to go forward and one to go back. Since your data is stored in a NSMutableArray, if you are following my code, when you click on the forward or button button, you can use something like
Forward button:
currentIndex += 1;
[[ProductsArray objectAtIndex:currentIndex] doSomethingWithReturnObject];
Back button:
currentIndex -= 1;
[[ProductsArray objectAtIndex:currentIndex] doSomethingWithReturnObject];
The objectAtIndex returns the object (or value) at index n. You just need to assign the object to another variable , etc.
K
Hi Kevin,
This question has nothing to do with database programming on the iPhone but I've asked this question other places and I'm not getting an answer. Please help.
My app will show a lot of pictures. I will have a screen showing small thumb nails of the pictures and then going to a detail screen showing a full screen of the picture.
So my question, should I scan and store two pictures, a small one of the thumb nail and a large one for the full screen? It seems logical to only scan and store one picture, a large one, to use in both places to save disk space. What do you suggest? Also, I plan on creating an iPad version so I need even a larger picture. So, how many pictures do I store? One for everything or one for thumbnails, one for old iPhone, one for high resolution iPhone, one for iPad and then one for the new iPad 3 coming out which will probably have a very high resolution screen?
I would scan and store one picture of each and just resize as needed. This is relativity easy; either with a web/native hybrid (ObjC with WebView and Javascript bridge) or with ObjC using UIImage or use the UIImageView. So , yes definitely resize.
I am glad you asked the question.
K
Kevin,
Thanks, I tried to implement this but I got errors. When I added the currentIndex=0 in the view did load method I got errors in the action for the next button saying that a local declaration of currentIndex is hiding the instance variable. and when I add currentIndex+=1 to the action I get an error saying "Arithmetic on pointer to interface NSNumber which is not a content size in none fragile ABI".
- (IBAction)getProduct:(id)sender {
currentIndex += 1;
if (currentIndex == [self.products count]) {
currentIndex=0;
}else{
productList *aProduct = (productList *) [self.products objectAtIndex: currentIndex];
[self.productID setText:aProduct.productID];
[self.prodName setText:aProduct.title];
[self.description setText:aProduct.description];
[self.image setImage:aProduct.image];
directory = aProduct.productID;
}
}
Nick
Try something like this:
add to header:
int nbr;
in implementation:
in viewDidLoad:
//if NSNumber is needed
number = [[NSNumber alloc] initWithInt:0];
nbr = 0;
NSString * nbrStr=[NSString stringWithFormat:@"%d",nbr];
[labelOutput setText:nbrStr];
===============================
in buttons:
nbr ++;
//or Nbr --;
//if you want to store in a number
number = [NSNumber numberWithInt:nbr];
NSString * nbrStr=[NSString stringWithFormat:@"%d",nbr];
[labelOutput setText:nbrStr];
I will write up a proper tutorial to fully demonstrate
I repost the link here later
kevin
Nick here is the tutorial
Great, thanks very much for your help with this I have got everything working correctly, here's my final code that is tested and works :-
//Skip to next product
- (IBAction)getProduct:(id)sender {
nbr++;
if (nbr == [self.products count]) {
nbr=0;
productList *aProduct = (productList *) [self.products objectAtIndex: 0];
[self.productID setText:aProduct.productID];
[self.prodName setText:aProduct.title];
[self.description setText:aProduct.description];
[self.image setImage:aProduct.image];
directory = aProduct.productID;
NSLog(@"number is %d",nbr);
}else{
productList *aProduct = (productList *) [self.products objectAtIndex: nbr];
[self.productID setText:aProduct.productID];
[self.prodName setText:aProduct.title];
[self.description setText:aProduct.description];
[self.image setImage:aProduct.image];
directory = aProduct.productID;
NSLog(@"number is %d",nbr);
}
}
//Skip to previous product
- (IBAction)previous:(id)sender {
//nbr--;
if (nbr-- == 0) {
nbr = ([self.products count] -1);
productList *aProduct = (productList *) [self.products objectAtIndex: nbr];
[self.productID setText:aProduct.productID];
[self.prodName setText:aProduct.title];
[self.description setText:aProduct.description];
[self.image setImage:aProduct.image];
directory = aProduct.productID;
NSLog(@"number is %d",nbr);
}else{
productList *aProduct = (productList *) [self.products objectAtIndex: nbr];
[self.productID setText:aProduct.productID];
[self.prodName setText:aProduct.title];
[self.description setText:aProduct.description];
[self.image setImage:aProduct.image];
directory = aProduct.productID;
NSLog(@"number is %d",nbr);
}
}
Cool. I am really happy you got everything working.
Have a good one!
Kevin
Thanks for the great tutorial.
It's possible update the data in this way?
Because i need a pre populated database and update or insert more data.
Yes, actually I also wrote a tutorial to perform CRUD ops. Please check out this one :
Thanks so much for the tutorial! Very clear, and it helped me fix a couple of bugs I had. I've been porting a C# program to the iPAD, and you really helped me make a breakthrough.
I am really glad I was able to help... thanks for the feedback.
Hi is a really nice tuto, i follow the steps and when i run, the app doesn't show the images, do you have any idea what's happening? should I positioning the images in project folder??
Thanks for the feedback...
When you run the app on debug, is the blob variable returning binary data. In other words, is the UIImage being properly assigned?
Hi klanguedoc,
This is a great tutorial. I got thru it this week and it is very straightforward and well explained. Can't wait for your book to come out!
Ric
One question though, I'm looking to do an app with an SQLite database, but with multiple views to the data in a tab view format. are there any special considerations in doing this and can you suggest any tutorials on this?
So sticking with the wine app theme, one tab could list all the wines in alphabetical order, the second tab consists of all wines ordered by rating.
Thanks again!, ric
Hello!
Thanks for the tutorial!
I'm getting an error message: Problem with prepare statement
That means that something is wrong with my database?
Thanks
Thanks.
Yes probably the prepare statement wasn't added properly to the database. Is the database properly initialized?
Kevin
I'm dong the debugging and that's the only thing that "fails" so I'm thinking that the DB is initialize because if not I would get "An error has occured" right?
someone else had that problem from what I read in the comments and was able to fix it but didn't post the solution.
i'm thinking that the problem is either in:
"const char *sql = "SELECT id, Wine, Rating, Photo FROM WineTbl";"
or
"if(sqlite3_prepare(db, sql, -1, &sqlStatement, NULL) != SQLITE_OK)"
Thanks
Hi Ricardo,
I am running the app with the above code and it runs ok. Is sqlite3_stmt *sqlStatement; properly initialized in debugger?
I somehow got it to work! Thank you so much for this tutorial!!
I have a question, this line:
[self.winename setText:((WineList *) [self.wines objectAtIndex:0]).wine];
prints the object in one UILabel, how about if I want several wines in that one label (I just make the label use multiple lines). Instead of using different labels.
I tried replacing objectAtIndex for objectAtIndexes but that didn't work. any ideas?
You're welcome Ricardo, I appreciate the feedback and the discussions.
Try loading the values in a string with stringByAppendingString. You can use in a loop.
Hi Ric,
Sorry I missed you comment... I didn't get a notification...strange. I suppose to you could load two arrays; one a for one list and the other for wine ratings. The second one you could assign to an id object and pass it to another UITableViewcontroller.
Hope this helps
K
thanks for sharing....http://www.dataentryhelp.com/data-extraction-servi
You are a Data Extraction specialist?
yes.. have any data extraction project plz send me
Simply nice and very helpful
I am glad you approve
Hey klanguedoc ...Thanks for the tutorial! this is really amazing stuff! I've been trying to get this to work on xcode 4.2. Can you help me? It keeps showing me the main.m and says SIGABRT when I try to run it. (there's a "problem with the prepare statement" I've included the page it keeps showing me below.
//
// main.m
// MyWineList4
//
// Created by me
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import "kcbAppDelegate.h"
int main(int argc, char *argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([kcbAppDelegate c
This is what is in the output box apparently there's a "problem with the prepare statement":
GNU gdb 6.3.50-20050815 (Apple version gdb-1708) (Mon Aug 15 16:03:10 UTC 2011)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin".Attaching to process 3428.
2012-04-21 19:12:42.796 MyWineList4[3428:207] Problem with prepare statement
sharedlibrary apply-load-rules all
Current language: auto; currently objective-c
(gdb)
Hi, thanks for kind comments, I am glad you find my tutorial useful.
Problem with prepare statement
This means your query string, like:
const char *sql = "SELECT id, Wine, Rating, Photo FROM WineTbl";
sqlite can't parse the your query string. There might be a typo somewhere, maybe missing a comma. Anyway your query string is not well formulated. It occasionally happens to me since there is no syntax checker for the string itself but the string needs to match sqlite syntax requirements.
Hope this helps
Kevin
Thanks for this tutorial
i'm getting an exception while running the code like this
Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'
but it's working for some rows
plz help me i'm new for this obj-c
Hey Klanguedoc,
Thanks for getting back to me. Can you tell me where to look to find this error?
thanks,
Isaac
This is what you said before:
Hi, thanks for kind comments, I am glad you find my tutorial useful.
Problem with prepare statement
This means your query string, like:
const char *sql = "SELECT id, Wine, Rating, Photo FROM WineTbl";
sqlite can't parse the your query string. There might be a typo somewhere, maybe missing a comma. Anyway your query string is not well formulated. It occasionally happens to me since there is no syntax checker for the string itself but the string needs to match sqlite syntax requirements.
Hope this helps
Kevin
venkat duvvuri
You are probably looping pass the bounds of the array
Hi Isaac,
Are you using a where clause, or are you doing something like "Select * from table"
For the first
"select cola, colb from table where colc = ?"
Don't try replacing the interrogation with a string value, like appending a NSString value because this will cause an error.
For the second possibility, always use column names instead of the wild card
Otherwise can you post your code in the comments
Kevin
The sqlite3_prepare is returning 1 and not SQLITE_OK, which will be a problem with prepare statement, how to debug such error?
Hi Klaws,
A return code = 1 means SQLITE_ERROR. SQLITE_OK is equal to 0. It is hard to provide advice without seeing the code.
There is something wrong (I know this sounds lame) with the syntax of your query string. You could try running the query from the SQLite command line.
From the SQLite web site, an error code = 0 usually means /* SQL error or missing database */
Is your db properly set?
Kevin
change the wine to winename in the sql query
change the sql query "SELECT id, wine,rating, photo from WineTbi" to "SELECT id, winename, rating,photo from WineTbi" change the wine to winename and it will work
vomr 5 months ago
Thanks very helpful!