Technology Servants

  • Increase font size
  • Default font size
  • Decrease font size
Home Articles Shopping List - an iPhone SQLITE tutorial

Shopping List - an iPhone SQLITE tutorial


Introduction

This is the first in a series of articles on writing applications for the iPhone.  The application is a shopping list program that I had written for my old Palm Pilot and am adapting to my iPod Touch.  For the iPhone, I am using SQLITE, a free database program that comes with the iPhone SDK.  This article covers an initial "bare bones" version of the application.  Future articles will cover some of the more advanced features.

The application works as follows:  A database is created that contains a list of items that you normally buy.  Each item has a name, how many you need (possibly zero), and notes for additional information.  In the full version, there is also a list of stores you frequent.  For each item, you can indicate what stores carry each item, and filter the list by stores.

For this version, we simply have a single list.   The items you need have a check mark next to them.  As you buy the item, you can uncheck it.  When there are no more checked items, you have everything you wanted to buy.  In later versions we will get fancier, but this is simple enough to get you started programming, but still have enough functionality to be useful.

Requirements Before Starting

If you are just curious about what is involved in programming on the iPhone,
you can simply read this tutorial.
If you want to actually learn, you probably want to follow along, writing the program yourself.
To do this, you will need is to join the Apple Developer Connection (it is free)
and download the (also free) iPhone SDK.  If you haven't already done this, go to:
http://developer.apple.com/iphone/program/ and come  back when you are done.

Creating the Database

 Although SQLITE is included in the SDK, there isn't a user interface installed.  You have several options, but the one I chose was to install the SQLITE Manager Add-On for Firefox.  You could also open a terminal window and use the command line interface.  I will also show you how to create the database within the program if you want. For the purpose of this tutorial, I will be assuming that you installed the SQLITE Manager. 

Creating table with SQLITE Manager

Create a database with a table named items that has the following fields:

  • id (integer, primary key, and auto-increment)
  • name (varchar)
  • need (integer, default zero)
  • notes (text)

The figure to the left illustrates creating the items table using the SQLITE Manager.  You could also create the table using SQL statement:

CREATE TABLE "items" ("id" INTEGER PRIMARY KEY  AUTOINCREMENT  NOT NULL , "name" VARCHAR, "need" INTEGER DEFAULT 0, "notes" TEXT)

The id field is the primary key, which means that it will be an indexed field where the database will assign an unique value for new items.  Creating a primary key field isn't always necessary, but is usually a good idea.  It makes updating the correct record very easy, and tends to speed things up.

The  name field is the name of the item on your shopping list (bread, milk, etc.)  It could be specific items or general terms (salad ingredients).  This is the name that will appear on the shopping list.

The need field is used to determine if you need any of this item.  If you don't need it, the value should be zero; if you want it, then this field will be 1 or more, depending on how many of the item you need.

The notes can be used to further describe the item name.  For example, if the item is "Bread", the notes field might be "rye", "multigrained", etc. or it might contain specific brand name, size, etc.  If present, this field will appear below the label as a subtitle for the item on the shopping list.

Some sample items for your shopping listOnce you have the table created, you probably will want to enter some items for your shopping list so you have something to test your program with.  Set the need field to zero in a few items, and a few with 2 or more, and the rest set to 1.

 The first time this application runs, it can copy this database to the iPhone /iPod Touch with the items selected based on the need field.

Once you have your database set up the way you want it for your shopping list, we can start to do the actual programming of the Shopping List application.

 XCode

 We will be using the XCode IDE to develop and test our application.

Initial SDK for the Shopping ApplicationBegin by creating a new project.  Select a Navigation based application, and give it the name Shopping.  This will generate a number of files and create an application that you can run.  It won't do much, but you can run it just to see how the iPhone Simulator works. 

Click on the green "Build and Go" button to compile the project and run the application.  If everything was set up correctly, you should see an iPhone display with a series of blank lines.  If something goes wrong, make sure you have selected the Simulator SDK.

Since we will be using SQLITE, we need to add the library to our frameworks.  Right-click on Frameworks and select  Add / Existing Framework and then navigate to /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.1.2.sdk/usr/lib/libsqlite3.dylib and add the library to your frameworks.  Next we want to add a copy of our database to the project.  Right-click on Resources and then Add / Existing Files and navigate to the database you created.  This will place a copy of the database within the resource bundle of the project.

 Item Class

 We will need to create a class for a shopping item.  Right-click on the "Classes" folder in the upper left section of the IDE, select "New File", and then "Objective-C Class".  Make sure the selector is set to NSObject, and click "Next".  This will bring up a dialog window where you can set the file name.  We will call this class "Item", and click on the "Finish" button.  This will create two files: Item.h - the header file, and Item.m - the source file.

Item.h definition of variablesIn the header file (Item.h) we will declare the class variables that we will use to store the information we read from the database.  If you look at Item.h, it contains a line starting with @interface with a pair of braces with nothing contained within the braces.  This is where we will add the variables for the Item class.  We need to create integer variables for id and need, and string variables for name and notes.  We will use the NSInteger and NSString classes to do this.  We also need to create @property statements to generate the getter and setter routines that will allow us to access the fields within the class.

The parameters after the @property statement contain information about the type of property, followed by the data type and name of the actual variable. The integer variables are set to read/write, which simply means that a getter and a setter method will be generated by this statement.   Variables that are more than simple values need some extra memory management.  For the two string variables, you will see the retain value, which means that the object will contain its value and the memory should be released when the object is destroyed.

Definitions of methods for the Item classIn addition to declaring the variables for this class, we also need to define the methods for the class (other than the getters and setters) that will be generated for the properties.  We will define two functions, although we will only use one of them for this version of the application.  I define both of them so you can see two common methods of assigning values to newly created objects.  The Item.h file declares what methods will be defined within the Item.m source code file, while the Item.m file contains the code that is to be executed when the method is called.

Contents for Item.mThe first method initializes a new Item object with the primary key (id), while the second method uses all of the instance variables for the object.  The first method allows you to create an item and then fill in the values as you get them, while the second method requires you to have all the values before you can create the object.  The second method allows you to create an object using a single method call, but for the first method, you will need to use the setters for the other variables.

The actual code appears to the left.  The method that uses the primary key sets the other variables to a safe value.  This is a good practice when initializing an object, even though some might prefer to let the environment assign the default values.

For the second method, all of the values are passed in, and so the variables are set to the values from the arguments.

While the second method seems easiest since you can do everything with a single method call, remember that at some point in the future you might add some new variables to a class, which might mean you will have to go back and change several method calls to add the new variables.  One trick you can use is to have the method with the most variables assign the variables that are unique to that method and then have it call the initialization method with the next most variables, etc.

In Objective-C, variables in a class should appear inside the @interface braces and in @property statements within the header file.  Within the source file, these variables should appear in a @synthesize statement, and should probably appear in at least one of the initialization methods.

 Our Item class doesn't have much functionality at this point.  The class is used mostly to store the information about an item on the shopping list.  If you have a class that requires more functionality, you will need to write methods to perform that functionality.  Just remember to declare the functions in the header file and then define the implementation in the source code file.

General Program Flow

Let us now look at the files that were created with the project and what they do.  When a project is created, an AppDelegate class is created, which is at the center of all that goes on.  The other classes that are created depend on the type of application you create.  For a Navigation based project, a RootViewController is created which is responsible for the navigation interface that consists of a list that can be divided into sections, and a project ViewController, which is responsible for the user interface.  Depending on how you configure the RootController, the items in the list can be selected and/or contain controls.  Clicking on a control or selecting an entry can cause various actions, such as changing the state of the entry or causing a different view to be displayed.  I addition, the list has a header bar that can also contain controls, such as edit or add entries.  The default RootController has only one section to its list and allows the user to select any entry in the list, although there is no functionality associated with the act of selecting an entry.  It is up to the programmer to decide what actions should be taken.

For our project, we see a ShoppingAppDelegate class, a RootViewController class and a ShoppingViewController class.The RootViewController is responsible for displaying items in the main window.  The projectViewController can be used for project specific navigation. We will only be adding code to the ShoppingAppDelegate and RootViewController classes.  The ShoppingViewController class won't be used for this initial version.

The standard classes that the project creates will perform the "normal" operations.  This is why when you create a new project, you can click on "Build and Go" and the project will run.  It won't do much, but it will run.  This means that the only code we need to write is the code to supply the data that is to be displayed and to perform and "non-standard" functionality.  For this version, we will simply provide the data to be displayed and minimal functionality.  In the next version we will introduce some additional functionality that will make the application more useful.

 AppDelegate

Interface Builder for the main windowAs mentioned before, the project AppDelegate class is responsible for controlling the application.  It does this by assigning views to the window and responding to various events that happen during the life of the program.  To decouple the model from the view, the Interface Builder serves as the mediator between the source code and the user interface.  To bring up the Interface Builder, double-click on "MainWindow.xib" in the Resources section of the project window.  This is often called a nib file because the file extension used to be ".nib", and they are still called that by many, or else a "xib" (pronounced like zib) file.  This file contains the information that is needed to relate the user interface to the code.  This should bring up a small window for the Interface Builder along with a larger window for the currently selected view.

Interface AssociationsFor this project, you should see icons for "File's Owner", "First Responder", "ShoppingAppDelegate", "Window", and "Navigation Controller."  The "File's Owner" icon indicates what object is responsible for the application, "First Responder" indicates what object is responsible for handling keyboard input, "Window" is the object that is responsible for the screen, and "Navigation Controller" is the object responsible for the list/table interface.  To learn more about this, Ctrl-Click on the project AppDelegate icon to see a small black dialog box displaying the code/interface associations.  On the left side will be the outlet and delegate objects, and on the right are the corresponding interface terms.  For our project, "navigationController" is associated with "Navigation Controller","window" is associated with "Window", and "delegate" is associated with "File's Owner."  These associations work both ways - if you Ctrl-click on Navigation Controller icon, you will see the same association that you saw with the AppDelegate icon.  Remember that we haven't done anything to change the default functionality, so every Navigation based project will have these same associations by default.  We can, however make changes so that the application behaves however we want.  We will be working with the interface builder in the next version of this application.

AppDelegate header fileTo understand what this means, look at the AppDelegate header file ShoppingAppDelegate.h.  Within the interface braces you see two objects declared: "window" and "navigationController", which are the two items that were in the association dialog.  Notice that in the @property statements, they are identified as "IBOutlet", which indicates that they may interact with the interface.  Based on what we saw in the associations dialog,and what is declared in the header file, any time that "window" appears in the code, it is referring to the interface window, and any time "navigationController" appears in the code, it is referring to the Navigation Controller of the user interface.

AppDelegate source fileNow look at the AppDelegate source code file, ShoppingAppDelegate.m.  Remember that this is the file containing the implementation of the items declared in the header file.  You will see the @implementation for the class, as well as the @synthesis  for the window and navigationController variables.  Basically, this means that the class will use the default constructors and getter/setter routines.

Look at the method applicationDidFinishLaunching now.  When the user starts an application, this method is called after the initialization of the class.  This is your first opportunity to change the functionality of the app.

The default functionality for this method consists of two statements that add the navigationController as a subview to the window, and then make the window visible.

We are going to insert code before these two statements to tailor the functionality of the app.  We could do things like create additional views and add them to the window, obtain data to display, etc.  But before we make any changes, let us understand the big picture of what happens when an app is started.  This will help us understand what kinds of things we need to do and how to do it.

When an application is started, the app is loaded into memory and its resources examined.  The File's Owner is obtained and the applicationDidFinishLaunching method of the File's Owner class is executed.  Any reference to interface entities are mapped into the associated entity of the objects within the application. One or more controllers are added to the view and the window is made visible.  What the method does before the window is made visible will control what the user sees and how the app responds to any user actions.

Customized Functionality

 We are now ready to write code to customize the functionality of our app.  We want our app to do the following:

  • Read items from the database
  • Create a list of the items that were read
  • Indicate which items are needed (need > 0)

 Once the above steps have been taken, we can display the resulting list to the user.  Since we are displaying a check list, we will use a check mark to indicate that an item is needed.  If the user then selects a "needed" item, the check mark will be removed.

As I mentioned before, I have written several versions of this application over the years for my old Palm Pilot.  For those applications, I would usually use a third party database package and configure the view so that when a "needed" item is selected, it is marked no longer needed and removed from the list.  At first this seems like a great way to maintain the list, but there is a problem.  Several times I would be at the store looking at the list while pushing a cart and accidentally touch the screen.  The result was sometimes that an item was removed from the list and then I had to try to figure which item it was so I can add it back in the list.  So, for this version, when an item is unselected, the check mark will be removed but the item will remain in the list until the user asks to refresh the "needed" list.  (Actually, we won't support refreshing the list until the next version.)

 AppDelegate

Changes to AppDelegate Header FileWe are going to start by changing the header file.  This class must open the database, extract the shopping items from the database, and store the results in an array for display.  We will also need to make a decision on how to handle the first time the application is run, when no database has been created  yet.

We will declare two string variables for the database name and the database path (location).  We will declare some SQLITE variables: one database object and two SQL statement objects: one for select statements and the other for update commands.

We will also declare an array of Item objects for the list we get from the database and a flag to determine how to handle the first time the application is run.  And finally. we need to declare whatever methods we will need to implement.

We will add the variable declarations within the @interface curly braces.  Declare two NSString variables - dbpath and dbname for the path and database name.  Declare a NSMutableArray called items to contain the items from the database.  Make sure you declare all of these variables as pointers by placing an asterisk (*) before the variable names.

Since we are declaring SQLITE objects, we need to include the header file by inserting the following statement at the top of the header file:

#import <sqlite3.h>

We will declare a sqlite3 object called *database, and two sqlite3_stmt variables called *selStmt and *updStmt for the SQL select statements and the update statements.  Finally, declare a BOOL variable called copyDb for the copy/create database flag.

Finally, after the closing brace, add a @property statement for the *items array, and declare the following methods:

-(void) openDatabase;
-(BOOL)createDatabase;
-(void) readItems;
-(void) updateItemAtIndexPath:(NSIndexPath *)path;
-(void) closeDatabase;

 The openDatabase method will open the database, creating a new database if it doesn't exist using the createDatabase method, and the closeDatabase method will close the database.  The readItems method will read all the items from the database and store them in the array items.  The updateItemAtIndexPath method is used to change the status of any list item that the user has selected (which toggles the "needed" status of the entry).

AppDelegate Source File

Now that we have declared the variables and methods for the application delegate, it is time to work on the actual implementation.

applicationDidFinishLaunching

applicationDidFinishLaunching methodThe first method we will implement is the applicationDidFinishLaunching method.  This method is called after the application has been loaded and the execution is about to begin.  This is where you perform whatever setup activities you need.  We will load the items array with the contents of the database.

The first thing we do is set the copyDb flag to true, which will cause the initial database that we created to be copied to the user's device the first time this application is run.  If set to false, then an empty database would be created.  This is probably what we want to do for a production version of this application, but for testing purposes, using a copy is better.  Especially  since the initial version doesn't allow the user to add any items.

The next thing we do is to define the database name, and build the path to where the database should reside.  This is done by getting the path to the user's Documents directory and appending the database name.  We initialize the database and SQL statements to nil and then open the database and read the items.  We will see the details on how this is done when we study the openDatabase and readItems methods.

There are two more methods that were generated when we created the project: applicationWillTerminate and dealloc.  These two methods are called at the termination of the application and when the application is about to be removed from memory. Here is what theylook like:

Termination methods

 When the application is terminating, we close the database, which will also free up memory from the SQL statements and other database related variables.

 The dealloc method should release all the memory you allocated for class variables.  This would not include simple scalar variables like copyDb.

 openDB Method

 We will now look at the method that will open the database.  It is fairly long, so we will be doing it in two sections.

The very first time this application is run, the database will not be present on the user's device.  There are two ways of creating the database: create a database with no items or copy the sample database we created to the user's device.  This is why we added the database to the project resources.

openDatabase method - part 1

 The first thing we do is declare some local temporary variables.  The flag ok will be used to save the return code from various methods.  If true, then operation succeeded.  The NSError variable will be set if an error happens, and will contain an error code and some kind of error message.

We begin by allocating memory for the variable array of Item objects.  This was declared in the header file and we initialize it here.  The NSMutableArray is a handy class for storing collections.   It is a variable-sized array of objects.  As we read items from the database, we will store them in this array.

We now test to determine if the database already exists on the user's device.  To do this, we create a NSFileManager object.  We use it to see if the file exists.

If the file does not exist and the copyDb flag is set, then we want to make a copy of the database.  We build the path of the database in the resource bundle and perform a file copy.  The ok flag will be set to true if either the file exists or the copy worked correctly.  We can then release the file manager, since we don't need it any more.

 openDatabase method - Part 2

 At this point, we actually open the database.  If the database didn't exist, then this method will create an empty database.  If the open fails, we will close the database in case it was partially open, and set the database object to nil so that we will not attempt to use the database object.

If the copyDb flag is turned off or it was turned on but the database copy failed, we will create the tables necessary for the database to be used with the application. The ok flag will save the return code from the createDatabase method.

If the ok flag is set to false at this point, then something went wrong, so we trigger an alert with an error message.

 createDatabase method

This is the method that is called when we want to initialize an empty database that was created instead of copying the sampleDB from the bundle.

Create Database   methodThe ret variable will be returned to the caller, and set to true if the database is created correctly, while rc is used to store the results of various SQLITE function calls.  The string createItemsSQL contains the SQL command that will create a Shopping database with no items, and stmt will be used to execute that statement.

The way that SQLITE works is you prepare a  statement using a constant string.  If the string contains any variables (indicated with a question mark),  you must then bind the values of those variables with a variable containing the value.  You can then use the step function to execute the SQL command. The return code value depends on the nature of the command, but the two you will usually be looking for is SQLITE_OK and SQLITE_DONE.  The former indicates that the command worked and you can proceed, while the latter indicates that the command worked but there is nothing further that can be done.  If variables were used, you can use the reset command to remove the bindings, and when you are done you can use the finalize command to release the statement and free all memory.

In our case, we want to perform a single update to the database.  If the prepare worked, we then execute the command and finalize the statement, returning true to the caller if everything worked

readItems method

In this method we read all the items from the database and store them in the items array.  We will first discuss the set up, and then the loop to read all the items.

readItems Method - Part 1The first thing we do is to test if the database was opened correctly. You might remember that we reset the database object to nil if anything went wrong.  If it is still nil, then we simply return since we can't read from the database.

The selStmt is initialized to nil.  The first time this method is called, it will be created and then reused each time this method is called.  The select statement requests all fields from the item table sorted by name, which means they will be stored in alphabetical order and therefore displayed as an alphabetized list.  Because of this, we can display our shopping items using an indexed list, which will allow the user to quickly find the items they are looking for. If we have problems setting up the statement we will issue an error message.

readItems Method - Part 2Once we have set up the select statement, we can loop through the database extracting all the items and storing them in the database.  There are two ways to do this - you can extract all the fields into temporary variables and then call a constructor that will initialize all the fields, or you can create an empty object and then use setters to set up all the values.  I tend to prefer the latter approach because if I need to add new  fields to the object at some later date, I simply add the necessary setters to this routine instead of changing the constructor to include the new fields.

The variable ret will be used to store the return code from the SQLITE select statement.  If the return code is SQLITE_ROW, then another row has been fetched and we can extract the data.  We continue to loop until all the items have been read.

The body of the loop consists of a series of instructions to extract fields from the record set and store them in the Item object, then finally adding the Item object to the list.  We add some logic to replace any NULL strings with empty strings.  After the item is added to the list, we release the memory for the object.

When the loop terminates, we reset the statement so that it can be used again the next time.

updateItemAtIndexPath method

This method is the one that will be called from the RootViewController when the user changes the selection status of any item in the list.  This method performs an update on the record specified in the NSIndexPath by using the primary key to uniquely identify the item.

updateItemAtIndexPath Method

The first thing we do is get the Item object that is stored at the row contained within the path.  It is important to understand that since we are using pointers to objects, when you extract an object from an array, that the object is still contained within the array, and any changes you make to the local copy are also applied to the copy within the array.  This is important because the RootViewController allows the user to make changes to an item in the list, and we can see the updated values by getting our local variable.

The next line is a debugging statement that I have left in to show you how you can troubleshoot any changes you make.  The NSLog function is like a  printf function where you can write debugging messages to the console.  The user would not see them, but you can read them by displaying the debugger console.  The big difference between NSLog and NSAssert is that execution terminates after an assert, but continues after a log message.

The variable ret will be used to store return codes, and the string sql will be used to build the update statement.  Since updStmt is initialized to nil the if statement will only be executed the first time this method is called, and it will prepare the statement for execution.

We then bind the actual values from the item object to the update statement.  You need to be careful to bind using the correct data type function.  You will also notice that we transform any NULL strings to empty strings.  After the update statement is complete, it is executed and the statement reset for the next time.

closeDatabase Method

 This method finalizes the two SQL statements and then closes the database.  It is called when the application is terminating.

closeDatabase Method

 We are now done with the AppDelegate class.  Try to build the project to make sure you don't have any  typos so far.  You should see the iPhone simulator appear, but nothing will be displayed yet, since we haven't done anything with that view.

 RootViewController

 For a Navigation based project, the RootViewController is the class responsible for controlling the main list that the user uses to navigate the data.  We don't need to make any changes to the header file, and if you look at the source file, you will see that a considerable number of routines were already generated when the project was created.  The changes that we need to make will involve allowing this controller to gain access to the items that were read from the database.  As you might remember, those items are stored in the array items within the AppDelegate object.

We will go through the various methods and add whatever code is needed to display the items on the shopping list.  In some cases the methods will be either empty or return a default value. If you are following along and entering the source code yourself, then be careful that you don't miss any changes.  If you need to, you might want to download the source code available on my personal web site and compare what you have with what you downloaded.

numberOfRowsInSection

There should already be a method that returns how many sections are in the view, which default to 1, which is what we want.  This method is to return how many entries are in the current (and only) section. The generated method should currently be returning zero items.  We need to change that to the number of items in our array.

numberOfRowsInSection Method The first thing we need to do is get the AppDelegate object.  Once we have the object, we simply return the count of how many items are currently stored in the array by using the count method.

cellForRowAtIndexPath Method

This is the method that is responsible for returning the content of a cell in the view.  It is used to draw the visible cells.  The argument that is passed to this method is the NSIndexPath for the cell that is requested.

If you look at the default generated method, the beginning of the method retrieves the requested cell, and the last two lines should be a comment to configure the cell followed by a return statement that returns the retrieved cell.   We will be adding code between the command and the return statement.

Just a quick review - the Item objects contain a name and notes, as well as a field containing the number of the items that are needed.  This initial version of the application does not allow the user to set the number of items or edit any of the fields for an item.  Since we need to be able to toggle between an item needed and an item not needed, we want to maintain the number of items that were needed.  The reason for this comes from my own personal experience with this type of application.  I often find myself walking with my hand-held pushing a shopping cart and accidentally bumping the screen in such a way to change the status of an item from needed to not needed

As I mentioned before, I have developed this kind of application for several hand-helds in the past.  Switching the status of an item often removed the item from the list, which seems like a great idea, but isn't always.  This can be a problem, especially if you can't figure out what item you removed.  While walking through the store operating your hand held, the cart sometimes sticks or you get run into by an old man in a hat or a young kid "helping" mommy.  This can cause your finger to accidentally toggle the status of an item and suddenly some random item has disappeared from your list.

To eliminate the above problem, we will use a check mark to indicate that an item is needed.  If we toggle the status of an item, the cell remains visible but the check mark will be removed. If an item is not needed, we don't want to set the quantity to zero in case the user wants to undo the action.  What will do instead is to change the sign of the quantity. So if 1 item is needed and the user selects the item, we will change the quantity from 1 to -1; if 2 items were needed, it will be changed to -2, etc.

On the other hand, if an item was marked not needed in the database and the user toggles the status, we will change the quantity from zero to one.  In later versions of this application, the user will be able to set the quantity to whatever value they want.

cellForRowAtIndexPath Method Part 1 The first set of changes will display the name of the item in the cell and the notes of the item (if present) as a subtitle for the cell.  If the number of items needed is more than 1, we will display the quantity in parentheses after the name.

We get the AppDelegate object and extract the requested Item object that is being displayed.  If the need field is greater than one, we display the name followed by the quantity; otherwise we simply set the textLabel field to the name of the item.  We also set the detailTextField to the notes field for the item.

cellForRowAtIndexPath Part 2

We now have the list built with names and notes.  The next thing we will do is to display a check mark for the needed entries.  If the quantity needed is greater than zero, we add a check mark on the right side of all the needed entries and no accessory marking if it isn't needed.

didSelectRowAtIndexPath Method

This method is called whenever the user selects a row in a list view.  The generated version of the method is probably commented out with some instructions for the kinds of things you might want to do.  We want this method to toggle the needed state for the selected item.  Remember that we want to negate any item that has a quantity greater than zero, and we want to set the quantity to one if the quantity is currently zero.

didSelectRowAtIndexPath Method Part 1

 The first thing we do is obtain the AppDelegate object and extract the Item for the selected row.  We then turn off the selection status of the row and get the selected cell.

At this point, we want to toggle the needed status of the selected cell.  If the quantity is currently zero, we set it to one and turn on the check mark.  If the needed quantity is less than zero, we negate the quantity and display a check mark.  If the quantity is greater than zero, we negate the quantity and turn off the check mark.  You can decide for yourself if you want to include the NSLog message.

The last thing we do is to update the selected cell in the database.

Testing and Troubleshooting

iPhone simulatorThose are all the changes needed to create our initial version of a shopping list application.  You should now try to build and run the app.  Click on the green "Build and Go" button. If all goes well (which it probably won't, of course) you should see the iPhone simulator with a shopping list.  If you set any of the quantities to more than one, you should see the quantity in parentheses.  All the needed items (with quantity greater than zero) should have check marks, and the other items should not.

If you run into any problems, click on the "Errors and Warnings"  tab along the left side of the project window.  The actual warnings should appear in the upper right section of the page, and if you select a message, the corresponding source code line should appear in the lower right section of the page.

Some of the more common questions you should ask are:

Did you include the SQLITE library under Frameworks?

Did you copy your sample database under Resources?

If an error message indicates that a class has not been defined, then make sure you are including all the header files that you need.  For example, if it says that ShoppingAppDelegate is not defined, make sure that you are importing " ShoppingAppDelegate.h".  You should also check to make sure that you didn't type the class name wrong.

If an error message indicates that a variable hasn't been defined, then check where it is defined in the files included with this article and compare the two versions.

This application functions, but is lacking some functionality that will be covered in future tutorials.  There is enough functionality that you can see what it takes to write an iPhone app more complicated than "Hello, World", but not so much functionality that it would be hard to follow.

I would welcome feedback on this article.  You can either add a comment here or email me your comments.  The source files can be found on the download page for my personal web site:

http://billpringle.com/download/index.html

Here is the second installment of this tutorial

 


 

 

 

 

 

 



Add this page to your favorite Social Bookmarking websites
Reddit! Del.icio.us! Mixx! Free and Open Source Software News Google! Live! Facebook! StumbleUpon! TwitThis Joomla Free PHP
Comments (0)
Only registered users can write comments!