TwitterCounter for @bigclick_dean

dBlog.com.au

My Development Blog

I see many people asking for SQLite tutorials around, and since I am using SQLite for the next part in the Advanced RSS Reader Tutorial, I thought I would write up a quick tutorial on using SQLite with the iPhone SDK.

1. Project Requirements

I suggest that you have at least a basic understanding of SQLite, writing SQL statements, the XCode interface and using the terminal in OSX. If you don’t know anything about any of these topics then this tutorial probably isn’t for you.

2. Creating our SQLite database for our tutorial

We first need to create a database for use with our application. For the purposes of this tutorial we will be building a database of animals along with a little information on them and a picture.

Fire up a new Terminal window and make a new folder to store the database in, here are the commands I ran

cd /Users/lookaflyingdonkey/Documents
mkdir SQLiteTutorial
cd SQLiteTutorial
sqlite3 AnimalDatabase.sql

You should now be at a “sqlite” command prompt, this is where we will be building our database structure and entering some test data.

For our example we need the name of the animal, a short description and a link to an image. Follow the commands below to create the table and to enter some sample data.

CREATE TABLE animals ( id INTEGER PRIMARY KEY, name VARCHAR(50), description TEXT, image VARCHAR(255) );

INSERT INTO animals (name, description, image) VALUES ('Elephant', 'The elephant is a very large animal that lives in Africa and Asia', 'http://dblog.com.au/wp-content/elephant.jpg');
INSERT INTO animals (name, description, image) VALUES ('Monkey', 'Monkies can be VERY naughty and often steal clothing from unsuspecting tourists', 'http://dblog.com.au/wp-content/monkey.jpg');
INSERT INTO animals (name, description, image) VALUES ('Galah', 'Galahs are a wonderful bird and they make a great pet (I should know, I have one)', 'http://dblog.com.au/wp-content/galah.jpg');
INSERT INTO animals (name, description, image) VALUES ('Kangaroo', 'Well I had to add the Kangaroo as they are the essence of the Australian image', 'http://dblog.com.au/wp-content/kangaroo.jpg');

The first command will create the table with the required structure and the next four will insert some test data for us to work with. To ensure that you have entered the data correctly you can execute “SELECT * FROM animals;” and see if it returns the items above. Once you are confident that everything had been created successfully you can leave the sqlite command line by typing “.quit”.

3. Creating our Project

Now that our database is all ready to go we need to setup our X-Code project.

Start off by creating a new “Navigation-Based Application”.

Give your Project a name, I called mine “SQLiteTutorial”.

Now set your screen layout to how you prefer it, I suggest making the window as large as possible, and making the code view as tall as possible by dragging the horizontal slider to the top. This will allow you the most room to move when building your application.

Now its time to create the required classes and views for our application, we will start off by making our views.

Right Click on the “Resources” folder in the left hand pane and click “Add File”, we want to create a new “View XIB” under the “User Interfaces” group.

We now need to give it a name, to stick the Apple’s naming conventions we are going to call it “AnimalViewController.xib”, Now Click “Finish”.

Now we need to create two classes, the first one will represent an animal, right click on the “Classes” folder in the left hand pane, click “Add > New File…”, choose the “NSObject subclass” template under the “Cocoa Touch Classes” group and name it “Animal”.

The second class will be for our AnimalsViewController, right click on the “Classes” folder in the left hand pane, click “Add > New File…”, choose the “UIViewController subclass” under the “Cocoa Touch Classes” group and name it “AnimalViewController”.

4. Adding SQLite Framework and our Animal Database

Now that we have created all of our views and classes it is time to start the real grunt work.

First off we need to include the SQLite libraries so our application can utilise them. To do this you will need to right click on the “Frameworks” folder in the left hand pane, then click on “Add > Existing Frameworks…”, then navigate to “/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/usr/lib/” and double click the “libsqlite3.0.dylib” file. A popup will appear, just click “Add” and the library will be added to your project.

We also need to add our database we created earlier to the Resources folder, to do this simply right click on the “Resources” folder, click “Add > Existing Files…”, navigate to the location you created the database in then double click on the AnimalDatabase.sql file. Another popup will appear, just click add.

All done with the importing, time to code!

5. The Coding begins!

We are going to start the coding by building our “Animal” object, every animal will have 3 properties, a name, a description and an image URL.

Open up the “Animal.h” file from the “Classes” folder and edit its contents to look like below,

#import <UIKit/UIKit.h>

@interface Animal : NSObject {
	NSString *name;
	NSString *description;
	NSString *imageURL;
}

@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSString *description;
@property (nonatomic, retain) NSString *imageURL;

-(id)initWithName:(NSString *)n description:(NSString *)d url:(NSString *)u;

@end

Most of the above code should be pretty familiar to you, the only thing that may not be is the initWithName line, this line will allow us to create a new object with the required data, we could have used the default init function, but it will be easier for us to define our own.

Now we will actually have to implement the Animal Object, open up the “Animal.m” file and edit its contents to look like below:

#import "Animal.h"

@implementation Animal
@synthesize name, description, imageURL;

-(id)initWithName:(NSString *)n description:(NSString *)d url:(NSString *)u {
	self.name = n;
	self.description = d;
	self.imageURL = u;
	return self;
}
@end

The above code should be pretty easy to read as well, it basically stores the supplied data from the initWithName function and return the object (self).

Now its time to setup the Application delegate to access the database.

Open up the “SQLiteTutorialAppDelegate.h” and edit its contents to look like below:

#import <UIKit/UIKit.h>
#import <sqlite3.h> // Import the SQLite database framework

@interface SQLiteTutorialAppDelegate : NSObject  {

    UIWindow *window;
    UINavigationController *navigationController;

	// Database variables
	NSString *databaseName;
	NSString *databasePath;

	// Array to store the animal objects
	NSMutableArray *animals;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
@property (nonatomic, retain) NSMutableArray *animals;

@end

What we are doing here is importing the SQLite database framework and creating some variables for storing the database details and an array of animal objects.

Now open up the “SQLiteTutorialAppDelegate.m” file and edit its contents to look like below:

#import "SQLiteTutorialAppDelegate.h"
#import "RootViewController.h"
#import "Animal.h" // Import the animal object header

@implementation SQLiteTutorialAppDelegate

@synthesize window;
@synthesize navigationController;
@synthesize animals; // Synthesize the aminals array

- (void)applicationDidFinishLaunching:(UIApplication *)application {
	// Setup some globals
	databaseName = @"AnimalDatabase.sql";

	// Get the path to the documents directory and append the databaseName
	NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
	NSString *documentsDir = [documentPaths objectAtIndex:0];
	databasePath = [documentsDir stringByAppendingPathComponent:databaseName];

	// Execute the "checkAndCreateDatabase" function
	[self checkAndCreateDatabase];

	// Query the database for all animal records and construct the "animals" array
	[self readAnimalsFromDatabase];

	// Configure and show the window
	[window addSubview:[navigationController view]];
	[window makeKeyAndVisible];
}

- (void)applicationWillTerminate:(UIApplication *)application {
	// Save data if appropriate
}

- (void)dealloc {
	[animals release];
	[navigationController release];
	[window release];
	[super dealloc];
}

-(void) checkAndCreateDatabase{
	// Check if the SQL database has already been saved to the users phone, if not then copy it over
	BOOL success;

	// Create a FileManager object, we will use this to check the status
	// of the database and to copy it over if required
	NSFileManager *fileManager = [NSFileManager defaultManager];

	// Check if the database has already been created in the users filesystem
	success = [fileManager fileExistsAtPath:databasePath];

	// If the database already exists then return without doing anything
	if(success) return;

	// If not then proceed to copy the database from the application to the users filesystem

	// Get the path to the database in the application package
	NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseName];

	// Copy the database from the package to the users filesystem
	[fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];

	[fileManager release];
}

-(void) readAnimalsFromDatabase {
	// Setup the database object
	sqlite3 *database;

	// Init the animals Array
	animals = [[NSMutableArray alloc] init];

	// Open the database from the users filessytem
	if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
		// Setup the SQL Statement and compile it for faster access
		const char *sqlStatement = "select * from animals";
		sqlite3_stmt *compiledStatement;
		if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
			// Loop through the results and add them to the feeds array
			while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
				// Read the data from the result row
				NSString *aName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
				NSString *aDescription = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 2)];
				NSString *aImageUrl = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 3)];

				// Create a new animal object with the data from the database
				Animal *animal = [[Animal alloc] initWithName:aName description:aDescription url:aImageUrl];

				// Add the animal object to the animals Array
				[animals addObject:animal];

				[animal release];
			}
		}
		// Release the compiled statement from memory
		sqlite3_finalize(compiledStatement);

	}
	sqlite3_close(database);

}

@end

Now I know that may look like a fair bit of code and it probably also looks quite scary! But really it is quite simple and I have tried to comment nearly every line to describe to you what the line does and why it is there.

The checkAndCreateDatabase function checks to see if we have already copied our database from the application bundle to the users filesystem (in their documents folder), if the database hasn’t already been created or it has been removed for some reason it will be recreated from the default database.

Next the readAnimalsFromDatabase function will make a connection to the database that is stored in the users documents folder, and then executes the SQL statement “SELECT * FROM animals”. It will then go through each row that is returned and it will extract the name, description and imageURL from the result and build an Animal object for each. You will see the “sqlite3_column_text” function used here, there are many more of these for returning other field types such as “sqlite3_column_int” for integers, “sqlite3_column_blob” for blobs or “sqlite3_column_value” to get an unknown value.

Now that we have the data in our array and we have it in our known format we are ready to start displaying it.

Open up the “RootViewController.m” file and edit the numberOfRowsInSection to look like the following:

SQLiteTutorialAppDelegate *appDelegate = (SQLiteTutorialAppDelegate *)[[UIApplication sharedApplication] delegate];
return appDelegate.animals.count;

What this does is it creates a link to the application delegate, and then the second line returns the size f the animals array in out Application delegate, this array was filled previously from the SQLite database.

Now in the cellForRowAtIndexPath function you will need at change it to look like the following:

- (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];
    }

    // Set up the cell
    SQLiteTutorialAppDelegate *appDelegate = (SQLiteTutorialAppDelegate *)[[UIApplication sharedApplication] delegate];
	Animal *animal = (Animal *)[appDelegate.animals objectAtIndex:indexPath.row];

	[cell setText:animal.name];
	return cell;
}

We pretty much just added 3 lines under the “// Set up the cell” line, the first one is the same as we added previously to access the application delegate. The second line creates a new Animal object based on the array from the application delegate, it will be used to create a row for each individual record in the database. On the final line we are just setting the text of the cell to the name field from the Animal object.

You can now run the program and you should see a table view with the 4 animals we added to the database, if you added more than my default animals you should see them in here as well.

We will now setup the AnimalViewController, open up the “AnimalViewController.h” file and edit its contents to below:

#import <UIKit/UIKit.h>

@interface AnimalViewController : UIViewController {
	IBOutlet UITextView *animalDesciption;
	IBOutlet UIImageView *animalImage;
}

@property (nonatomic, retain) IBOutlet UITextView *animalDesciption;
@property (nonatomic, retain) IBOutlet UIImageView *animalImage;

@end

What we are doing above is adding an outlet for the description and image for the Animal, we will use these later on when we link the view up.

Now open up the “AnimalViewController.m” file and add a synthesize call for for the description and image, this will go under the “@implementation AnimalViewController” line, like so:

#import "AnimalViewController.h"

@implementation AnimalViewController

@synthesize animalDesciption, animalImage;

Now it is time to make the detailed view page appear when you select a record. Open up the “AnimalViewController.xib” file from the resources folder and the interface builder should appear.

The first thing we need to do is to set the File’s Owner Class to AnimalViewController, this is done by selecting the “File’s Owner” item in the main window and then clicking Tools > Identity Inspector in the top menu, and then selecting AnimalViewController from the class dropdown.

Your inspector window should now look like this:

We are going to be using a UITextView for the description (as it will allow for word wrapping and scrolling in the case that the description is quite large) and a UIImageView to display the image. I have laid mine out like below:

Now that we have everything laid out it is time to link them all up, start by holding control and click+drag from the “File’s Owner” to the “View” objects, a little gray menu will appear and you will need to select view. Now hold control and click+drag from the “File’s Owner” to the UITextView in the layout window, you should see “animalDescription” in the popup list, select it. Repeat this process for the UIImageView and you should see animalImage appear, select it also.

Now save the interface and close the interface builder.

Nearly done! All we have to do now is to setup the code for when a user presses on a record in the table view.

Open up the “RootViewController.h” file and edit its contents to below:

#import <UIKit/UIKit.h>
#import "AnimalViewController.h"

@interface RootViewController : UITableViewController {
	AnimalViewController *animalView;
}

@property(nonatomic, retain) AnimalViewController *animalView; 

@end

We are creating an instance of the AnimalViewController to be used bu the RootViewController when a user presses on an item.

Now open up the “RootViewController.m” file and edit the top part of the file to look like below:

#import "RootViewController.h"
#import "SQLiteTutorialAppDelegate.h"
#import "Animal.h"

@implementation RootViewController
@synthesize animalView;

This will just synthesize the animalView that we just added.

First up lets set the default title of our view, to do this you need to uncomment the viewDidLoad function, and edit it to below:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Uncomment the following line to add the Edit button to the navigation bar.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;

	self.title = @"My Zoo";
}

We also need to edit the didSelectRowAtIndexPath

function in this file, edit it to look like below:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // Navigation logic -- create and push a new view controller
	SQLiteTutorialAppDelegate *appDelegate = (SQLiteTutorialAppDelegate *)[[UIApplication sharedApplication] delegate];
	Animal *animal = (Animal *)[appDelegate.animals objectAtIndex:indexPath.row];

	if(self.animalView == nil) {
		AnimalViewController *viewController = [[AnimalViewController alloc] initWithNibName:@"AnimalViewController" bundle:nil];
		self.animalView = viewController;
		[viewController release];
	}

	// Setup the animation
	[self.navigationController pushViewController:self.animalView animated:YES];
	// Set the title of the view to the animal's name
	self.animalView.title = [animal name];
	// Set the description field to the animals description
	[self.animalView.animalDesciption setText:[animal description]];
	// Load the animals image into a NSData boject and then assign it to the UIImageView
	NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[animal imageURL]]];
	UIImage *animalImage = [[UIImage alloc] initWithData:imageData cache:YES];
	self.animalView.animalImage.image = animalImage;

}

What we are doing here is checking to see if the animalView object has already been created, if not then create it.

The next few lines are used to setup the animation (slide from right to left) and to set the actual data fields to those of the selected animal.

Now you should be ready to fire up the application and see it in all its glory.

You should see your windows looking like below.
 

5. Project Files

Here are the source files for this project: Download the project source files

If you have any questions or comments please feel free to post them here and I will try to get back to you ASAP. Also keep your eye out for my second part to the advanced RSS Reader tutorial, it will be using TouchXML, SQLite, UIWebview and more!

Popularity: 100% [?]

Comments

There are 162 comments for this post.

  1. Ian lim on September 22, 2008 10:26 am

    Hi, Dean,

    Really appreciate your effort. I gain a lot of knowledge and techniques from your tutorial. Really helpful. Hopefully you will continue with some new tutorials so i can learn much more from you.
    Thanks.

    Best regards,
    Boon Huang

  2. Dom on September 22, 2008 11:59 pm

    I’ve found your tutorials most helpful.
    Looking forward to Part II of the advanced RSS reader article.

  3. Michael on September 24, 2008 6:40 am

    I am getting so many errors its crazy. Would it be possible to post the finished project file?

  4. mek on September 24, 2008 7:44 pm

    on all your import#’s you dont specify a filename,

    im just trying to debug 14 errors in trying to get this to run

    is it possible to post the project file with the source code, like other tutorials,

    otherwise, amazing tutorial, it is exactly what i am trying to learn

    thank you for creating it

  5. Dean on September 24, 2008 9:50 pm

    Hey Michael & mek,

    Sorry about that, wordpress had filtered out the imports as they were surrounded with < and > signs. I fixed it up and all the imports are there now, and without them it would have thrown alot of errors.

    Have a go now and let me know how you go.

    I am also looking at posting the source files for my tutorials.

    Good Luck,
    -Dean

  6. mek on September 25, 2008 12:24 am

    this might be material for another tutorial, but how would you search this database if you had populated many more animals,

    would something like this work?

    https://developer.apple.com/iphone/library/samplecode/TableSearch/index.html

  7. mek on September 25, 2008 12:32 am

    in the didSelectRowAtIndexPath in the RootViewController.m
    i am having two syntax errors

    self.animalView.title = [animal name];
    error: syntax error before ‘]’ token

    &

    // Set the description field to the animals description
    [self.animalView.animalDesciption setText:[animal description]];
    error: syntax error before ’setText’

    why does the self.animalview.animaldescription have []’s and theself.animalView.title does not, i tried adding []’s but i get a series of other errors

    thanks for your help

  8. Dean on September 25, 2008 5:44 am

    Hey Mek,
    That code should be fine, the reason there is no square brackets around the self.animalView.title command is because we are setting the value of a property (as in it has been @synthesized. Whereas for the description we are calling the “setText” function on the animalDescription UITextView object.

    The only reason I can think you are getting errors is because you may not have created the animal object correctly?

    Can you post your whole didSelectRowAtIndexPath function?

    -Dean

  9. Markus on September 27, 2008 3:45 pm

    Something is wrong. I dont recieve errors or warnings. The animal objects were created but the tableview on the RootViewController does not show any animal. What could be wrong?
    thanks for help.

  10. omar on September 27, 2008 9:40 pm

    worked like a champ… very informative tutorial and for a .NET developer a great start to learning iPhone development. Thanks!

  11. mek on September 28, 2008 11:44 pm

    its the same,:

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // Navigation logic — create and push a new view controller
    SQLiteTutorialAppDelegate *appDelegate = (SQLiteTutorialAppDelegate *)[[UIApplication sharedApplication] delegate];
    Animal *animal = (Animal *)[appDelegate.animals objectAtIndex:indexPath.row];

    if(self.animalView == nil) {
    AnimalViewController *viewController = [[AnimalViewController alloc] initWithNibName:@”AnimalViewController” bundle:nil];
    self.animalView = viewController;
    [viewController release];
    }

    // Setup the animation
    [self.navigationController pushViewController:self.animalView animated:YES];
    // Set the title of the view to the animal’s name
    self.animalView.title = [animal name];
    // Set the description field to the animals description
    [self.animalView.animalDesciption setText:[animal description]];
    // Load the animals image into a NSData boject and then assign it to the UIImageView
    NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[animal imageURL]]];
    UIImage *animalImage = [[UIImage alloc] initWithData:imageData cache:YES];
    self.animalView.animalImage.image = animalImage;

    }

    i did the project over again from scratch, cutting and pasting everything, one thing i did notice is when you get to this point,

    “Now that we have everything laid out it is time to link them all up, start by holding control and click+drag from the “File’s Owner” to the “View” objects, a little gray menu will appear and you will need to select view. Now hold control and click+drag from the “File’s Owner” to the UITextView in the layout window, you should see “animalDescription” in the popup list, select it. Repeat this process for the UIImageView and you should see animalImage appear, select it also.”

    the animalImage and animalDescription names of the UItextview and UIimageView do not appear when you drag the files owner over to them, until you type in the code that follows that instruction in the RootViewControllers

    I still after a few attempts have not been able to get this running, the closest i got was what Markus was reporting

    but I def. am learning, so thank you for this

  12. ADRS on September 29, 2008 4:15 pm

    Hi! Thanks for the tutorial it works fine but i have a issue:
    In this case, when i add data to the DB through the shell and then “run and debug” the project, data that appears is not updated. Data just become updated when I delete the executable from the emulator and then “run and debug”… What can I do to fix that?

    Thanks :)

  13. Dean on September 30, 2008 12:57 am

    Howdy,
    I have just uploaded the source files for this project, you can grab them at the bottom of the tutorial.

    @Markus: If there are no errors and the objects are being created than the only reason they wouldn’t show is if they weren’t linked correctly in Interface Builder. I just followed the project to the tee to create the project download files and everything went to plan. Possibly try to download my source files and compare them to ensure that everything is the same.

    @omar: Glad to hear that it helped and that you are picking it up, it is really quite interesting when you get into it.

    @mek: The links should come up as long as you have set the view class to “AnimalViewController” and that you make sure that the files are saved back in XCode before launching Interface Builder. Those links have nothing to do with the RootViewController so that may just be a coincidence. Try grabbing my source files from the bottom of the tutorial and see how they compare to yours.

    @ARDS: No problems, the reason that is happening is because we copy the database over to the users local file system if it doesn’t exist, so once the program has been run once the file will not be read again from the bundle until it is deleted. You could edit the “checkAndCreateDatabase” function to remove the existing database and copy the most recent one over every time, this would be fine for a read only database from the users end, but if they can edit the contents in the application it would be lost every time you launch the application.

    Thanks for the feedback everyone and I hope that you can all get it to work soon!

    -Dean

  14. ADRS on September 30, 2008 9:49 am

    Oh thanks for the explanation, it really helped me to understand what’s happening with the DB. So, it doesn’t make sense to remove the DB every time because, if we want to insert or remove data from de DB, we will do that with the DB copied to the file system. Is that right?

    Thanks a lot!!!

  15. Dean on September 30, 2008 10:30 am

    Thats right, the reason we copy the database to the users documents folder is because it is backed up every time the phone is sync’d with iTunes. There can also be problems with editing files outside the documents folder due to the security permissions on the iPhone.

    -Dean

  16. Markus on September 30, 2008 11:54 am

    Thanks Dean. I found the problem. With copy & past I left a return 0; in the source code and the program did nothing.

    I had the same problem like mek. I could not connect the “File’s Owner” to the “View” objects because the header file was not saved.
    Now I corrected all problems and the program runs.

    I added some views and tried to reuse the databasePath pointer from
    SQLiteTutorialAppDelegate *appDelegate = (SQLiteTutorialAppDelegate *)[[UIApplication sharedApplication] delegate];
    I can find the databaseName pointer with correct content, but the databasePath points to nothing. Do you have an idea?

    Markus

  17. mek on October 2, 2008 2:48 am

    this is exciting, i finally got it working

    do you think, in another tutorial, or even a mini one to build off this, you could explain how to sort the original table, like if you have a lot of animals in the database…have header files like,

    Reptiles
    Snake
    Turtle
    Mammals
    Rabbits
    Dog
    Cat
    Birds
    Dove

    and the also how to Search the table?

    thank you so much for everything

  18. mek on October 2, 2008 3:04 am

    that didn’t really come out…

    i meant like
    Reptiles
    –Snake
    –Turtle
    Mammals
    –Rabbits
    –Dog
    –Cat
    Birds
    –Dove

  19. Dean on October 2, 2008 3:16 am

    Hey Mek,
    I will have a look into it and write up a tutorial, shouldn’t be too hard.

    -Dean

  20. palo on October 2, 2008 12:11 pm

    Hi , nice tutorial, but i found little missing part in your “checkAndCreateDatabase”, its really a little thing. You are releasing NSFileManager only if there is no database file. So if your file exists there will be a memmory leak. But its just a noncritical part.

  21. Bob Schoenburg on October 4, 2008 9:04 pm

    How are you able to use the

    checkAndCreateDatabase

    and

    readAnimalsFromDatabase

    methods without declaring them as (Private) or in the header?

  22. JDW on October 8, 2008 5:49 pm

    Thanks for the tutorial! Quick question though. Is it possible to have the imageview continuously refresh (a still image coming from a web cam)? MJPG won’t work with the imageview class.

    Thanks,
    JDW

  23. carole on October 9, 2008 4:14 am

    I went through the entire tutorial… and thought it wasn’t working at first, because I was having the same results as Markus where nothing was displaying…. but then I finally figured out that is isn’t loading the .sql file in my project directory…. instead it was creating a blank database in ‘/User/MyAccount/Library/Application Support/iPhone Simulator/User/Applications/2FB8C50D-3BE2-44F9-BC63-BFA18D6BC423/Documents’

    Any idea why it’s looking there for the database instead of in my project folder? I’m assuming it’s a project setting somewhere… but this is my first app.. so I thought I’d ask before spending hours looking for it.

  24. mek on October 9, 2008 7:49 pm

    so i’ve been playing around for a week or so, getting this animal sqlite3 code interfaced with a sort by Name of animal much like the elemets example, specifically in the ElementsSortedByNameDataSource.m code

    https://developer.apple.com/iphone/library/samplecode/TheElements/index.html

    but im not having the greatest of luck

  25. Dean on October 9, 2008 10:34 pm

    Howdy,
    @Bob: They can be called from the same class but anything external will not be able to access them.

    @JDW: You could setup a NSTimer to refresh the image in the UIImageView, just make a new NSTimer object, set the time between ticks and then in the tick function just reload the UIImageView with a new NSURL and it should work.

    @carole: The folder you are seeing is correct, the simulator builds a structure similar to the actual iPhone folder structure when simulating applications. This shows that every iPhone app has its own Documents folder for security reasons. When you first launch the app it should run the “checkAndCreateDatabase” which will copy the database to the specified Documents folder. You may have to delete the app from the simulator and then rebuild it if you have changed the DB.

    @mek: I will start to have a play with the sorting function when I get a spare minute. You may be able to put an ORDER BY in the query to get the results pre-sorted, but if you want to change the sorting in real time in the app you will need to use the iPhone functions.

    -Dean

  26. mek on October 9, 2008 11:21 pm

    thanks Dean for responding

    i wonder if it is possible to place that sql command in a buttom command/action

    so that when you click on it, it executes a Order by sql command?

    i like in the elements example, the animated section header [a, b, c, d, etc] so when you are mid swipe, browsing “e” for example, it shows you at the top what section you are in

    im going to play around with it a bit

    thanks for everything man

  27. Bob Schoenburg on October 10, 2008 12:16 am

    I got an error since the methods were not declared, but the program worked! Thanks, great tutorial.

    Now, I have a default.png that displays for less than 1 second. I would like to put in a short delay of about 2-3 seconds to keep the the default.png on long enough to read.

  28. Willo on October 10, 2008 4:38 am

    Thanks for the Tutorial, execellent for someone just starting out on OSX and iPhone dev. I am not getting any results when my project builds and runs. I do not get any errors or warnings when compiling and have followed your directions to the point and even downloaded your source code and replaced so the files are exactly the same. But still my app is not displaying any records shown.

    Any assistance would be greatly appreciated.

  29. Willo on October 10, 2008 4:41 am

    Whoops scratch that, the hint to @carole fixed my problem :)

  30. Jim on October 11, 2008 8:35 pm

    Great tutorial – I have one problem though.

    I tried recreating the project for a more complex database containing recipes. I am getting the errors below. Any ideas what I did wrong?

    “_sqlite3_step”, referenced from:
    -[SQLiteCookbookAppDelegate readRecipesDatabase] in SQLiteCookbookAppDelegate.o
    “_sqlite3_prepare_v2″, referenced from:
    -[SQLiteCookbookAppDelegate readRecipesDatabase] in SQLiteCookbookAppDelegate.o
    “_sqlite3_finalize”, referenced from:
    -[SQLiteCookbookAppDelegate readRecipesDatabase] in SQLiteCookbookAppDelegate.o
    “_sqlite3_column_text”, referenced from:
    -[SQLiteCookbookAppDelegate readRecipesDatabase] in SQLiteCookbookAppDelegate.o
    -[SQLiteCookbookAppDelegate readRecipesDatabase] in SQLiteCookbookAppDelegate.o
    -[SQLiteCookbookAppDelegate readRecipesDatabase] in SQLiteCookbookAppDelegate.o
    -[SQLiteCookbookAppDelegate readRecipesDatabase] in SQLiteCookbookAppDelegate.o
    -[SQLiteCookbookAppDelegate readRecipesDatabase] in SQLiteCookbookAppDelegate.o
    -[SQLiteCookbookAppDelegate readRecipesDatabase] in SQLiteCookbookAppDelegate.o
    -[SQLiteCookbookAppDelegate readRecipesDatabase] in SQLiteCookbookAppDelegate.o
    “_sqlite3_open”, referenced from:
    -[SQLiteCookbookAppDelegate readRecipesDatabase] in SQLiteCookbookAppDelegate.o
    “_sqlite3_close”, referenced from:
    -[SQLiteCookbookAppDelegate readRecipesDatabase] in SQLiteCookbookAppDelegate.o
    ld: symbol(s) not found
    collect2: ld returned 1 exit status

    Thanks
    Jim

  31. TIM on October 13, 2008 3:48 pm

    In the code above for the animalViewController.h and .m files, “animalDescription” is misspelled as “animalDesciption”.

    So be careful copying and pasting here!

  32. Stuart on October 13, 2008 8:39 pm

    HI there,
    Awesome tutorial, thanks! I’m attempting to implement it but slightly different to my specific needs. I’ve made a table called tasks which has id, name, creationdate (integer, varchar, varchar respectively).

    In the readTasksFromDatabase method sqlite3_open get the SQLITE_OK fine, however sqlite3_prepare_v2 does not, it returns 1.. which is SQLITE_ERROR I think. I’m not quite sure why this could be. Is there a common cause?

    This is my code here, the sql statement works fine with sqlite3 at the terminal:

    //Open the databse from the user’s file system
    if (sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK){
    printf(“\ndatabase opened ok”);
    //Setup the sql statement and compile it for faster access
    const char *sqlStatement = “SELECT * FROM tasks;”;
    sqlite3_stmt *compiledStatement;

    // printf(“%d”,sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL));
    if (sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK){
    printf(“\ndb statement prepared OK”);

    Thanks very much!

  33. Stuart on October 13, 2008 9:21 pm

    A little further investigation has told me that it’s not a problem with the sql or db, but with the actual .sql file… it is empty! I’m now trying to find out why. In the project directory, the one that I work on has stuff in, but the ones at databasePath and databasePathFromApp are completely empty!

    For others’ reference, this is a very handy line:

    printf(sqlite3_errmsg(database));

  34. Stuart on October 13, 2008 9:25 pm

    OK, I have no idea why, but changing the version from 2.0 to 2.1 and back again worked. I’m gonna stop commenting now! Sorry!

  35. Brian on October 15, 2008 8:39 pm

    For a Part 2. could you show us how to add animals from the gui (an “Add” button up top). I have been trying to do it for a week now with no success.

    thanks,
    brian

  36. Werner on October 16, 2008 2:09 pm

    Hi there,

    I’m a webdesigner who’s interested in making a native app for the iPhone.
    I’m new to the Apple-environment, so I have some questions.
    - this SQLite DB, where is it stored? On the iPhone or on a web-server
    - can data be sent to and retrieved from online DB’s with the iPhone (like forms and queries on a website)
    - can you use PHP/MySQL within the iPhone world

    Thanks for any feedback.

    Werner

  37. janders on October 31, 2008 12:31 am

    Great tutorial, thanks. Quick comment… Right after editing RootViewController.m by editing numberOfRowsInSection and then editing cellForRowAtIndexPath you suggest that the user run the program for the first time. Actually, they need to first import Animal.h into RootViewController.m. Otherwise they will get errors.

  38. Ibolit on November 5, 2008 11:58 am

    2: Werner
    Hi there,

    I’m a webdesigner who’s interested in making a native app for the iPhone.
    > – this SQLite DB, where is it stored? On the iPhone or on a web-server
    SQLite is stored on the iphone.
    > – can data be sent to and retrieved from online DB’s with the iPhone (like forms and queries on a website)
    Yes, it can.
    > – can you use PHP/MySQL within the iPhone world
    > – no, you can’t. At least on “Unjailbroken” iphones, for i think i saw some ports of php to the iPhone, but i haven’t seen mysql yet (and I don’t think it is necessary). In order to be able to store data from the iPhone on a web-server, you have to write a client on your server that will be able to receive and handle the requests sent by an iPhone…

    > Thanks for any feedback.
    You are wellcome :)

  39. Ibolit on November 5, 2008 12:20 pm

    By the way, i’ve got another question: is it really unnecessary to release the filemanager in case of failure in the following method:
    -(void) checkAndCreateDatabase{
    BOOL success;
    NSFileManager *fileManager = [NSFileManager defaultManager];
    success = [fileManager fileExistsAtPath:databasePath];

    // If the database already exists then return without doing anything
    if(success) return; // Here. Isn’t it better to say : if (success) {[fileManager release]; return;}

    NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseName];
    [fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
    [fileManager release];
    }

  40. Marc Collet on November 5, 2008 3:19 pm

    Hi folks,

    As a newby on the Objective-C language and as a routined programmer for C#.NET I was in search for a more wrapped way of handling SQLite DB Access. For interested people…I combined the above tutorial with the easyness of the FMDB cocoa wrappers. This brakes the above code down to no more then 10 codelines to do the same thing but then without any quite low-level coding.

    So if you are interested I’m more then willing to share. Just let me know ok? you can contact me anytime at marc@collet.nl

  41. icoco on November 6, 2008 8:17 am

    very good ,

    if we can find a db framework for this purpose ? like Hibernate in Java Language .

  42. nic on November 7, 2008 2:33 pm

    Thanks for this. I was working on a project an got completely stuck

    1/ point if your database has structure problem and you make changes to it, after the first compile, you must reset the contents and settings of the simulator to purge the copy that checkAndCreateDatabase leaves behind… other wise it will never copy the new copy to the docs folder

  43. Wolfgang on November 7, 2008 8:05 pm

    Hello Dean,

    thank you very much for your excellent work! Very appreciated! You solved all my problems with my current iPhone project. I’m coding for fun – but I plan to put the finished project into the App-Store. I had problems with SQLite, and I was wondering where I should put the logic for different views, subviews, etc.
    You also solved some design problems I was thinking about. My todo list is much shorter now. This tutorial explained all that in a way which enables me to proceed with my work. You saved a lot of my life time.

    Thank you very much,

    Wolfgang.

  44. Jacko331 on November 10, 2008 12:06 pm

    To fix the 3 yellow code errors you receive… add the following to the SQLiteTutorialAppDelegate.m file under the // Import the animation object header.

    @interface SQLiteTutorialAppDelegate(Private)
    - (void)checkAndCreateDatabase;
    - (void)readAnimalsFromDatabase;
    @end

    Hope this helps not an expert but picking things up as I go and this tutorial was a big help.

    jacko

  45. Wolfgang on November 15, 2008 12:14 am

    I successfully applied the knowledge I learned from your tutorial. Now I have a problem which looks a bit complicated. Can anybody help me here?

    The following statement is used in the tutorial:

    const char *sqlStatement = “select * from table”;

    I need to create a query dynamically – and I tried the following:

    tablename = @”testtable”;
    prefix = [NSString stringWithFormat:@"select * from %@", testtable];

    and then:

    const char *sqlStatement = prefix;

    Unfortunately this produces the following warning and the statement is not working:

    “initialization from incompatible pointer type”

    Can anybody tell me how to fix this? My guess is that it has something to do with the C-Api – and I’m using Objectiv-C methods. I have no idea how to create a C-String from a Objective-C string object…

    (and I’m not even sure that my guess is correct…)

    Thanks in advance for your help,

    Wolfgang

  46. Michael on November 20, 2008 8:39 pm

    I get here:

    “You can now run the program and you should see a table view with the 4 animals we added to the database, if you added more than my default animals you should see them in here as well.”

    and it compiles but I see nothing in the Simulator’s screen?

    I do get 2 warnings: “‘SQLiteTutorialAppDelegate’ may not respond to ‘-checkAndCreateDatabase’ (Messages without a matching method signature will be assumed to return ‘id’ and accept ‘…’ as arguments.)”

    and “‘SQLiteTutorialAppDelegate’ may not respond to ‘-readAnimalsFromDatabase’”

    ALSO, in order to get it to compile i had to add “#import “Animal.h” to SQLiteTutorialAppDelegate. I didn’t notice you mention it before you claim that we should be able to compile & see a list of our animals…

    I’ll continue with tutorial and see if it comes together later… until then…Any ideas? or am I just not following instructions?!?

  47. Hazimj on November 22, 2008 6:10 pm

    Hi, Dean. I’ve been wondering, how do you add a search bar so I can filter the things I type, and add a “Done” button to close when I am done? Thanks in advance!

    -Hazim

  48. Gopi on November 24, 2008 12:51 pm

    Hi Jim,

    You must be insert the Framework for libsqlite3.0.dylib

    To Add-> Existing Framewoks..–>lib–>SDKs–>IphoneOS2.0.sdk–>usr–>lib–>libsqlite3.0.dylib

  49. gopi on November 24, 2008 12:55 pm

    Hi ,

    Just try it..definitely you get the answer..

    Bye.

  50. Wolfgang on November 24, 2008 7:19 pm

    Problem solved: it must be a UTF8 string…

  51. Dmitry on November 24, 2008 7:47 pm

    Hi! I started make app with sqlite3 and just added link to the sqlite lib and build app. and I have got the error ” …. file is not required architecture ‘. I tryed delete link on sqlite3, but i have the same error. Please help!!!

  52. Edward Benson on November 25, 2008 4:29 am

    Fantastic tutorial. Thanks a lot!

  53. Tom on November 27, 2008 8:10 pm

    Thanks very much for a great tutorial — it really helps a lot.

    I’m wondering what the best practice is for placing your db code as your app becomes more complex. I’d imagine the app delegate would get really huge with a half dozen or more tables, each with CRUD methods.

    Do you move the db code out into the Animal object itself? Or just create a helper class? Is there any sort of best practice way of approaching that?

    Thanks!
    Tom

  54. Stephen J on December 7, 2008 4:40 am

    Hi Dean – Great tutorial. It’s comprehensive and easy to understand. Thanks for posting it.

    I have one question: How can I use my own images in the database? I can import them into the project, but how do I tell the database where to find them.

    Regards – Stephen

  55. Stephen J on December 7, 2008 8:33 pm

    Thank you for the great tutorial. I am new to Xcode (I am also now to Mac, I got my first Apple a couple of months ago and am loving it) and I found your tutorial to be well written and easy to understand.

    I have a question – How do I use my own images instead of using an URL. I can add extra animal objects using Firefox SQLite Manager, but if I attach an image I get an error message about NSUrls. Any help would be greatly appreciated.

    Thanks-Stephen

  56. Virgil on December 13, 2008 2:01 pm

    Great tutorial! Just have one question. I would like to have the animal images local to the project. Can they be loaded from the sql database or would it be better to store them in an Image folder and load them there. Can you give me an example of how this would be coded in your example.

    Thanks,

    Virgil

  57. Mike on December 17, 2008 6:00 am

    I am going through the tutorial and I get errors here:

    In the file you have in your tutorial prefaced with:
    “Now in the cellForRowAtIndexPath function you will need at change it to look like the following:”

    // Set up the cell
    SQLiteTutorialAppDelegate *appDelegate = (SQLiteTutorialAppDelegate *)[[UIApplication sharedApplication] delegate];
    Animal *animal = (Animal *)[appDelegate.animals objectAtIndex:indexPath.row];

    Specifically the last line: Animal *animal = (Animal *)…..

    Here are the errors:

    x error: ‘Animal’ undeclared (first use in this function)
    x error: ‘animal’ undeclared (first use in this function)
    x error: syntax error before ‘)’ token
    x error: syntax error before ‘objectAtIndex’

    They are all at the same line

    Please help ASAP

  58. Mike on December 17, 2008 6:05 am

    I added

    #import “Animal.h”

    to the top of

    RootViewController.m

    and it compiled

  59. Howard on December 22, 2008 8:12 pm

    Nice tutorial! One question (from a relative noobie): Doesn’t the Animal object require a dealloc() method for the NSStrings declared there?
    Howard

  60. LB on December 24, 2008 10:20 pm

    Thanks for the great tutorial! I was trying to add a url field so that you could click on the link and go to a website for more information. I have tried everything. Do you have any suggestions on how to add that feature?

    Thanks and Merry Christmas…

  61. Anant on December 27, 2008 2:33 am

    It is really a very good tutorial.

    I am developing an iphone application for a “FAQ” in which I am required to store data on to a company server and even fetch it from there if possible. Can you tell me how should I go about it and what can I use?

    I dont think I can use SQLite for that.

  62. Darryl on December 27, 2008 7:38 pm

    Excellent Tutorial thanks for making it available.

  63. Bren on December 28, 2008 9:05 am

    Thanks for the great tutorial.
    I am setting up my app with a list of chemicals. How would I go about adding another row of data FROM the database that would appear next to the chemicals name in the ‘animal view?’ Eg: I would see Hydrogen and next to it in smaller text (and a different colour) an H.
    Thanks

  64. Diego Ramirez on January 3, 2009 1:49 am

    Niceeee Tutorial!!!, i´m triying to add a tab bar navigation to the main view, i´ve tried everything, but when i add the tab bar the Animal Detail view stop working. How could i do it???

    Thanks & Regards

    Diego

  65. Stephen James on January 15, 2009 3:11 am

    Great tute. Informative and easy to follow. One question – how would I store the images locally on the iPhone instead of calling them from the web? Any help would be greatly appreciated. Thanks – Stephen

  66. Raj on January 21, 2009 9:54 am

    Hey excellent tutorial. I have bookmarked it on http://www.iphonekicks.com/database/iPhone_SDK_Tutorial_Reading_data_from_a_SQLite_Database

    Thanks for this :-)

  67. Robert Bolton on January 25, 2009 4:31 pm

    Thank you for putting together this tutorial! I found it to be VERY helpful in understanding the basics to working with SQLite and iPhone development. =)

  68. Kevin R on February 5, 2009 2:04 pm

    +1 on how to do this with local files in the Xcode project instead of using urls. How do you do that?

    Thanks

  69. Kevin R on February 5, 2009 7:27 pm

    I tried to load the images locally. How is this done? I modified:

    // Setup the animation
    [self.navigationController pushViewController:self.animalView animated:YES];

    // Set the title of the view to the animal’s name
    self.animalView.title = [animal name];

    // Set the description field to the animals description
    [self.animalView.animalDesciption setText:[animal description]];

    // Load the animals image into a NSData object and then assign it to the UIImageView
    UIImage *animalImage = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@”elephant” ofType:@”png”]];
    //NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[animal imageURL]]];
    //UIImage *animalImage = [[UIImage alloc] initWithData:imageData cache:YES];
    self.animalView.animalImage.image = animalImage;

    See where I have “elephant” ofType:@”png”? That works, but it puts an elephant in every view. What do I have to put there to load an image that has the same name as the animal UI picked? I have “monkey.png”, “elephant.png” etc in the xcode project.

  70. Brian on February 10, 2009 8:54 pm

    I’m getting an error when selecting an item… it does not load up the view for the description and fails with:

    ___TERMINATING_DUE_TO_UNCAUGHT_EXCEPTION___

    When I comment out the line:

    [self.navigationController pushViewController:self.syntaxView animated:YES];

    I don’t get that error anymore… but of course it doesn’t load my description. :)

    Any ideas?

  71. Brian on February 10, 2009 9:23 pm

    By the way… I changed animalView to syntaxView incorporating the same methods on a different app. Everything else seems to be working fine though.

  72. Brian on February 11, 2009 2:19 am

    Nevermind… :) I figured it out. When I cleaned up the code, the view controller was pointing to animalDesciption and when I changed that to animalDesc[R]iption in the code, I forgot to change the linkage in IB. :) It works now! Great tutorial!

  73. Brian on February 11, 2009 2:46 am

    So here’s another question… how do I go about changing the color of the list view. I managed to change the background color but there’s no place in IB to change the text color… only on the textView component. Also… I tried changing the titlebar to black but it did nothing but remains blue. :)

    What I want to do is change the background color of both views to black, the text to white and the titlebar to a darker blue.

  74. Brandon on February 12, 2009 4:11 pm

    Thank for the info!
    I am trying to do something similar; however, my table is in alphabetical order in a plist list file. How do I connect this list of names to a detail view with data similar to the details for the zoo app?

  75. Brian on February 12, 2009 9:41 pm

    I’ve gone through these steps several times now and double/triple checked everything. Then I tried it with the source files.

    I get the same problems either way. The program compiles, the simulator launches, the screen says “My Zoo”… and that’s it. It’s blank.

    Could it be version differences of SQLite? or the wrong version of libsqlite3.0.dylib (e.g. 2.0, 2.1, 2.2)?

    (As a side note, before the compilation, in SQLiteTutorialAppDelegate.m, I have to move applicationDidFinishLaunching to the bottom, so it’s below checkAndCreateDatabase and readAnimalsFromDatabase. Otherwise I get a warning that says it might not read those. Maybe that’s something that can normally be ignored. But the program isn’t working for me either way.)

  76. Rick on February 19, 2009 5:18 am

    Great tutorial! Thanks for putting this out there, you’ve helped lots of people like myself…

  77. Doug Butner on February 21, 2009 6:04 am

    Great, exactly what i was looking for!! Im used to MySQL, glad to see it can be implemented just as easily.

    Thanks

  78. Mike on February 24, 2009 6:00 pm

    Great tutorial… I really appreciate when all steps are clearly described and in the right order!

    The only glitch in the instructions for me was the missing import of the Animal.h into the RootViewController.m before the first suggestion to build and run. Later instructions suggest adding this import.

    I had a compiler warning down in didSelectRowAtIndexPath in the RootViewController.m file complaining that it couldn’t find a method for initWithData that included the parameter cache, so, I commented it out and removed the cache and it worked fine:

    //UIImage *animalImage = [[UIImage alloc] initWithData:imageData cache:YES];
    UIImage *animalImage = [[UIImage alloc] initWithData:imageData];

    One question, by UI Guidelines, shouldn’t the main table list have an icon with the > sign or an information sign (i) to indicate that clicking on the icon would take you to a detail view? If so, what has to be changed to get that icon to display?

    Really appreciate the tutorial… really helps me understand several things about app databases.

    Cheers!
    Mike

  79. Mike on February 24, 2009 6:30 pm

    found it… in the RootViewController.m file, down in the cellForRowAtIndexPath method, after initializing a cell instance, set the accessoryType

    cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

  80. Kevin R on March 4, 2009 4:43 am

    Anyone get this to work with local images? Can you link to the source? Thanks

    get me on twitter @RyeMAC3

  81. ravi on March 5, 2009 5:35 am

    Thanks a lot for sucha nice tutorial , learned so much from here !!
    i have some confusion regarding ,

    “create a new folder in iPhone” while running my application;
    so i can store user modify data !!

    since i don’t know the directory structure of iPhone ..

    so please help me regarding this..
    i request other reader as well …

  82. ajay rawat on March 6, 2009 11:05 am

    It’s really very good.

  83. vSalazar on March 6, 2009 11:47 pm

    how can i get more information about that use sqlite and iphone,

    samples basic intuitives and dynamics….

    so a congratulations for this tutorial is great¡¡¡¡

  84. avantime on March 9, 2009 10:48 pm

    Hey great tutorial!!

    But I was wndering (and trying) how could i make a similar thing but with multiple views and a tab bar?

    Each tab woud be associated with a view and each view would show a specific type of data from a database…

    thanks in advance..

  85. Olivia on March 10, 2009 11:07 pm

    Great tutorial. There aren’t many on SQLite databases, so this was awesome.

    I, too, am wanting to load local images, and am interested in learning how.

  86. Ravi on March 12, 2009 5:57 pm

    I am newbie in IPhone programming and in general with Objective-C. I had lots of questions and I got many of them answered here. Nicely written. Great post

  87. Olivia on March 13, 2009 8:54 am

    To all who have wanted to access images locally, I have figured it out!!! It is a simple change:

    CHANGE:

    NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[animal imageURL]]];
    UIImage *animalImage = [[UIImage alloc] initWithData:imageData cache:YES];
    self.animalView.animalImage.image = animalImage;

    TO:

    UIImage *animalImage = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[animal imageURL] ofType:@”jpg”]];

    self.animalView.animalImage.image = animalImage;

    Works if you have the same type “png,” “jpg,” etc. Have not tried with different file types. Hope it helps!

  88. Kevin R on March 14, 2009 1:29 pm

    @Olivia
    Your change…

    UIImage *animalImage = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[animal imageURL] ofType:@”jpg”]];

    self.animalView.animalImage.image = animalImage;

    … isn’t working on mine.

    I have all the local images named exactly how they were in the SQL database but the images just don’t show. Any way you can link to or email me the modified template so I can see exactly how everything came together?

    ryemac3@mac.com

    Thanks

  89. Joe on March 16, 2009 4:36 pm

    Joe, what is your error?

  90. Joe on March 16, 2009 5:30 pm

    Oops, what I meant to say is, what kind of error did you get, “Kevin” :) .

    Keep in mind, you want to remove the extension of the image and leave only the name in your imageURL. ofType will basically append the image type onto the name.

  91. Kevin on March 17, 2009 4:19 pm

    Anyone else get Olivia’s local image change to work? I can’t seem to nail it. Do you also have to change the imageURL in the SQL database to something else? Is she keeping that “NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[animal imageURL]]];” line too?

    ryemac3@mac.com
    twitter @ryemac3

  92. Dean on March 17, 2009 10:46 pm

    Hey Everyone,

    As you may have noticed I am writing a series of tutorials that will encompass SQLite, TouchXML, UIWebView, etc and I have just finished working on the section that will store and retrieve images from a SQLite database and I am in the process of putting it into tutorial form right now. I hope this will help many people that are having trouble with images in a database.

    Cheers,
    Dean

  93. radar on March 19, 2009 11:48 am

    Hi,

    I have the same 2 warning message as Michael

    “‘SQLiteTutorialAppDelegate’ may not respond to ‘-checkAndCreateDatabase’ (Messages without a matching method signature will be assumed to return ‘id’ and accept ‘…’ as arguments.)”

    and

    “‘SQLiteTutorialAppDelegate’ may not respond to ‘-readAnimalsFromDatabase’”

    That makes the program having troubles loading the database content.

    Is there a way to fix this issue?

    Thanks.

  94. Olivia on March 20, 2009 8:31 am

    Kevin,
    As Joe mentioned, in your database under the image column, you want to just have the names of your files without the extensions.

    I did not make any other changes in the code, and do not use the NSData *imageData code at all.

    Did you add all of the images to your Resources folder?

    I am definitely a Noob, but I can help walk you through it if you’re still having problems. E-mail me oliviaiphonedev@gmail.com

  95. Kevin on March 20, 2009 3:20 pm

    @Olivia

    I love you!

    You did it. That was it. I had put “elephant.png” in the SQL database instead of “elephant”. I changed it and it worked!

    Thanks so much!

  96. Olivia on March 20, 2009 8:14 pm

    Glad I could help!

  97. nardev on March 28, 2009 9:14 pm

    Did anyone tell you that you are awesome!!! ;) Thanks for the tut!

  98. nardev on March 28, 2009 9:14 pm

    love the little monkey :)

  99. Mykola Golubyev on March 29, 2009 7:21 pm

    Hi. I didn’t notice, does your Animal calls release on the strings? Is there some memory leak problem?

  100. Stephen on April 4, 2009 7:44 pm

    YAY OLIVIA – Thank you so much. I have ben struggling with this for months. You are the bestest noob developer ever!!!

  101. Dean on April 5, 2009 8:54 am

    Hey all,

    I have just posted another part to the RSS Reader Tutorial Series that goes into setting up a UITabBar and UINavigationBar. The next part is going to go into storing RSS feed URL’s in a SQLite Database and also how to create custom UITableView cells in code to have multiple lines of text and images.

    Please spread the word!

    Cheers,
    Dean

  102. ed on April 6, 2009 6:51 pm

    Hi, I’m getting these warnings too, any suggestions on fixing? thanks!

    “‘SQLiteTutorialAppDelegate’ may not respond to ‘-checkAndCreateDatabase’ (Messages without a matching method signature will be assumed to return ‘id’ and accept ‘…’ as arguments.)”

    and

    “‘SQLiteTutorialAppDelegate’ may not respond to ‘-readAnimalsFromDatabase’”

  103. Olivia on April 8, 2009 6:46 am

    Yay, Stephen! Ok, so I have tackled how to display the images. Does Dean or anyone know how to add Section headers to data from a database? For example, if I want to categorize data into sections like Reptiles, Mammals, etc., how would I do this? Do I need to add the array of animals into a NSDictionary? I have done this for data from a plist, but don’t know what changes I would make for a database.

  104. evosonic on April 10, 2009 4:33 pm

    I am having the same problems as radar and Michael:

    “‘SQLiteTutorialAppDelegate’ may not respond to ‘-checkAndCreateDatabase’ (Messages without a matching method signature will be assumed to return ‘id’ and accept ‘…’ as arguments.)”

    and

    “‘SQLiteTutorialAppDelegate’ may not respond to ‘-readAnimalsFromDatabase’”

    ??????

  105. evosonic on April 10, 2009 4:44 pm

    Problem fixed. Add the two method names to the SQLiteTutorialAppDelegate.h file.

  106. miro on April 14, 2009 1:07 am

    hello
    anybody knows how to:
    1. either ADD another Integer variable
    2. OR change one of the String variable into Integer variable

    … so I can use it when defining different size of the images “height” using UIScrollview in the command to be changed from

    scrollview.contentSize = CGSizeMake(0, 900)

    into

    scrollview.contentSize = CGSizeMake(0, secondvariable)

    Many thanks

  107. Miro on April 23, 2009 2:50 am

    anybody would know how to define variable height in cgsizemake, in case I would like to be loading images with different HEIGHT, and the same width in scrollview?

    something like

    if image01 = xyz.gif, then height = 1000
    if image02 = zzz.gif, then height = 1200

    etc

  108. Jamie on April 24, 2009 10:09 pm

    Hi Dean, great tutorial, this really helped me learn! I have a question though, might be silly, so here it goes:

    After loading and running this application, I made some database updates like adding my own animals, but for some reason they are NOT showing. It’s like the database was cached.

    Please help! Thanks!

  109. Jamie on April 24, 2009 11:52 pm

    To my comment earlier: (for newbies like myself)

    I didn’t realize that sqlite database are copied locally to the phone. So I just deleted the reference and the installed program and reran the application.

  110. Pierre-Marc on May 5, 2009 4:16 am

    I am trying to add more things to the animal view controller for an app i am trying to create but after creating my sql database, and adding the appropriate lines of code all i get is an app that starts and then crashed right away. i have added 4 entries to the AnimalDatabase.sql and so far everything i tried hasnt worked. there are not errors in my code please give me some insight of how to fix this problem. thanks

  111. crgt on May 7, 2009 4:27 am

    Thanks for this. Really, really helpful. Should keep me busy playing with it for awhile.

  112. Eric on May 7, 2009 2:16 pm

    Hey Dean,

    Thanks for your continued efforts in assisting us with our limited understanding. I have reviewed your code but wonder if you could offer some help in simplifying it a bit more for me.

    I would like to use your database of animals but have the animal names simply show up on the center of the screen (like a “hello world”) but one at a time rather than in a list. Then simply swipe to scroll through the database, seeing the names of the animals one at a time.

    I think this would help me understand the basics so I can move forward.

    Thanks again for all of your help – you are appreciated!

  113. meDoiPhone on May 9, 2009 4:43 pm

    Thanks for the tutorial. I have developed in Java, .Net and some other languages and am used to the client server paradigm when it comes to databases. I have a few newbie questions;

    1. Can iphone apps use databases other than SQLlite such as SQL Server or Oracle?

    2. Do commercial iphone apps that use data utilize databases sitting on servers?

    3. I have read that the SQL Lite database is on the iPhone. What if there is a lot of data. Won’t the size of your app grow if the database size grows?

    Thanks

  114. rahulvyas on May 13, 2009 11:21 am

    UIImage *animalImage = [[UIImage alloc] initWithData:imageData cache:YES];

    this line is producing a warning
    warning no ‘-initWithData:cache:’ method found

  115. rahulvyas on May 13, 2009 11:27 am

    i like your tutorial very much.
    could you please further expand it to add a new animal in database from a User Interface.like we click on add button and it shows a UI in which we can insert a new name,description,and address for the image.

    and also how to delete from the databse.
    please i need it very much

  116. Rahul Vyas on May 13, 2009 11:35 am

    and also a request from you..
    please explain this method in more detail
    -(void) readAnimalsFromDatabase

  117. Greg on May 19, 2009 3:28 am

    THANKS Dean! Excellent tutorials.

    THANKS Olivia. Excellent way to access images locally.

    Did you figure out how to categorize data into sections?

    Also, does anyone know how to add additional views to “elephant” and have arrows at the bottom to go forward and reverse?

    Thanks
    Greg

  118. Mike on May 20, 2009 12:09 am

    This is great. Do you have any tutorials on editing fields and adding data to a database from within an app?

  119. karim on May 21, 2009 12:27 pm

    hii i wanna ask if someone hav any idea why when i add a lot of record in my sqlite database for an application of iphone my application become so slow specially when i add almost 2000 records

  120. johnmabassa on May 22, 2009 12:30 pm

    Thanks for this tutorial…. I appreciate your effort.

  121. Andi Stancu on May 28, 2009 6:01 pm

    Hei,

    Great tutorial. Very useful and even more useful since it’s explained everything.

    I have a problems tho, and it’s with about the single line unexplained.

    I fallowed the tutorial until the place where you started creating the tables and all, since I know how to do that my self. Anyway, when I tried to add contents to the cell, I get 4 errors at the line:

    SQLiteTutorialAppDelegate *appDelegate = (SQLiteTutorialAppDelegate *)[[UIApplication sharedApplication] delegate];

    The errors are:

    ‘SQLiteTutorialAppDelegate’ undeclared (first use in this function)
    ‘appDelegate’ undeclared (first use in this function)
    syntax error before ‘)’ token
    syntax error before ‘delegate’

    Please note I manually returned the numbers of row in table in order to simplify debugging.

    Now, I know I did something wrong, but maybe you could give me a hint on what to check for mistakes.

    Thank you,
    Andi

  122. Andi Stancu on May 28, 2009 6:08 pm

    Update: My table is not in the root view controller. I imported AppDelegate.h file in the controller which includes the table. Now it compiles succesfully but it crashes when I click on the button that takes me to the controller that includes the table.

    Thank you.

  123. Urs on May 30, 2009 1:39 pm

    Hi

    Thanks for this tutorial – I can’t get it to work, though. I have to admit, I changed it a little to suit my needs, but all I did was change the array and ivar-names…

    when using this

    // Set up the cell
    SQLiteTutorialAppDelegate *appDelegate = (SQLiteTutorialAppDelegate *)[[UIApplication sharedApplication] delegate];

    places *place = (places *)[appDelegate.arrPlaces objectAtIndex:indexPath.row];

    [cell setText:place.item_name];

    that’s in «- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {»

    The program crashes with this error:

    2009-05-30 15:38:27.515 SQLiteTutorial[2780:20b] *** -[CALayer item_name]: unrecognized selector sent to instance 0×527410
    2009-05-30 15:38:27.520 SQLiteTutorial[2780:20b] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘*** -[CALayer item_name]: unrecognized selector sent to instance 0×527410′

    I tried to google it but didn’t find any solution…

    Any ideas?

  124. Urs on May 30, 2009 3:53 pm

    Never mind – I found out, that the array «arrPlaces» wasn’t filled correctly… Now, I get only one row back from the sql-statement.. but that’s another issue :)

  125. Olivia on May 31, 2009 11:36 am

    @Eric: did you ever find an answer to your question? This is what I would like to do in my app as well.

  126. Andi Stancu on June 3, 2009 6:35 am

    Any hints what I did wrong guys ? It’s the only tutorial that took me this far.

  127. Olivia on June 3, 2009 9:30 pm

    @Andi: Did you check your interface builder connections?

  128. Andi Stancu on June 3, 2009 9:52 pm

    Yea, I think so. I mean, I can add items manually in the table. The error comes up at line:

    SimpleTableAppDelegate *appDelegate = (SimpleTableAppDelegate *)[[UIApplication sharedApplication] delegate];

    Where SimpleTable is the name of my project. (Yea, SimpleTableAppDelegate exists as it is)

    The error is:
    ‘SimpleTableAppDelegate’ undeclared (first use in this function)

  129. Olivia on June 4, 2009 7:34 am

    Are you sure you imported your appDelegate.h file? I just commented out that line of code in my app and I got the same errors you were describing.

  130. Andi Stancu on June 13, 2009 7:44 pm

    Thanks Olivia. Not sure what was wrong. Rewritten the entire code and it works now. Thanks.

  131. Bas Meijer on June 16, 2009 9:48 pm

    .I noticed that you do a lot of work on the AnimalViewController.xib from within the RootViewController.m method:

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

    }

    Which does not seem so MVC/Cocoa/OO-ish. This approach (also shown in Erica Sadun’s Cookbook) might give problems in further development, for instance when you would like to add ‘next’ & ‘previous’ buttons to walk the array to the AnimalViewController, as that object is not “controlling” anything, it does not even know which object is shown.

    My app AudioZoo uses didSelectRowAtIndexPath mostly to set an index pointing to the selected animal in the appdelegate. The didSelectRowAtIndexPath remains very generic and reusable, and does not need to know anything about displaying animals and their sections (or letting them make a lot of noise in my case below). So the AnimalViewController is used to actually control the view(s) in AnimalViewController.xib using the object found in the appDelegate model (NSMutableArray *animals) based on that appDelegate.animalIndex . (I use sections headers too using a similar sectionIndex)

    So the two methods are:

    // this code is in the file RootViewController.m
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // comply to UI guideline
    [tableView deselectRowAtIndexPath:[tableView indexPathForSelectedRow] animated:YES];
    // get access to the data stored in the appDelegate
    AudioZooAppDelegate *appDelegate = (AudioZooAppDelegate *)[[UIApplication sharedApplication] delegate];
    // set the indices to be used later from the code in AnimalViewController.m
    appDelegate.animalIndex = indexPath.row;
    appDelegate.sectionIndex = indexPath.section;

    // push control to the AnimalViewController
    if(self.animalView == nil) {
    AnimalViewController *viewController = [[AnimalViewController alloc] initWithNibName:@”AnimalViewController” bundle:nil];
    self.animalView = viewController;
    [viewController release];
    }
    [self.navigationController pushViewController:self.animalView animated:YES];
    }

    // this code is in the file AnimalViewController.m
    -(IBAction) startPlaying {
    AudioZooAppDelegate *appDelegate = (AudioZooAppDelegate *)[[UIApplication sharedApplication] delegate];

    Animal *a = (Animal *)[[appDelegate.sectionArray objectAtIndex:appDelegate.sectionIndex] objectAtIndex:appDelegate.animalIndex];
    NSError *err = nil;
    AVAudioPlayer *player = [ [ AVAudioPlayer alloc ] initWithContentsOfURL:
    [ NSURL fileURLWithPath: [ [ NSBundle mainBundle ]
    pathForResource: [f name] ofType:@”m4r” inDirectory:@”/” ] ]
    error: &err
    ];
    player.play;
    }

  132. Jesus on June 17, 2009 11:58 am

    I’m having a problem following this tutorial. When I try to build, I received this error:
    “_NSSearchPathForDirectoriesInDomains”, ferenced from:
    -[IArgotWords openDataBase] in IArgotWords.o
    symbol(s) not found
    collect2: id returned 1 exit status

    Any idea? Thanks a lot.

  133. Harman on June 19, 2009 12:34 pm

    Nice tutorial. Got hold of the SQL functionality in few minutes. Cheers to this Tut!!

  134. Patrick on June 25, 2009 9:11 pm

    @DEAN,

    Great tutorial! Thanks a lot!

    I was wondering could you gimme some idea about how can I show the data in database to the pickerview? Specifically, the data is NSMutableArray, how can I convert it to NSArray to show it on piererview?

  135. paul on June 28, 2009 1:01 am

    What if i wanted to access the database from a webserver instead of on the iphone. Does anyone have the code changes or a sample code that could help me with this?

    Thanks
    Paul

  136. Michael Heyeck on July 10, 2009 10:34 pm

    Useful tutorial, esp. the bit about the libsqlite3.0.dylib Framework. I was a bit puzzled by one piece of memory management minutiae, however: Shouldn’t Animal’s initWithName:description:url: method retain it’s arguments, e.g.:

    -(id)initWithName:(NSString *)n description:(NSString *)d url:(NSString *)u {
    self.name = [n retain];
    self.description = [d retain];
    self.imageURL = [u retain];
    return self;
    }

  137. Prerak Shah on July 11, 2009 3:35 pm

    Hi Dan,
    Its really nice tutorial…. Its very helpful….Thanks…

  138. John on July 12, 2009 1:51 am

    Great tutorial. I appreciate it.

    I have a question on how to deal with Large amounts of data and images. For example, what if your animal table had 1000 entries in it.

    Grabbing them all and loading them into a NSDictionary (for name search etc) or NSArray to be displayed would take quite a while. I am thinking in the 20 – 35 seconds range.

    For Customer Expectations this is quite a long time.

    How would you go about doing this, or better, do you know a “Best Practices” when dealing with SQLite3 or any Large Data delivery and iPhone displays.

    Again, GREAT Tutorial. I def have this in my delicious account for a re-read later.

    roostert

  139. Ryan on July 13, 2009 8:34 am

    Hey, I appreciate this post and think it’s very helpful. I do have a few questions/suggestions for future tutorials: How would this be different doing all this from an online database opposed to through a saved database. I am porting over a web site and cannot possibly have all the database information stored on the application (obviously, and on top of that the content is user-generated and user-accessed so it is definitely necessary for all this to be external). any help with coding for those scenarios (the log in/create account, and accessing internet database from the application). Thanks in advance for the future help, as well as thanks for the already provided help.

  140. z on July 16, 2009 12:27 pm

    Can someone show an example of using the select statement to get a specific record (row number or animal) instead of doing a loop for all the animals?

    Thanks.

  141. Sai on July 24, 2009 7:55 am

    Hi
    This tutorial is really nice and helped me a lot.
    Can u give an example of interacting with database that reside on web server instead of adding database as a resource into iPhone.

  142. Mars on July 27, 2009 12:23 am

    I’m getting a sqlite3 undeclared first use in this function when setting up the db object. I do think the import to the sqlite library should be explicited somewhere or no?

  143. novellino on July 28, 2009 10:53 am

    Hello, thanks for this tutorial. To adapt it for me I would need to include a search engine and the animals in alphabetical order as in contacts in the iphone. I have searched far and wide but I can not. You could make us see how implementing this tutorial?

  144. Henri on July 29, 2009 12:42 pm

    Thanks very much for a great tutorial — it really helps a lot.

    Thank you.

  145. Olivia on August 7, 2009 8:05 am

    Michael,
    You don’t need retain statements because you are actually supposed to deallocate those variables (at least I did).

  146. Olivia on August 7, 2009 8:25 am

    This is for anyone that wants to take a stab at it:

    I am trying to add a Search bar to search the data. I have a Navigation Controller that is accessing the data in the app delegate. My question is regarding this portion of code:

    listContent = [[NSArray alloc] initWithObjects: @”Test”, @”Test1″, nil];

    // create our filtered list that will be the data source of our table, start its content from the master “listContent”
    filteredListContent = [[NSMutableArray alloc] initWithCapacity: [listContent count]];
    [filteredListContent addObjectsFromArray: listContent];

    This code was taken from the Table View Search sample app from Apple’s web site I believe. My question is, if I wanted to initialize the listContent array to the array of data from my app delegate (an array of Animal objects for example), how would I change listContent = [[NSArray alloc] initWithObjects: @”Test”, @”Test1″, nil]; to reflect it? I only want the name variables from the object…do I have to create a separate array when I am reading the SQL data that contains only the names?

    I have been struggling with this for hours. Please help if you can. Thank you.

  147. Mel Malinowski on August 13, 2009 5:16 pm

    Great tutorial! I would love to see a version of this project based on NSFetchedResultsController instead of filling an array. I want to have read-only access to a 200 object Sqlite database with thumbnail images feed a table view list just like this, but cannot find any straightforward examples of how to use NSFetchedResultsController.

    iPhoneCoreDataRecipes uses NSFetchedResultsController, but has such elaborate features it is unclear how to do a simpler, read-only version. Apple has not yet posted adequate documentation of NSFetchedResultsController, at least not adequate for those not steeped in CoreData. It would seem possible to do a clean, relatively simple table view setup, which would be useful to lots of us.

  148. Guy Nesher on August 22, 2009 2:23 pm

    I’m getting an odd error in didSelectRowAtIndexPath when I try to setup the animation –
    [self.navigationController pushViewController:self.noteView animated:YES];
    gives the following error :
    “this class is not key value coding-compliant for the key animalDesciption.”

    Now I made several changes to the example but I can’t figure out what causing this. The database is loaded correctly (with all relevant values) and the list does appear in the first table view.

    Any ideas ?

  149. Jitesh on August 24, 2009 10:16 am

    Thanks for the nice tutorial !!

    I m having same problem like Jim. Can anyone tell me what is my mistake. It do not show any error in code but when u click on Errors and Warnings it gives like what Jim has posted.

  150. sarah lee on September 4, 2009 7:41 am

    Thank you very much your tutorial article and big helpful~!!

  151. Mel on September 13, 2009 2:50 am

    Hi, nice tutorial.

    I just want to know if I can create primary and foreign keys when using sqlite?

    Thanks.

  152. César on September 14, 2009 12:24 pm

    Someone warned you for a memory leaking for the FileManager when file exists…
    An NS type object that you don’t allocate is autoreleased by defalut… in fact you don’t need to release it… if you don’t retain it, the wrong think is not to not release it is to release it… when you leave the method automaticatly release it and in the next autorelease pool the object will be deleted…. thanks for your post and contribution…

  153. JavaMan on September 16, 2009 12:58 am

    Thanks for this, learn to add sql to your app in 20 minutes. Cool!

    I did find that I could not reploy the AnimalDatabase.sql after making changes using the firefox plugin sqlite manager. I took out the line about if (success) return; (tht stops redeployment of a deployed db)but it still showed the same elements from before. I will work on this, I am guessing it has not over-written the first deployment.

    It would be cooler still if this was translated to a framework extension and that could be accessed via a business level service, but this is a great start.

    Much appreciated.

  154. JavaMan on September 16, 2009 10:57 pm

    Hi,

    Just replying to my own mail about not seeing the data changes. My issue was because the simulator deployed it once and the resolutely deied my attempts to overwrite the db later. I fixed it by resetting the simulator installed setting completely and installed the app again. Changes were there.

    Again, cool tutorial

    JavaMan

Trackbacks

  1. pligg.com
  2. 100 Free Courses & Tutorials for Aspiring iPhone App Developers | Best Universities
  3. Apple iPhone development links | 72dpi.net.au
  4. 200ml » 100 Free courses for iPhone Dev
  5. Things that were not immediately obvious to me » Blog Archive » iPhone and SQLite
  6. 100 Free Courses & Tutorials for Aspiring iPhone App Developers | online classes college
  7. [iPhone] SQLite ?? 11?? ?? ?? – 11 SQLite Resources for iPhone Developers | Alones world
  8. links for 2009-08-11 | Alones world
  • There are no trackbacks for this post

Write a Comment