Subscribe ( RSS )

iPhone Programming Tutorials


GPS Where To Go? Find Points of Interest using GPS.
Where To Eat? Find restaurants using GPS.
Dynamic photo effector
Intimate Secrets
Got It!
Advertise Your App Here
 

iPhone Programming Tutorial - Creating a ToDo List Using SQLite Part 4

This is the final installment of our 4 part series of creating a Todo list for the iPhone.  In this tutorial, I will detail how to add and delete new todo objects from the SQLite database.  Make sure that you have completed the following tutorials before you begin this one:

When you have completed this tutorial, you should have a main screen that looks something like this:

Let’s get started…

The first thing we need to do is add the UIBarButtonItem items to the NavigationBar so that we get the “Edit” and “Add” button.  Open up RootViewController.m and add the following code to the viewDidLoad method.

The first thing we see is the line that sets the leftBarButtonItem to self.editButtonItem.  This automatically adds the “Edit” button to the NavigationController.  Also, it sets up the functionality that allows the “delete” buttons to be displayed when the button is pressed.  You can see this functionality if you do a “Build and Go” at this step.  Next, I have manually created a UIBarButtonItem and added it to the navigationbar.  This can be done in Interface Builder, but I wanted to show you how to do it manually and assign an action to it (I’m sure you will require this functionality in a future program).  Here is a break down of the parameters:

  • initWithTitle - The text to be displayed on the button
  • style - How the button will look
  • target - The class object that handles the messages sent from this button
  • action - The method to be called when the button is passed.  We can use @selector and give it the name of the function to call.

Finally, we assign this button to the rightBarButtonItem.  If you do a Build and Go, it should error since we haven’t created the addTodo method. We will do that in a bit.  Now, let’s create a method inside of our Todo object that will add new Todos to the database.

Open up Todo.h and add the following code:

So in addition to the insertNewTodoIntoDatabase method, we also see the deleteFromDatabase method signature.  I have just added this so I don’t have to come back to it later.  We will be implementing this when I show you how to delete todos from the database.  One thing to note about the insertNewTodoIntoDatabase method is it has a “+” rather than a “-” sign.  This means that it is a static method.  Static methods are associated with the class not the instance meaning we can call this method without instanciating this class.  So we can do stuff like Todo.insertNewTodoIntoDatabase.  Now we will implement this method.

Before we can do this, we must declare a few more static sqlite3_statement’s.  Add the following statements to the top of Todo.m

Nothing new here…Now implement the following method:

This is similar to our update method.  Notice that we are inserting default values into the database.  This is so we don’t run into any problems with null or nil values.  The most important part of this method is the fact that it returns the primary key of the newly created todo object.  This will be used later so we can immediately transition to the todo when the “Add” button is pressed.  The last thing we need to do to the todo object is update the dehydrate method so that the todoText gets saved if it gets changed.  Update the dehydrate method to look like this:

There are only a few minor changes here.  First we see the “text = ?” part added to the sql statement.  This is simply so we can update the text of the todo.  The other change is we bound the self.text property to the 1st question mark in the sql statement.  One thing to notice is we call [self.text UTF8String].  This is because sqlite3_bind_text takes a (char *).  This will convert an NSString to an acceptable format.

Now we need to add a method inside of our RootViewController to add a todo.  This is the method that will be called when the user presses the “Add” button.  Inside of RootViewController.m add the following code:

First, we get a reference to the appDelegate object.  This is because we need to call its addTodo method.  Next, we instantiate the TodoViewController if it has not already been instantiated.  We need this around because we will push it on to the view stack and transition to it after we create our new todo object.  After this is done, we call the addTodo method of the appDelegate.  It will return the newly created todo object and the view will be transitioned to its detail screen in order to update its details.  Now we need to implement the method addTodo inside of our appDelegate.  Open up todoAppDelegate.h and add the following code to create the method signature.

Now, let’s implement this method.  Open up todoAppDelegate.m and add the following code:

First, we are calling the insertNewTodoIntoDatabase method of the Todo object.  Notice that we are simply calling the method without first building an instance of a todo object.  As I said in tutorial 3, this is because that method is static and gets called without building an instance of the class.  Next, we insatiate the todo object that was just created by calling its initWithPrimaryKey method.  This will give us reference to the new todo object.  Finally, we append this todo to the end of our todos array.  Since our UITableView is updated with this array, it will automatically include the new todo object.  The last line just returns this todo object.

Remember is the last tutorial we made it so the users could update the status and the priority of a todo?  Well, now we also need to give them the ability to update the text of the todo.  So open up TodoViewController.h and add the following code:


Ok, so I’m guessing you are wondering why the UITextView for the todoText object has been changed to a UITextField.  Well, I will tell you.  UITextView doesn’t have the methods that we need to save the text with our current design.  We will also be changing this on our Interface inside of Interface Builder.  So for now, just believe me and anywhere it says UITextView, change it to UITextField.  The only additional code we added here is the method signature for the updateText method.  It’s an IBAction that will get called when the user presses the “Done” button on the keyboard after setting the text for the todo.  Next, we need to implement this method.  Open up TodoViewController.m and add the following code:

All this does is update the text of the todo to the text that the user entered inside of the UITextField.  The last thing we need to do in order to add a todo is to replace the UITextView with a UITextField and connect it to our updateText method.  Double click on your TodoViewController.xib file to open it in Interface Builder.

Now click on the UITextView on your interface and press the delete key on your keyboard to delete it.  Now, drag a UITextField from the library and drop it onto your interface.  Resize it to fit.  When you have completed that, your interface should look something like this:

Now we need to connect this component.  Make sure it is selected and click Tools -> Connections Inspector to open up the connections inspector.  Drag from the circle next to the method “Did End On Exit” to the “File’s Owner” object.  The words udpateText should pop up.  Click on them to make the connection.  Next, click in the circle next to “New Referencing Outlet” and drag it to the “File’s Owner” object.  Select todoText  when it pops up.  The Connections Inspector should look like this:

Now we are done with Interface Builder.  Go ahead and close it.  We are now able to add todos.  The last thing we need to do is give the ability to delete todos from the list as well as our database.  This is all done in code, and we won’t need interface builder for this.

Let’s start by adding the methods to the appDelegate to handle the deletion of todos.  Open up todoAppDelegate.h and add the following code:

All we see here is a signature for the removeTodo method.  Also, be sure to add a #import “Todo.h” statement to the top of this file so that we can interface with the todo objects. Now let’s implement the removeTodo method.  Open up todoAppDelegate.m and add the following code:

The first line looks up the todo in the todos NSArray.  It returns the index in the array of the todo to be deleted.  Then, we call the deleteFromDatabase method on the todo object and then remove it from the todos array.  Since the UITableView is updated via this array, it will automatically remove the todo without any additional code on our part.

Now, let’s create the removeTodo method for the todo object. We have already written the method signature in Todo.h in a previous step, so open up Todo.m and add the following code:

Remember the delete_statement variable is a static sqlite3_stmt that we declared in a previous step.  First, we check to see if it is nil.  If it is we compile the statement using the sqlite3_prepare statement.  Next, we bind the primary key of the current todo to the “?” in the sqlite3 statement.  Next, we just step the statement to execute it and reset it.  The last thing we need to do to delete todos from the database is to specify what happens when the user presses the “delete” button.  Open up RootViewController.m and add the following code:

The first step (like the first step of many functions) is to get a reference to the appDelegate.  Next, we check to see if we are currently editing.  If so, call the removeTodo method on appDelegate.  The next line, removes the row from the UITableView at the given indexPath.

Now click Build and Go!  You should now be able to add and delete todo items from the database.  This concludes our four part series of creating a todo list for the iPhone.  As always, if you have any questions feel free to leave them in the comments section.  If you get lost at any time you can download the sample code here.

Happy iCoding!

 

32 Responses

arod Says:

September 23rd, 2008 at 12:15 am

This is an awesome tutorial. This really helps me understand how the edit functionality can be triggered and how saving to the database works.

I have found one bug. You have not implemented a mechanism for setting the “dirty” BOOL to yes in the updateText method. You did this in the updatePriority and in the updateStatus. When I change a Todo’s text without also changing the status or the priority, the change is not saved to the database. Can I just declare the “dirty” BOOL in the ToDoViewController and add “dirty = YES” in the updateText method? Of will this need to be done inside the ToDo.m implementation?

Also, in the deleteFromDatabase method, I suggest changing the NSAssert error to something like: @”Error: failed to delete todo with message ‘%s’.” since you’re trying to delete a todo not ’save the priority”.

I’m curious. Could you outline the changes that are needed to the project to support a textView. I’m not asking for you to redo the tutorial. I’m just wondering what is involved. I did notice in IB that there are no built in methods for the TextView as there are for TextField. Thanks,

Alex

pfargo Says:

September 23rd, 2008 at 4:30 am

A nice wrap up. I was able to fix the omission of the updating text Alex mentioned without too much trouble. This series has been a great help.

Looking forward to more :^)

Brandon Says:

September 23rd, 2008 at 2:18 pm

@alex,

I realized this after publishing. I will update it when I have some time. Good job finding it… And the error message was just a lazy copy and paste error. No biggie, I should probably change them though.

Also, with the UITextView, to allow it to save, we would have to change the design a bit. So rather than having each method update their respected property (updatePriority, updateStatus,etc…) we would just do one big update when the user presses the “Back” button. This would just retrieve the values in each of the UI components and update the todo from there. Not a bad design actually, I just didn’t do it this way from the beginning.

Thanks for reading…

AdamJTP Says:

September 23rd, 2008 at 4:45 pm

Firstly - Great work! I can’t find a more detailed set of tutorials on the web.

Just one question - when are you going to show the world how to create these forms using table cells (like the data entry forms in iPhone -> settings)?

BTW. Love the way the code snippets are graphics and not text. It forces us to type the code (which really helps to understand it - not just use it).

Brandon Says:

September 23rd, 2008 at 5:00 pm

@Adam

One such tutorial is in the queue as that makes for much prettier interfaces. Also, that was exactly my intention with the code snippets. It has been my experience with tutorials that if you don’t type it yourself, it is really hard to learn and retain.

Thanks for posting.

Mitchell Says:

September 26th, 2008 at 3:30 pm

Brandon,

Thanks so much. I’ve been following along and so far and really appreciate what you are doing. Like many, I’m getting all the samples and tutorials I can find and you have put together a great series!

Mike Says:

September 26th, 2008 at 6:04 pm

Brandon-
Again, this series has been a great help for me! One question, when I click in the text box to edit an existing name, it clears out the current value - is there a way to set it so the keyboard pops up with the previous value still intact?

Dave Says:

September 28th, 2008 at 10:17 am

Couple things:
1. I get an ‘Assertion failure in -[Todo deleteFromDatabase] path…. error when I try to delete at the stepping command it throws an exception ‘failed to save priority’. Perhaps its a permission thing…

2. When you add an item it doesn’t update the table view, so I guess you need a [self.tableview reloadData] at the end of the addTodo method in the rootviewcontroller.m.

But I got to say that I’ll probably use this app when its done!

Thanks.

Brandon Says:

September 28th, 2008 at 2:10 pm

@Dave,

That’s odd about your assertion failure. Does it give you any more detail (could be a type-o in your SQL statement). Also, the table should update when adding a todo item.

The [self.tableview reloadData] inside of the viewWillAppear method should take care of that. Did you get that step from part 3 of this tutorial. Let me know if you need more help.

thanks for reading…

Kevin Says:

October 4th, 2008 at 10:25 pm

This has been a great series. Thanks for an incredible amount of work. One great thing is the “safety net” this approach gives us. We can experiment within a framework we know works. For example, in RootViewController.m I switched the “Add” button to use the system “+” button type using the initWithBarButtonSystemItem:target:action: method, passing UIBarButtonSystemItemAdd.

Ulthor Says:

October 10th, 2008 at 5:51 pm

Great tutorial, Brandon. Thanks! I feel like I’m beginning to get the hang of it now.

MGC Says:

October 24th, 2008 at 10:22 pm

” Next, we insatiate the todo object that was just created by calling its initWithPrimaryKey method.”

what is “insatiate”?

MGC Says:

October 25th, 2008 at 10:34 am

Why do methods of adding and deleting records need to be at the AppDelegate level? Other methods are within other files, what makes these two methods so special that they need to be part of the delegate? Why not other methods?

AdeC Says:

October 26th, 2008 at 5:36 pm

Any chance you could do something with the swipe-delete functionality???

Also what is the cost of getting the records upfront over getting them when you need to, from the database. I wanted to add a filter function to the to do list where I could filter by completed/open etc My problem is that I dont know how many there are of each type before I tell the tableview. At the moment we tell the table view only the count of todos which is al of them not a filtered subset !!!!

Any idea how I could implement this. I was thinking of creating a todos class which gets the resords from the database when you need them. Would that slow things down too much??

AdeC Says:

October 26th, 2008 at 5:37 pm

Also if you get them when you need them you can do searches ???

Brandon Says:

October 26th, 2008 at 7:17 pm

@AdeC

The swipe to delete functionality should already exists. By making this table editable, this functionality should work.

Off hand, I’m not sure the cost trade-off. I would implement both solutions and test the load times with the tools apple provides.

What are you asking about searches? I’m not sure I understand the questions…

Brandon Says:

October 26th, 2008 at 7:19 pm

@MGC,

what are you, my freaking English teacher? You really enjoy pointing out type-os ehh? No worries, I should be proof reading a little better.

I structured this program using the design pattern that Apple used in the SQLBooks example. I’m not 100% sure why this structure was chosen, but Apple usually makes some solid apps so I think they know a thing or two about design patterns.

Thanks for (proof)reading…

MGC Says:

October 27th, 2008 at 10:40 pm

No not an english teacher, but a newbee to all of this. One thing I learned is that one little character change, omission or capitalization change and code doesn’t work. I even forgot a ‘:’ one time and code compiled but crashed every time. Talk about difficult to debug.

I looked at Apple’s SQLBooks example and I couldn’t figure it out either. So now my concern is that I am not sure Where I should declare methods for other programs.

Thanks for the great tutorials.
Do you have one on doing basic arithmetic?

Mateo Says:

November 1st, 2008 at 1:14 pm

Thanks Brandon! This was a really great tutorial. Before I got to this last part I tried to insert more todo items in the sqlite db via terminal. When I brought the new db into my project none of my new records showed up. I tried deleting previous builds of the project and that still didn’t help. So I was wondering if using an XML instead of sqlite has any benefits for storing records.

Brandon Says:

November 1st, 2008 at 7:22 pm

@Mateo,

The problem is you need to uninstall your app from the simulator. The app is still using to old db (because we copied it to the document folder). When you uninstall it, it clears it out.

After you do this, drag your new db into your project and launch it again. It should then reinstall with the new db.

Hope that helps…

Will Says:

November 3rd, 2008 at 11:50 pm

I found a small bug in the code (or at least my code) that I thought might help everyone out as it took me a while to find.

When you created a new todo and edited nothing else before switching back to the To Do Items tableview, if you exited the app the text would not be saved, this is because the updateText method didn’t change dirty to YES… I fixed this by doing:

In todo.h

-(void)updateText;

In todo.m

-(void)updateText {
dirty = YES;
}

in todoViewController.m

-(IBAction)updateText:(id)sender {
self.todo.text = self.todoText.text;
[self.todo updateText];
}

Works fine now. Hope I helped, let me know id it works for you.

Bill Says:

November 10th, 2008 at 10:11 pm

Great stuff!
Does anybody on this forum know what might be causing the following?
I did this tutorial and everything worked great!!! I then tried to implement the same sqlite3 type code into a separate program. The select statements work great. The problem is that the database will not update. I have the createEditableCopyOfDatabaseIfNeeded implemented just like in the todo example and as I step through it all is good. In my new code though, when I try an insert, it says it is successful, if I make a call to sqlite3_last_insert_rowid it indicates that all is good. But if in a terminal window, I look at the database file, it is not update and when I quit the app and come back in, the changes are not there. Interestingly enough, a ‘-journal’ file does get created on the insert, but it doesn’t seem to do anything for me.
Does anybody have any ideas on this?

Vitor Almeida Says:

November 11th, 2008 at 6:40 am

Great series. Thanks for the hard-work and dedication. Looking forward

Vitor Almeida Says:

November 11th, 2008 at 6:41 am

Great series. Thanks for the hardwork and dedication. These tutorials are very detailed.

Brandon Says:

November 11th, 2008 at 2:00 pm

@Bill,

This could be because the database gets copied to the Documents folder of the iphone (simulated for the simulator). So the database in your project is not the database that gets modifed.

This could be why you are not seeing the changes in the terminal.

Bill Says:

November 11th, 2008 at 3:34 pm

Thanks Brandon, it ended up being something as simple and yet elusive (since an error code is not returned) as the fact that I wasn’t reseting a statement after one of my selects.

On a different note, do you know how a digital timer would be implemented. Is there some simple cool class that I haven’t seen, or do you use the Date or NSTimer classes?

www.iPhonik.pl Says:

November 20th, 2008 at 11:30 am

This is the best tutorial in the web! I saw many , becouse I try to understand how to create soft for iPhone, and I want to thank You for this on!

Great job! I wait for next one !

Paweł

rupert Says:

November 26th, 2008 at 9:35 am

I love your blog! I’ve been following your tutorials and they are simply great!

You’re a great teacher and it really helps me to understand.

Keep up the good work!

Christian Says:

November 29th, 2008 at 3:10 pm

Does anyone know how I would add a tick, just before the priority, so it is easier to distinguish between complete and incomplete to-do’s?

Any help would be appreciated.

Thanks,
~ Christian

KayZee Solutions Says:

December 28th, 2008 at 12:06 pm

Great work Brandon. These have been excellent tutorials to help with some necessary functionality.

In one of your earlier posts (from Tutorial 3) you mentioned that you might be adding video tutorials in the future. I just wanted to mention that I find the written tutorials MUCH more useful and easier to follow. The written version allows us to read at our own pace and scroll up and down as we please rather than pausing/rewinding video playback. Just my opinion.

Keep up the great work!

Tom Says:

January 1st, 2009 at 5:29 pm

Hi Brandon,

Just completed the 4 tutorials and they were fantastic. One quick question, how can I upload the application to my iphone?

Thanks

Tomas

Mike Says:

January 3rd, 2009 at 3:04 pm

When I add an entry, the segmented controller is set to the value of low when the view is loaded. Lets say, I change the value to Med and save it. When I try to add another entry… the segmented controller is set to the last value… in this case… Med. My question is… how do I reset the segmented controller to Low everytime I add a new value? I’ve checked Apple’s docs and can’t find the answer.

Leave a Reply