Sep 012009

Reading and writing to a .plist file is easy! making it a breeze to save application settings =) this is an example from my “Firmware.app” Objective C Code:

- (void)readPlist

{

NSString *filePath = @”/System/Library/CoreServices/SystemVersion.plist”;

NSMutableDictionary* plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];

NSString *value; value = [plistDict objectForKey:@"ProductVersion"];

/* You could now call the string “value” from somewhere to return the value of the string in the .plist specified, for the specified key. */

}

Objective C Code:

- (void)writeToPlist

{

NSString *filePath = @”/System/Library/CoreServices/SystemVersion.plist”;

NSMutableDictionary* plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];

[plistDict setValue:@"1.1.1" forKey:@"ProductVersion"];

[plistDict writeToFile:filePath atomically: YES];

/* This would change the firmware version in the plist to 1.1.1 by initing the NSDictionary with the plist, then changing the value of the string in the key “ProductVersion” to what you specified */

}

Hope this helps some people!

How To – Write/Read .plist files from an app
Posted by Naeem
Aug 282009

If you are here I am guessing that you are interested in iPhone/iTouch development and are looking for a place to start. Due to Apple’s NDA on the iPhone SDK there really isn’t much information out there on getting started with iPhone development, by writing this tutorial I hope to give people the confidence to get started developing on the iPhone and also to help myself get my head around it.
Lets get started

1. Getting your development environment setup

You will need a copy of the iPhone SDK (available for free at http://developer.apple.com/iphone). Once you have downloaded the installer (it is pretty big, 1GB+) you will need to let it run and to install all the required components.

By default the installer doesn’t add any icons to anywhere so you will have to navigate to /Developer/Applications to find the tools that you will need, I suggest that you drag XCode and Interface Builder to your dock as you will be using them alot!

Fire up XCode to make sure that it launches OK, if so then you are ready to get into it!

2. Getting the TouchXML Libraries

As you cannot use the standard NSXML* libraries for iPhone development (they will work in the simulator but not when you try to deploy the app to the real hardware, I found out the hard way after working on an app for days!) we will be using the TouchXML library from TouchCode

You will need to check the latest code out from the TouchXML SVN Repository, if you dont know how to use SVN I have included the required commands below.

Launch a new Terminal window
Create a new folder called “ExtraLibraries” where you would like to keep your iPhone development libraries (I save mine in /Developer/ExtraLibraries/)

mkdir /Developer/ExtraLibraries

Change into the new folder

cd /Developer/ExtraLibraries

Run the SVN checkout code

svn checkout http://touchcode.googlecode.com/svn/trunk/ touchcode-read-only

You will see the filenames scrolling up the screen and it should only take around 30 seconds to grab all the required files.

Now you have the TouchXML libraries on your local machine ready to start creating your first iPhone app.

3. Setting up your New Project

Now its time to get into the hands on development, start up XCode if you haven’t already and click File > New Project and you will see the window below.

New Project Window

Double click on “Navigation-Based Application”, enter name your project “AdvancedBlogTutorial” and click Save.

You should see the following screen appear with all of your project files.

Initial Project Window

You can lay this screen out any way you wish but my favourite is to make the screen as large as possible and to drag the horizontal resizer all the way to the top to allow for the largest possible “code view” area as possible, like below.

My Development Layout

4. Including TouchXML in our project

As we are using TouchXML for our xml parsing, we will need to import the required files and also edit some of the project settings.

In the menu bar click Project > Edit Project Settings and a new window will appear with 4 tabs at the top (General, Build, Configurations and Comments). We are only interested in the “Build” tab at the moment, so go ahead and click the “Build” Tab and the following window will appear.

Default Project Settings Window

The settings that we are interested in are “Header Search Paths” and “Other Linker Flags”, you can easily find these by typing the beginning of the name into the “Quick Find” at the top right of the window.

You need to add “/usr/include/libxml2? to the “Header Search Paths” and you need to add “-lxml2? to the “Other Linker Flags”.

Once you have done this you can close the settings window and you will be back at your project window.

Now that the libxml2 libraries have been included you will need to import the TouchXML files to your project, to do this simply right click on the “Classes” folder in the left hand pane and click Add > Existing Files…

You will be presented with a finder window and you will need to navigate to the location that you checked the TouchXML files out to (in my case it was /Developer/ExtraLibraries/) and then keep going to the following path “touchcode-read-only/TouchXML/Common/Source/” and select all the files and click “Add”. You will be presented with the following screen:

Add files dialog

Leave everything as default and click “Add” .

You should now see the CXML* files in your left hand pane and they are now available for your application to use.

We are now finally setup and ready to get going.

5. Setting up our Variables, Outlets and Classes

As we used the “Navigation-Based Application” template, much of our layout and code structure has already been created for us. We could have started this from scratch but for simplicity sake it is much easy to use one of the default templates.

Open up the “RootViewController.h” file by expanding the “Classes” folder in the left hand pane and clicking once on “RootViewController.h”, you should see the contents of the right hand pane change to that of the file you selected.

By default you will see

//
//  RootViewController.h
//  AdvancedBlogTutorial
//
//  Created by dBlog on 15/09/08.
//  Copyright __MyCompanyName__ 2008. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface RootViewController : UITableViewController {

}

@end

Change the code to the following

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

@interface RootViewController : UITableViewController {
    // This is the outlet for the blog view, it will allow the data from the controller to be used in a view
    IBOutlet UITableView *blogTable;

    // blogEntries is used to store the data retrieved from the RSS feed before being added to the view
    NSMutableArray *blogEntries;

    // loadSwirlie will display a loading overlay while the data is downloaded from the RSS feed.
    UIActivityIndicatorView *loadSwirlie;
}
@end

The first line, “#import “TouchXML.h””, will import the TouchXML library for us to use in this Controller, it is very important because if we do not include it here we will not be able to access any of the TouchXML features.

The second line, “IBOutlet UITableView *blogTable;”, will allow our view to access data from the controller, this will be where we add the individual blog entries.

The third line, “NSMutableArray *blogEntries;”, creates a new Mutable Array called blogEntries that will be where we store the RSS feed items.

Finally the forth line “UIActivityIndicatorView *loadSwirlie;”, is a view that will overlay the default “Load Swirlie” while the RSS feed is being downloaded, this is especially helpful when accessing via EDGE or when trying to read large feeds.

Save the file.

6. Digging into the core code!

Now that we have the headers all setup and our TouchXML libraries included we are ready to start on the real workhorse of the application, the RSS reader!

Open up the “RootViewController.m” file the same way that you did in the previous step.

You will see alot more code in this file as when the project was created from the template it also created most of the basic code, we will be using some of the auto-generated code and also adding some of our own.

The first thing that we want to do is to make our RSS grabbing function, to do this just paste the following code below the “@implementation RootViewController” line.

// grabRSSFeed function that takes a string (blogAddress) as a parameter and
// fills the global blogEntries with the entries
-(void) grabRSSFeed:(NSString *)blogAddress {

    // Initialize the blogEntries MutableArray that we declared in the header
    blogEntries = [[NSMutableArray alloc] init];	

    // Convert the supplied URL string into a usable URL object
    NSURL *url = [NSURL URLWithString: blogAddress];

    // Create a new rssParser object based on the TouchXML "CXMLDocument" class, this is the
    // object that actually grabs and processes the RSS data
    CXMLDocument *rssParser = [[[CXMLDocument alloc] initWithContentsOfURL:url options:0 error:nil] autorelease];

    // Create a new Array object to be used with the looping of the results from the rssParser
    NSArray *resultNodes = NULL;

    // Set the resultNodes Array to contain an object for every instance of an  node in our RSS feed
    resultNodes = [rssParser nodesForXPath:@"//item" error:nil];

    // Loop through the resultNodes to access each items actual data
    for (CXMLElement *resultElement in resultNodes) {

        // Create a temporary MutableDictionary to store the items fields in, which will eventually end up in blogEntries
        NSMutableDictionary *blogItem = [[NSMutableDictionary alloc] init];

        // Create a counter variable as type "int"
        int counter;

        // Loop through the children of the current  node
        for(counter = 0; counter < [resultElement childCount]; counter++) {

            // Add each field to the blogItem Dictionary with the node name as key and node value as the value
            [blogItem setObject:[[resultElement childAtIndex:counter] stringValue] forKey:[[resultElement childAtIndex:counter] name]];
        }

        // Add the blogItem to the global blogEntries Array so that the view can access it.
        [blogEntries addObject:[blogItem copy]];
    }
}

Now I know that probably looks quite confusing but I have tried to add detailed commenting to allow you to follow what It does. Basically it sends a request to the address that you specify and pulls back the response into the rssParser object. Once this is done it loops through the <item< nodes and adds it, along with its children to the global blogEntries Array.

Now that we have a function that will request, read and process a RSS feed into an Array we have to actually call it from somewhere.

Enter “viewDidAppear”!

This function will already be in your file as the template would have created it, but it will be commented out. Remove the comment tags and edit the code to look like the following:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    // Check if blogEntries has already been filled, if not
    // then send the request
    if([blogEntries count] == 0) {
        // Create the feed string, in this case I have used dBlog
        NSString *blogAddress = @"http://dblog.com.au/feed/";

        // Call the grabRSSFeed function with the above
        // string as a parameter
        [self grabRSSFeed:blogAddress];

        // Call the reloadData function on the blogTable, this
        // will cause it to refresh itself with our new data
        [blogTable reloadData];
    }
}

The above code simply checks to see if the item count in blogEntries is zero, if true then it will call the grabRSSFeed function with the supplied URL and then reload the Table Outlet with the new data. You can change the URL to any valid RSS feed and it will work.

We are getting extremely close to a working application now, just a couple more small changes and we are up and running!

Ones of these is the “numberOfRowsInSection” function, if you have a look in your file you will see it up near the top and it will be returning a static value of zero. What this means is, is that every time the table is reloaded it calls this function to see how many cells it needs to draw. Currently this will always return zero and therefore it will never actually draw anything :(

What you need to do is to make it so when this function is called it returns the count of items in the blogEntries Array, this is very easy to do. Just change the “return 0;” line to the following:

return [blogEntries count];

Now whenever this function is called it will return the correct number of entries, that was easy wasn’t it?

Our final code change will actually generate the cells for the table view, we are just doing a simple cell that shows the title text, but this can be anything including icons, fonts, styles, etc.

Find the “cellForRowAtIndexPath” function in your file, then inside that function find “// Set up the cell” and enter the following code under it.

int blogEntryIndex = [indexPath indexAtPosition: [indexPath length] -1];
[cell setText:[[blogEntries objectAtIndex: blogEntryIndex] objectForKey: @"title"]];

What the above code does is it grabs the index of the item that is being generated and then calls the setText function on the cell with the “title” value of the corresponding entry in blogEntries. You could easily change this to “link”, “pubDate” or any other child node of the node.

Guess what? Its now time to run your application!

7. Running the code for the first time

Make sure that everything is saved and then click Build > Build and Go (Run), you can also press Command + Enter to do the same thing.

You should see the iPhone simulator appear and your application will start up, you should see…nothing!

What? You mean I spent all that time for an application that doesn’t even do anything?

No, no, no, It does do everything that you told it to..BUT, we forgot to link the table display to the blogTable Outlet..doh!

8. Linking the Table in the View to the blogTable Outlet

Double click on the “RootViewController.xib” file in the resources folder in the left hand pane, the “Interface Builder” application will launch with your RootViewController interface in it. You should see something similar to the image below:
Interface Builder

Now in the Main window you will see three icons, the “File’s Owner”, “First Responser” and “Table View”. What we need to do is to Control click and drag from the “File’s Owner” to the “Table View” icons, you will see a blue bar appear as you drag and when you let go over the “Table View” icon a little grey window will appear, see below:

picture-11.png

You will need to select “blogTable” as that is the Outlet we created in our header file. Once you are done you can save the interface and click “Interface Builder”.

9. The Moment of Truth

If you try to Build and Run your application now you should get some results in the screen, for my blog it looked like the following:
Mine Worked!

10. Project Files

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

11. What’s Next?

Well currently you can’t really do much with the application apart from read the headers, I intend to create a series of tutorials outlining how to actually read the rest of the feeds on your phone, how to add multiple feeds and even how to add some simple animation to spruce things up. I will also be taking a look at memory management too as this tutorial hasn’t looked into this at all.

This all depends on time and also on how well this first tutorial goes. But hopefully there will be many more to come.

I hope that you all have a better idea of how to work with the iPhone SDK and also how to get the TouchXML library up and running (it took me a fair while to get my head around it!).

iPhone SDK Tutorial: Building an Advanced RSS reader using TouchXML (Part 1)
Posted by Naeem Tagged with: , , , , ,
Aug 262009

Introduction
In the detail view, the user will be able to edit the fields by clicking the edit button and selecting a row at a time to load the edit view. When the data is updated in the edit view, we will mark the object as dirty. Data will only be saved in the database when the application is being terminated or when it receives a memory warning.

Editing Data
First thing we have to do is, place the edit button on the left bar. We add the button in viewDidLoad method and this is how the source code looks like for DetailViewController.m

- (void)viewDidLoad {
[super viewDidLoad];

self.navigationItem.rightBarButtonItem = self.editButtonItem;
}

When the edit button is clicked, we will hide the back button and let the user know that the row can be selected to edit. This is how the code is going to look like

- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
[self.navigationItem setHidesBackButton:editing animated:animated];

[tableView reloadData];
}

The method setEditing is called when the user clicks on the edit button and it is where we hide or show the back button. The table view is also refreshed, so we can set the accessory type. To hide the back button use setHidesBackButton message. This is how the code looks like

- (UITableViewCellAccessoryType)tableView:(UITableView *)tv accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath {
// Show the disclosure indicator if editing.
return (self.editing) ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone;
}

accessoryTypeForRowWithIndexPath gets called to use a disclosure control for the specified row. Based on the editing variable status, an indicator is set. You may have noticed that, the row can be selected even if the user did not click on the edit view. The following code takes care of that

- (NSIndexPath *)tableView:(UITableView *)tv willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Only allow selection if editing.
return (self.editing) ? indexPath : nil;
}

willSelectRowAtIndexPath tells the delegate that a specified row is about to be selected and is called before didSelectRowAtIndexPath. Return the indexPath selected if the user clicks on edit and nil if the done button is clicked.

Now we handle the event didSelectRowAtIndexPath, which is called when a row is selected. The user will select a row to load the edit view where a value can be changed.

Creating the edit view

Create a new view in Interface Builder and name it “EditView”. Place a text field on the view and set the property capitalize to Words and uncheck “Clear when editing begings” field. leave all the other properties as default. Then add UINavigationBar from the library to the EditView nib file and place two bar button items on the right and left bar button items. Set the style of the left button item to Cancel and the right one to Save.

Creating the edit view controller
In Xcode create a new UIViewController and name it “EditViewController”. This is how the header file looks like

#import &lt;UIKit/UIKit.h&gt;
@interface EditViewController : UIViewController {

IBOutlet UITextField *txtField;
NSString *keyOfTheFieldToEdit;
NSString *editValue;
id objectToEdit;
}

@property (nonatomic, retain) id objectToEdit;
@property (nonatomic, retain) NSString *keyOfTheFieldToEdit;
@property (nonatomic, retain) NSString *editValue;

- (IBAction) save_Clicked:(id)sender;
- (IBAction) cancel_Clicked:(id)sender;

@end

Let’s finish working with the Interface Builder first, by making all the right connections.

Text Field Properties

File’s Owner Class Identity

Edit View Nib File

Navigation Bar with two buttons

File’s Owner Connections

Coming back to the EditViewController header file, notice that we do not have a reference to the Coffee class any where, the edit view controller does not know anything about what object it is going to edit and we want to keep it this way. Let’s look at the properties defined in the header file and see what they are meant to to. txtField is a reference to the text field placed on the view, keyOfTheFieldToEdit is the key of the field we are going to edit or the property name as string, editValue is going to be the value we are about to edit (this is the value which will be displayed on the text field) and objectToEdit declared as type id which is going to be the Coffee object itself. save_Clicked and cancel_Clicked handle the events when save or cancel are clicked.

Selecting a row to edit
Let’s go back to the Detail view controller and look at what goes on in didSelectRowAtIndexPath method. This is how the code looks like

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

//Keep track of the row selected.
selectedIndexPath = indexPath;

if(evController == nil)
evController = [[EditViewController alloc] initWithNibName:@”EditView” bundle:nil];

//Find out which field is being edited.
switch(indexPath.section)
{
case 0:
evController.keyOfTheFieldToEdit = @”coffeeName”;
evController.editValue = coffeeObj.coffeeName;
break;
case 1:
evController.keyOfTheFieldToEdit = @”price”;
evController.editValue = [coffeeObj.price stringValue];
break;
}

//Object being edited.
evController.objectToEdit = coffeeObj;

//Push the edit view controller on top of the stack.
[self.navigationController pushViewController:evController animated:YES];

}

selectedIndexPath is a new variable declared in the Coffee header file, which is of type NSIndexPath. The variable is used to keep track of which row was selected, so we can deselect it when the detail view is shown. evController is declared in the header file and is of type EditViewController. This is how the code looks like in the DetailViewController header file

@class Coffee, EditViewController;

@interface DetailViewController : UIViewController <UITableViewDataSource, UITableViewDelegate> {

IBOutlet UITableView *tableView;
Coffee *coffeeObj;
NSIndexPath *selectedIndexPath;
EditViewController *evController;
}

@property (nonatomic, retain) Coffee *coffeeObj;

@end

Coming back to didSelectRowAtIndexPath, first thing we do is keep track of the row selected then initialize the controller if it is nil. We then find out which row to edit and assign “keyOfTheFieldToEdit”, “editValue” and objectToEdit properties. keyOfTheFieldToEdit takes the name of the property, edit value takes the value to edit and objectToEdit takes the coffee object. Since objectToEdit is declared as id and not Coffee, this view can be used to edit any object. We will see how to edit the coffee object soon. At last, the edit view controller is pushed to the top of the stack.

Display data in edit view
To make the edit view look pretty, let’s give it a background color. In viewDidLoad method, set the view’s background color to groupTableViewBackgroundColor, and this is how the code looks like

- (void)viewDidLoad {
[super viewDidLoad];

self.view.backgroundColor = [UIColor groupTableViewBackgroundColor];
}

Back to EditViewController, in viewWillAppear method, we are going to set the text of the text field and also the title of the view. The title of the view is going to be the property we are about to edit, which we get it from keyOfTheFieldToEdit variable, this also becomes the placeholder text for the text field. Since, we are only going to edit one field in this view, it makes sense to have the keyboard show up when the view is loaded. This way the user does not have to tap the text field. We do this by passing the message becomeFirstResponder to text field. This is how the code looks like

- (void)viewWillAppear:(BOOL)animated {

[super viewWillAppear:YES];

self.title = [self.keyOfTheFieldToEdit capitalizedString];
txtField.placeholder = [self.keyOfTheFieldToEdit capitalizedString];

txtField.text = self.editValue;

[txtField becomeFirstResponder];
}

The code is pretty self explanatory, we get the property name as capitalized string by passing the message capitalizedString and show the text field by passing becomeFirstResponder to the text field.

Now we have to handle save_Clicked and cancel_Clicked methods. This is how the code looks like for the cancel method

- (IBAction) cancel_Clicked:(id)sender {

[self.navigationController popViewControllerAnimated:YES];
}

We simply pop the present view controller from the stack to show the detail view controller, by passing popViewControllerAnimated message.

This is how the save_Clicked method looks like

- (IBAction) save_Clicked:(id)sender {

//Update the value.
//Invokes the set<key> method defined in the Coffee Class.
[objectToEdit setValue:txtField.text forKey:self.keyOfTheFieldToEdit];

//Pop back to the detail view.
[self.navigationController popViewControllerAnimated:YES];
}

Two lines, without any comments. This is what I love about iPhone programming, we can update any object by just passing a message to the object and asking it to change some properties. setValue does all the tricks, which will set the property of the receiver specified by a given key to a given value (text borrowed from documentation).

However, I found out that I was not able to set the value of boolean property isDirty to YES using setValue:forKey and that is why I had to write my own version of setCoffeeName and setPrice. According to the design of this app, we will only save data which has been changed in memory, to the database when the application is terminated or the app receives a memory warning. This way we reduce the round trip from the app to the database, making it a little faster.

This is how the setCoffeeName and setPrice methods look like

- (void) setCoffeeName:(NSString *)newValue {

self.isDirty = YES;
[coffeeName release];
coffeeName = [newValue copy];
}

- (void) setPrice:(NSDecimalNumber *)newNumber {

self.isDirty = YES;
[price release];
price = [newNumber copy];
}

Since the property is declared with copy attribute, we get the copy of the newValue and assign it to the local variable. The isDirty property is also set to YES.

When the user clicks on save, the detail view is shown but the row which was selected is still highlighted. To deselect the row, pass deselectRowAtIndexPath message to the table view, the message is passed in viewWillDisappear method and this how the code looks like. You can call this method in viewWillAppear method too

- (void)viewWillDisappear:(BOOL)animated {

[tableView deselectRowAtIndexPath:selectedIndexPath animated:YES];
}

We still have to update the data in the SQLite database and write code for it. Data is only updated when the app is about to be terminated or when a memory warning is received. applicationWillTerminate method is called when the app is shutting down, which is declared in SQLAppDelegate.m file. This is how the code will change

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

//Save all the dirty coffee objects and free memory.
[self.coffeeArray makeObjectsPerformSelector:@selector(saveAllData)];

[Coffee finalizeStatements];
}

The method makeObjectsPerformSelector sends saveAllData message to each object in the array. So we are going to declare a method in the Coffee class header file called “saveAllData” and write the implementation in the implementation file (Coffee.m file)

//Complete code listing not shown
- (void) hydrateDetailViewData;
- (void) saveAllData;
...

Code in Coffee.m file looks like this

- (void) saveAllData {

if(isDirty) {

if(updateStmt == nil) {
const char *sql = “update Coffee Set CoffeeName = ?, Price = ? Where CoffeeID = ?”;
if(sqlite3_prepare_v2(database, sql, -1, &updateStmt, NULL) != SQLITE_OK)
NSAssert1(0, @”Error while creating update statement. ‘%s’”, sqlite3_errmsg(database));
}

sqlite3_bind_text(updateStmt, 1, [coffeeName UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_double(updateStmt, 2, [price doubleValue]);
sqlite3_bind_int(updateStmt, 3, coffeeID);

if(SQLITE_DONE != sqlite3_step(updateStmt))
NSAssert1(0, @”Error while updating. ‘%s’”, sqlite3_errmsg(database));

sqlite3_reset(updateStmt);

isDirty = NO;
}

//Reclaim all memory here.
[coffeeName release];
coffeeName = nil;
[price release];
price = nil;

isDetailViewHydrated = NO;
}

We only save the data of the objects which has been changed in memory. updatestmt is declared as static and is of type sqlite3_stmt.

//Complete code listing now shown.
static sqlite3_stmt *detailStmt = nil;
static sqlite3_stmt *updateStmt = nil;

@implementation Coffee

We prepare the update statement if it is nil, then assign the variables to the update statement where the index starts from 1 and not 0. The method sqlite3_step is called and if it returns SQLITE_DONE then we know that the update was a success. Finally we reset the updatestmt so it can be reused later. The update statement like any other statement is finalized in finalizeStatements method which looks like this, which is implemented in Coffee.m file.

+ (void) finalizeStatements {

if (database) sqlite3_close(database);
if (deleteStmt) sqlite3_finalize(deleteStmt);
if (addStmt) sqlite3_finalize(addStmt);
if (detailStmt) sqlite3_finalize(detailStmt);
if (updateStmt) sqlite3_finalize(updateStmt);
}

When the application receives a memory warning, “applicationDidReceiveMemoryWarning” is called which is implemented in SQLAppDelegate.m file and this is how the source code looks like

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {

//Save all the dirty coffee objects and free memory.
[self.coffeeArray makeObjectsPerformSelector:@selector(saveAllData)];
}

Here the message finalizeStatements is not passed to the Coffee class because the application is not shutting down.

Conclusion
This tutorial shows how to save data in a edit view by passing the setValue message, shows how to save dirty data to the database when the application is terminated or when it receives a memory warning. This also concludes the SQLite tutorial series.

Attachments

If you think information was helpful for you Please Donate


SQLite Tutorial – Updating data
Posted by Naeem Tagged with: , , , ,
Aug 262009

Introduction
Using BLOB data type we can store an image in the SQLite database. The data that actually gets stored in the database is the bytes that make up an image or a file. The is the sixth tutorial in SQLite tutorial series and borrows its source code from the previous one. We will change the “DetailView” by adding a new section which will be used to display or change the image. The image can only be changed when the view is in the edit mode. We will use a UIImagePickerController to display the photo album and to select an image.

Changing the database
Let’s start by adding a new column to our “Coffee” database. Name the column “CoffeeImage” and set its data type to BLOB. This is how the database schema looks like in SQLite manager


Changing the Coffee Class
Add a new property to the “Coffee” class which will hold the image from the database. This is how the header and the implementation files look like

//Coffee.h
//Complete code listing not shown
@interface Coffee : NSObject {

NSInteger coffeeID;
NSString *coffeeName;
NSDecimalNumber *price;
UIImage *coffeeImage;
//Complete Code listing not shown …
}
@property (nonatomic, readonly) NSInteger coffeeID;
@property (nonatomic, copy) NSString *coffeeName;
@property (nonatomic, copy) NSDecimalNumber *price;
@property (nonatomic, retain) UIImage *coffeeImage;
//Complete Code listing not shown …

The new property is synthesized and released in the dealloc method

//Coffee.m
//Complete code listing now shown
@synthesize coffeeID, coffeeName, price, isDirty, isDetailViewHydrated, coffeeImage;

- (void)setCoffeeImage:(UIImage *)theCoffeeImage {

self.isDirty = YES;
[coffeeImage release];
coffeeImage = [theCoffeeImage retain];
}

- (void) dealloc {

[coffeeImage release];
[price release];
[coffeeName release];
[super dealloc];
}

Changing the Detail View
Let’s add a new row to the detail view, which will be used to display the image or to change the image in edit mode. Since the style of the table view placed on the detail view is set to “Grouped”, we will return three sections in numberOfSectionsInTableView and this is how the source code looks like

//DetailViewController.h
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tblView {
return 3;
}

Let’s also display a title for the new section in tableView:titleForHeaderInSection. The code changes like this

//DetailViewController.m
- (NSString *)tableView:(UITableView *)tblView titleForHeaderInSection:(NSInteger)section {

NSString *sectionName = nil;

switch (section) {
case 0:
sectionName = [NSString stringWithFormat:@"Coffee Name"];
break;
case 1:
sectionName = [NSString stringWithFormat:@"Price"];
break;
case 2:
sectionName = [NSString stringWithFormat:@"Coffee Image"];
break;
}

return sectionName;
}

Displaying the photo album
Now we need to display a photo album when the section is selected in edit mode. From this album we can select a image to be saved in the database. Using a UIImagePickerController we can capture an image from the camera or the photo library on the device. UIImagePickerController class manages all the user interactions and all we have to do is display and dismiss it. Before we can display the view, we need to check if the interface is supported by calling isSourceTypeAvailable class method. The delegate of the UIImagePickerController should also confirm to UINavigationControllerDelegate and UIImagePickerControllerDelegate. In this case we will set the delegate of UIImagePickerController to “DetailViewController” and this is how the header and implementation file changes

//DetailViewController.h
@class Coffee, EditViewController;

@interface DetailViewController : UIViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate, UITableViewDataSource, UITableViewDelegate> {

IBOutlet UITableView *tableView;
Coffee *coffeeObj;
NSIndexPath *selectedIndexPath;
EditViewController *evController;

UIImagePickerController *imagePickerView;
}

@property (nonatomic, retain) Coffee *coffeeObj;

@end

The variable “imagePickerView” is released in the dealloc method and the code listing is now shown here. We will initialize “imagePickerView” in viewDidLoad method and this is how the code changes

//DetailViewController.m
- (void)viewDidLoad {
[super viewDidLoad];

self.navigationItem.rightBarButtonItem = self.editButtonItem;

//Configure the UIImagePickerController
imagePickerView = [[UIImagePickerController alloc] init];
imagePickerView.delegate = self;
}

From the above code, “imagePickerView” is initialized and its delegate is set to self. This way “DetailViewController” will be notified of all the user actions on the picker controller. First, we need to display the view and it is done in tableView:didSelectRowAtIndexPath and this is how the code looks like

//DetailViewController.m
- (void)tableView:(UITableView *)tblView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

//Keep track of the row selected.
selectedIndexPath = indexPath;

if(evController == nil)
evController = [[EditViewController alloc] initWithNibName:@”EditView” bundle:nil];

//Find out which field is being edited.
switch(indexPath.section)
{
case 0:
evController.keyOfTheFieldToEdit = @”coffeeName”;
evController.editValue = coffeeObj.coffeeName;

//Object being edited.
evController.objectToEdit = coffeeObj;

//Push the edit view controller on top of the stack.
[self.navigationController pushViewController:evController animated:YES];
break;
case 1:
evController.keyOfTheFieldToEdit = @”price”;
evController.editValue = [coffeeObj.price stringValue];

//Object being edited.
evController.objectToEdit = coffeeObj;

//Push the edit view controller on top of the stack.
[self.navigationController pushViewController:evController animated:YES];
break;
case 2:
if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary])
[self presentModalViewController:imagePickerView animated:YES];
break;
}
}

First confirm if the photo library is available on the device or not, if it is then present the view to the user. The method imagePickerController:didFinishPickingImage:editingInfo is called when an image is selected. This is how the code looks like

//DetailViewController.m
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)dictionary {

coffeeObj.coffeeImage = image;
[tableView reloadData];
[picker dismissModalViewControllerAnimated:YES];
}

We get the selected image and set the “coffeeImage” property of the selected coffee. The table view is then reloaded and the picker view is dismissed.

Display the coffee image
We will display the coffee image in tableView:cellForRowAtIndexPath and this is how the code looks like

//DetailViewController.m
- (UITableViewCell *)tableView:(UITableView *)tblView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}

switch(indexPath.section) {
case 0:
cell.text = coffeeObj.coffeeName;
break;
case 1:
cell.text = [NSString stringWithFormat:@"%@", coffeeObj.price];
break;
case 2:
cell.text = @”Change Image”;
if(coffeeObj.coffeeImage != nil)
cell.image = coffeeObj.coffeeImage;
break;
}

return cell;
}

If the index of the section is 2 then we set the title of the cell to “Change Image” and the image to the coffee image.

Saving image in the SQLite database
Until now we haven’t saved the image in the database because we only save data when the application is being terminated or if a memory warning is received. The data is saved in “saveAllData” method as seen in the previous tutorial. Let’s change this method to save the image in the database which now has a new column called “CoffeeImage” to hold the image as bytes. This is how the code looks like

//Coffee.m
- (void) saveAllData {

if(isDirty) {

if(updateStmt == nil) {
const char *sql = “update Coffee Set CoffeeName = ?, Price = ?, CoffeeImage = ? Where CoffeeID = ?”;
if(sqlite3_prepare_v2(database, sql, -1, &updateStmt, NULL) != SQLITE_OK)
NSAssert1(0, @”Error while creating update statement. ‘%s’”, sqlite3_errmsg(database));
}

sqlite3_bind_text(updateStmt, 1, [coffeeName UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_double(updateStmt, 2, [price doubleValue]);

NSData *imgData = UIImagePNGRepresentation(self.coffeeImage);

int returnValue = -1;
if(self.coffeeImage != nil)
returnValue = sqlite3_bind_blob(updateStmt, 3, [imgData bytes], [imgData length], NULL);
else
returnValue = sqlite3_bind_blob(updateStmt, 3, nil, -1, NULL);

sqlite3_bind_int(updateStmt, 4, coffeeID);

if(returnValue != SQLITE_OK)
NSLog(@”Not OK!!!”);

if(SQLITE_DONE != sqlite3_step(updateStmt))
NSAssert1(0, @”Error while updating. ‘%s’”, sqlite3_errmsg(database));

sqlite3_reset(updateStmt);

isDirty = NO;
}

//Reclaim all memory here.
[coffeeName release];
coffeeName = nil;
[price release];
price = nil;

isDetailViewHydrated = NO;
}

We first rewrite the update query to include the CoffeeImage column. We also need a way to get the image data as bytes and this is where UIImagePNGRepresentation method helps us. We then bind the BLOB parameter using sqlite3_bind_blob method. The first parameter takes the update statement, the second parameter takes the index of the parameter value, the third one is the actual data itself, the fourth one is the length of the data which is being saved, and a pointer to a method which is responsible to clean up the data. If sqlite3_bind_blob method does not return SQLITE_OK then we display an error in NSLog. If there is no error while saving the data then we have successfully saved the image in the SQLite database on the iPhone/iPod.

Getting an image from the SQLite database
We now have to get the data from the database when the detail view is loaded. This is done in “hydrateDetailViewData” method and this is how the code changes

//DetailViewController.m
- (void) hydrateDetailViewData {

//If the detail view is hydrated then do not get it from the database.
if(isDetailViewHydrated) return;

if(detailStmt == nil) {
const char *sql = “Select price, CoffeeImage from Coffee Where CoffeeID = ?”;
if(sqlite3_prepare_v2(database, sql, -1, &detailStmt, NULL) != SQLITE_OK)
NSAssert1(0, @”Error while creating detail view statement. ‘%s’”, sqlite3_errmsg(database));
}

sqlite3_bind_int(detailStmt, 1, coffeeID);

if(SQLITE_DONE != sqlite3_step(detailStmt)) {

//Get the price in a temporary variable.
NSDecimalNumber *priceDN = [[NSDecimalNumber alloc] initWithDouble:sqlite3_column_double(detailStmt, 0)];

//Assign the price. The price value will be copied, since the property is declared with “copy” attribute.
self.price = priceDN;

NSData *data = [[NSData alloc] initWithBytes:sqlite3_column_blob(detailStmt, 1) length:sqlite3_column_bytes(detailStmt, 1)];

if(data == nil)
NSLog(@”No image found.”);
else
self.coffeeImage = [UIImage imageWithData:data];

//Release the temporary variable. Since we created it using alloc, we have own it.
[priceDN release];
}
else
NSAssert1(0, @”Error while getting the price of coffee. ‘%s’”, sqlite3_errmsg(database));

//Reset the detail statement.
sqlite3_reset(detailStmt);

//Set isDetailViewHydrated as YES, so we do not get it again from the database.
isDetailViewHydrated = YES;
}

The select query is changed to include CoffeeImage column and we use sqlite3_column_blob method to load the BLOB data into variable of type NSData. WE create an image of type NSData from imageWithData class method. The rest of the code works as described above.

This tutorial wasn’t designed to display imags anywhere and is a direct result of all the emails I got asking, how to save images in the SQLite database. So if you choose an image which does not fit in the section, it will take up all the space on the view.

Conclusion
This tutorial explains how to save and read images from a SQLite database. We can use the same functionality to save files in the database. I hope you had fun and learnt something new with this tutorial.

Attachments

If you think information was helpful for you Please Donate


SQLite Tutorial – Saving images in the database
Posted by Naeem Tagged with: , , , , , , ,
Aug 262009

Introduction
Adding data into the database is very simple using the SQLite library, although there are some tweaks we have to perform if we want the application to behave in a certain way. This tutorial picks up its source code from the last tutorial in this series. This is how the “AddView” looks like

The work flow is as following, a user will click on the “Add” button on the left hand bar button item and the “AddView” will be presented to him. The user will enter the coffee name and price and click on “Save” on the navigation item on the right hand side to save it. Internally, it will call “save_Clicked” method which will create a “Coffee” object and set all the right properties. We then call “add_Coffee” method on “SQLAppDelegate” which will call “add_Coffee” method on the “Coffee” class and then it will add the object to the array. We then dismiss the add view controller and reload data on the table view in “viewWillAppear” method implemented in “RootViewController.m”.

Creating the addCoffee Method
First thing we do is create the addCoffee method in the Coffee class which is responsible for inserting data in the database.

This is how the header file looks like (Complete code not shown)

- (void) addCoffee;

and the method is implemented in Coffee.m file

- (void) addCoffee {

if(addStmt == nil) {
const char *sql = "insert into Coffee(CoffeeName, Price) Values(?, ?)";
if(sqlite3_prepare_v2(database, sql, -1, &amp;addStmt, NULL) != SQLITE_OK)
NSAssert1(0, @"Error while creating add statement. '%s'", sqlite3_errmsg(database));
}

sqlite3_bind_text(addStmt, 1, [coffeeName UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_double(addStmt, 2, [price doubleValue]);

if(SQLITE_DONE != sqlite3_step(addStmt))
NSAssert1(0, @"Error while inserting data. '%s'", sqlite3_errmsg(database));
else
//SQLite provides a method to get the last primary key inserted by using sqlite3_last_insert_rowid
coffeeID = sqlite3_last_insert_rowid(database);

//Reset the add statement.
sqlite3_reset(addStmt);
}

The “add_Stmt” is declared as static sqlite3_stmt variable. It is finalized in finalizeStatements and this is how the code looks like

//Complete code listing not shown
#import "Coffee.h"

static sqlite3 *database = nil;
static sqlite3_stmt *deleteStmt = nil;
static sqlite3_stmt *addStmt = nil;

@implementation Coffee
...
+ (void) finalizeStatements {

if(database) sqlite3_close(database);
if(deleteStmt) sqlite3_finalize(deleteStmt);
if(addStmt) sqlite3_finalize(addStmt);
}

Coming back to addCoffee method, “add_Stmt” is built using the appropriate insert SQL code. To bind the coffee name the following method sqlite3_bind_text is used and sqlite3_bind_double is used to bind the price variable to the insert statement. Since the method only accepts a value of datatype double, we send doubleValue message to the receiver. Execute the statement using sqlite_step method and if it returns SQLITE_DONE then the row was successfully added to the database. We still do not have the primary key for the row which was inserted, which we can get by calling sqlite3_last_insert_rowid method and passing the database object. The “rowid” is only returned on column of type INTEGER PRIMARY KEY.

After adding the data in the database, we have to add the coffee object to the coffeeArray declared in SQLAppDelegate class. To do this we will declare a method called “add_Coffee” which will take a parameter of type Coffee and this method is called from “save_Clicked” method, which is called when the user clicks on the save button.

This is how the header file changes (Full code not shown)

//FileName: SQLAppDelegate.h
- (void) addCoffee:(Coffee *)coffeeObj;
...

addCoffee is implemented in SQLAppDelegate.m file and this is the code listing

- (void) addCoffee:(Coffee *)coffeeObj {

//Add it to the database.
[coffeeObj addCoffee];

//Add it to the coffee array.
[coffeeArray addObject:coffeeObj];
}

The first line calls the “addCoffee” method on the coffee object which we just created. The second line adds the object in the array.

Now we have to work with the Add View and its associated view controller.

Adding new UIView
Create a new view using the Interface Builder, I have named the view “AddView”. Add two labels and two text boxes as shown in the figure below. For the text fields, Capitalize is set to “Words”, Return Key is set to “Done” and set the Placeholder as “Coffee Name” and “Price” for the two respective text fields. You would set the properties in “Text Field Attributes” (Tools -> Inspector) using IB. Open “Connections Inspector” and create a connection from the delegate property to “File’s Owner” object, do the same for both the text boxes. I find it hard to explain what goes in IB, so here is a screen shot of how it should look like. The “Text Field Connections” applies to both the text boxes.



Creating a UIViewController
Create a new view controller (using Xcode), the name of my file is “AddViewController”. Create two variables of type UITextField with “IBOutlet” attribute so the variables show up in IB and create two methods called “save_Clicked” and “cancel_Clicked”. This is how the header file should look like

#import &lt;UIKit/UIKit.h&gt;

@class Coffee;

@interface AddViewController : UIViewController {

IBOutlet UITextField *txtCoffeeName;
IBOutlet UITextField *txtPrice;
}

@end

Now that we have defined “AddViewController” set the File’s Owner class as “AddViewController” in “Controller Identity”, below is a screen shot of that.

Also link the text fields declared in the view controller to the objects on the “AddView”, below is a screen shot of that

We are done with using Interface builder, feels good now that we can concentrate on code.

We now have to add two buttons “Cancel” and “Save” on the “UINavigationItem” of the “AddView”. Let us do this in “viewDidLoad” method and this how the code looks like

- (void)viewDidLoad {
[super viewDidLoad];

self.title = @"Add Coffee";

self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
target:self action:@selector(cancel_Clicked:)] autorelease];

self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemSave
target:self action:@selector(save_Clicked:)] autorelease];

self.view.backgroundColor = [UIColor groupTableViewBackgroundColor];
}

Everything is self explanatory, we set the title, add two buttons and set the background of the view by passing groupTableViewBackgroundColor message to UIColor.

Since the view has only two text fields, it will be easier if the keypad is presented to the user when the view is loaded. Lets do this in “viewWillAppear” method and this is how the code looks like

- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];

//Set the textboxes to empty string.
txtCoffeeName.text = @"";
txtPrice.text = @"";

//Make the coffe name textfield to be the first responder.
[txtCoffeeName becomeFirstResponder];
}

Here we always set the text property of the text fields to empty string and we make the keypad show up for the coffeeName text field by passing becomeFirstResponder message.

Since the user can click “Done” the keyboard should be hidden and the method which gets called is textFieldShouldReturn and this is how the code looks like

- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {

[theTextField resignFirstResponder];
return YES;
}

Please note that, I send “resignFirstResponder” message to the text field without finding out which textbox should be hidden. I do this because, the actual save happens in “save_clicked” method.

Before we look at “save_Clicked” method, this is how “cancel_Clicked” method looks like

- (void) cancel_Clicked:(id)sender {

//Dismiss the controller.
[self.navigationController dismissModalViewControllerAnimated:YES];
}

and this is how the “save_Clicked” method looks like

- (void) save_Clicked:(id)sender {

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

//Create a Coffee Object.
Coffee *coffeeObj = [[Coffee alloc] initWithPrimaryKey:0];
coffeeObj.coffeeName = txtCoffeeName.text;
NSDecimalNumber *temp = [[NSDecimalNumber alloc] initWithString:txtPrice.text];
coffeeObj.price = temp;
[temp release];
coffeeObj.isDirty = NO;
coffeeObj.isDetailViewHydrated = YES;

//Add the object
[appDelegate addCoffee:coffeeObj];

//Dismiss the controller.
[self.navigationController dismissModalViewControllerAnimated:YES];
}

We create a new coffee class and set all the properties, isDetailViewHydrated is set as YES because all the data is in memory and isDirty is set as NO because the row will be inserted in the database after setting all the properties. The view is dismissed by passing “dismissModalViewControllerAnimated” message to the receiver.

We still have to add the “Add” button to the “RootViewController” and add the code to show the “AddView”. This is how the header file changes for “RootViewController”

#import &lt;UIKit/UIKit.h&gt;

@class Coffee, AddViewController;

@interface RootViewController : UITableViewController {

SQLAppDelegate *appDelegate;
AddViewController *avController;
UINavigationController *addNavigationController;
}

@end

Do not forget to import “AddViewController.h” in “RootViewController.m” file. The “Add” button is added in the “viewDidLoad” method and this is how the code changes

- (void)viewDidLoad {
[super viewDidLoad];

self.navigationItem.rightBarButtonItem = self.editButtonItem;

self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self action:@selector(add_Clicked:)];

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

self.title = @"Coffee List";
}

When “Add” is clicked “add_Clicked” method is called and this is how the code looks like

- (void) add_Clicked:(id)sender {

if(avController == nil)
avController = [[AddViewController alloc] initWithNibName:@"AddView" bundle:nil];

if(addNavigationController == nil)
addNavigationController = [[UINavigationController alloc] initWithRootViewController:avController];

[self.navigationController presentModalViewController:addNavigationController animated:YES];
}

The reason we present the “AddView” using “addNavigationController” because when we want a custom UINavigationItem to show up on the AddView and that is why we initialize “addNavigationController” with “avController” and present it using “presentModalViewController”.

Run your application to insert data in the database.

There is however a problem with the design here, a User can click on Edit and nothing restricts him/her from clicking the add button. The code below fixes the issue

- (void)setEditing:(BOOL)editing animated:(BOOL)animated {

[super setEditing:editing animated:animated];
[self.tableView setEditing:editing animated:YES];

//Do not let the user add if the app is in edit mode.
if(editing)
self.navigationItem.leftBarButtonItem.enabled = NO;
else
self.navigationItem.leftBarButtonItem.enabled = YES;
}


The method setEditing is called when the edit button is clicked. We send the same message to the parent class and the tableview, also disable or enable the leftBarButtonItem if the tableview is in edit mode.

Conclusion
As we can see inserting data in SQLite databases is very easy to do.

Attachments

If you think information was helpful for you Please Donate


SQLite Tutorial – Adding data
Posted by Naeem Tagged with: , , , ,
Aug 262009

Deleting a row from UITableView
To delete data, we first need to delete the row from the table and then delete it from the UITableView, it is not necessary to do it in that order. Data can be deleted by using the edit button or by swiping your finger across the UITableViewCell.

When a row is deleted from the UITableView “commitEditingStyle” method is called which can be found in RootViewController.m.

This is how the source code looks like

- (void)tableView:(UITableView *)tv commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath {

if(editingStyle == UITableViewCellEditingStyleDelete) {

//Get the object to delete from the array.
Coffee *coffeeObj = [appDelegate.coffeeArray objectAtIndex:indexPath.row];
[appDelegate removeCoffee:coffeeObj];

//Delete the object from the table.
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}

We first check if the editingStyle is “UITableViewCellEditingStyleDelete” or not, if it is then we first delete the row from the database and then from the UITableView using “deleteRowsAtIndexPaths” method.

Since we have not yet implemented “removeCoffee” method, let’s do that now.

Deleting rows from the database
Declare “removeCoffee” in “SQLAppDelegate.h” file which takes a “Coffee” object as a parameter and returns void, the code looks like this

//Complete code listing not shown
- (void) removeCoffee:(Coffee *)coffeeObj;

The method does two things, sends a message to the Coffee Object to delete itself from the database and removes the object from the coffeeArray. The method is implemented in SQLAppDelegate.m file and looks like this

- (void) removeCoffee:(Coffee *)coffeeObj {

//Delete it from the database.
[coffeeObj deleteCoffee];

//Remove it from the array.
[coffeeArray removeObject:coffeeObj];
}

The first line, it sends a “deleteCoffee” message to the coffee object, let’s see how the code for that looks like

//Method is declared in the header file.
//Full code listing not shown
- (void) deleteCoffee;

“deleteCoffee” is implemented in SQLAppDelegate.m file.

- (void) deleteCoffee {

if(deleteStmt == nil) {
const char *sql = “delete from Coffee where coffeeID = ?”;
if(sqlite3_prepare_v2(database, sql, -1, &deleteStmt, NULL) != SQLITE_OK)
NSAssert1(0, @”Error while creating delete statement. ‘%s’”, sqlite3_errmsg(database));
}

//When binding parameters, index starts from 1 and not zero.
sqlite3_bind_int(deleteStmt, 1, coffeeID);

if (SQLITE_DONE != sqlite3_step(deleteStmt))
NSAssert1(0, @”Error while deleting. ‘%s’”, sqlite3_errmsg(database));

sqlite3_reset(deleteStmt);
}

The “deleteStmt” as a static variable whose type is sqlite3_stmt. Deceleration shown below

#import "Coffee.h"

static sqlite3 *database = nil;
static sqlite3_stmt *deleteStmt = nil;

@implementation Coffee

We first prepare the delete statement with the sql query “delete from Coffee where CoffeeID = ?”. The ‘?’ specifies that we have to pass a parameter to the statement, we do this using sqlite3_bind_int method since the parameter we have to pass is an int. Note that the index is specified as 1 and not 0 because when assigining parameters the index starts from 1 and not 0. sqlite_step is called to execute the delete statement and if it returns “SQLITE_DONE” it means that the row is deleted successfully. Click here to get a list of complete return codes by SQLite. We then reset the delete statement so it can be reused later, without having the need to build it.

Run your application and delete a row to test.

Conclusion
Deleting rows is very easy and straight forward. In my next tutorial, I will show you insert data in the database.

Attachments

If you think information was helpful for you Please Donate


SQLite Tutorial – Deleting Data
Posted by Naeem Tagged with: , , ,
Aug 262009

Introduction
Like any other applications you may have the need to store data in some kind of a database. For iPhone applications, we can use SQLite for free. SQLite is a software library that implements a self-contained, serverless, zero-configuration, transactional SQL database engine. Note: iPhone applications cannot work with remote databases.

In this tutorial, I will discuss how to create a new SQLite database and use it in a iPhone application.

This is how the application will look like
SQLite Manager
To use SQLite in your application, you do not have to install any new software on your mac. You can either use the command line to create a new SQL database or use SQLite Manager for Firefox add-on, which is what I use.

The screenshots below shows you the database schema used, which is very simple.

iPhone App
Create a new project by selecting Navigation-based application. The name of the project used for this tutorial is “SQL”. We first need to add a library which understands how to communicate with the SQLite database. In Xcode select “Frameworks” folder and click on the Action dropdown and select Add -> Existing Frameworks and browse to /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.1.sdk/usr/lib and select libsqlite3.0.dylib file and it will be added in the “Frameworks” folder.

Now add the SQLite database file to the “Resources” folder in Xcode.

Next we need to create a data structure to hold the data from the database. Create a new class and name it “Coffee”. This is how the class looks like.

#import &lt;UIKit/UIKit.h&gt;
#import &lt;sqlite3.h&gt;

@interface Coffee : NSObject {

NSInteger coffeeID;
NSString *coffeeName;
NSDecimalNumber *price;

//Intrnal variables to keep track of the state of the object.
BOOL isDirty;
BOOL isDetailViewHydrated;
}

@property (nonatomic, readonly) NSInteger coffeeID;
@property (nonatomic, copy) NSString *coffeeName;
@property (nonatomic, copy) NSDecimalNumber *price;

@property (nonatomic, readwrite) BOOL isDirty;
@property (nonatomic, readwrite) BOOL isDetailViewHydrated;

@end

Note that in the “Coffee” class the price variable is declared as “NSDecimalNumber” because in the database its corresponding column is of type “REAL”. Note: Although SQLite does not enforce data type constraints, it is good practice to enforce data type constraints at the application level. The two boolean variables keep track of the state of the object. The boolean “isDirty” tells us if the object was changed in memory or not and “isDetailViewHydrated” tell us, if the data which shows up on the detail view is fetched from the database or not.

NOTE: It is good practice to fetch only the data required to show on the screen, which makes the application load faster. So in our case, we will only get the primary key and the name of the coffee from the database as the price is only shown in the detail view.

Synthesize the properties and release price and coffeeName in the dealloc method. This is how the implementation file of class “Coffee” looks like

#import "Coffee.h"

@implementation Coffee

@synthesize coffeeID, coffeeName, price, isDirty, isDetailViewHydrated;

- (void) dealloc {

[price release];
[coffeeName release];
[super dealloc];
}

@end

Copying the database
The first thing we need to do when the application loads is to check whether the user’s phone has the database or not, if not then we copy it to the user’s phone. We will create two methods which will help us in copying the database to the user’s phone. This is how SQLAppDelegate header file will look like

#import &lt;UIKit/UIKit.h&gt;

@class Coffee;

@interface SQLAppDelegate : NSObject <UIApplicationDelegate> {

UIWindow *window;
UINavigationController *navigationController;

//To hold a list of Coffee objects
NSMutableArray *coffeeArray;
}

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

@property (nonatomic, retain) NSMutableArray *coffeeArray;

- (void) copyDatabaseIfNeeded;
- (NSString *) getDBPath;

@end

coffeeArray is the array used to hold all the “Coffee” objects. It is declared in the application delegate file and not anywhere else is because the object lives in memory as long as the application is running and it also gets a notification when the application is being terminated. The method copyDatabaseIfNeeded is used to copy the database on the user’s phone when the application is finished launching. Another method used with “copyDatabaseIfNeeded” is “getDBPath” which gets the database location on the user’s phone.

This is how “copyDatabaseIfNeeded” method looks like

- (void) copyDatabaseIfNeeded {

//Using NSFileManager we can perform many file system operations.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSString *dbPath = [self getDBPath];
BOOL success = [fileManager fileExistsAtPath:dbPath];

if(!success) {

NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@”SQL.sqlite”];
success = [fileManager copyItemAtPath:defaultDBPath toPath:dbPath error:&error];

if (!success)
NSAssert1(0, @”Failed to create writable database file with message ‘%@’.”, [error localizedDescription]);
}
}

and this is how the getDBPath method looks like

- (NSString *) getDBPath {

//Search for standard documents using NSSearchPathForDirectoriesInDomains
//First Param = Searching the documents directory
//Second Param = Searching the Users directory and not the System
//Expand any tildes and identify home directories.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
return [documentsDir stringByAppendingPathComponent:@"SQL.sqlite"];
}

In “copyDatabaseIfNeeded” method we get the NSFileManager object with which we can perform some basic file management tasks. The method “getDBPath” gives us the database location on the user’s phone. Method “NSSearchPathForDirectoriesInDomains” is used to find documents in the user’s documents directory expanding the tildes so we get the whole path. Using the “fileManager” object we check if the database exists or not, if it doesn’t exists then we copy it to the user’s phone from the application bundle. Method “copyDatabaseIfNeeded” is called from “applicationDidFinishLaunching”. Once the database is copied to the user’s phone, we need to display a list of coffee’s from the database on the UITableView.

Getting data from the database
The approach that I have taken here to get the data from the database is a little clean, as I have all my database operations in the Coffee Class. I have declared a class method in the “Coffee” class which is responsible to get the data from the database and fill the coffeeArray which is declared in the application delegate object (SQLAppDelegate).

This is how the header file of the “Coffee” class will look like after the method decelerations are added.

#import &lt;UIKit/UIKit.h&gt;
#import &lt;sqlite3.h&gt;

@interface Coffee : NSObject {

NSInteger coffeeID;
NSString *coffeeName;
NSDecimalNumber *price;

//Intrnal variables to keep track of the state of the object.
BOOL isDirty;
BOOL isDetailViewHydrated;
}

@property (nonatomic, readonly) NSInteger coffeeID;
@property (nonatomic, copy) NSString *coffeeName;
@property (nonatomic, copy) NSDecimalNumber *price;

@property (nonatomic, readwrite) BOOL isDetailViewHydrated;

//Static methods.
+ (void) getInitialDataToDisplay:(NSString *)dbPath;
+ (void) finalizeStatements;

//Instance methods.
- (id) initWithPrimaryKey:(NSInteger)pk;

@end

The method “getInitialDataToDisplay” gets the data from the database and creates “Coffee” objects using “initWithPrimaryKey” method and fills the objects in the coffeeArray which is declared in SQLAppDelegate.

This is how the “getInitialDataToDisplay” method looks like

+ (void) getInitialDataToDisplay:(NSString *)dbPath {

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

if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {

const char *sql = “select coffeeID, coffeeName from coffee”;
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {

while(sqlite3_step(selectstmt) == SQLITE_ROW) {

NSInteger primaryKey = sqlite3_column_int(selectstmt, 0);
Coffee *coffeeObj = [[Coffee alloc] initWithPrimaryKey:primaryKey];
coffeeObj.coffeeName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];

coffeeObj.isDirty = NO;

[appDelegate.coffeeArray addObject:coffeeObj];
[coffeeObj release];
}
}
}
else
sqlite3_close(database); //Even though the open call failed, close the database connection to release all the memory.
}

initWithPrimaryKey method is very simple and it looks like this

- (id) initWithPrimaryKey:(NSInteger) pk {

[super init];
coffeeID = pk;

isDetailViewHydrated = NO;

return self;
}

In “getInitialDataToDisplay” method we first get a reference to the application delegate because that is where the coffeeArray is declared. We open the database using sqlite3_open method which takes the database path and a database object. The database object is declared as static in the “Coffee.m” file.

#import "Coffee.h"

static sqlite3 *database = nil;

@implementation Coffee

After we open the connection to the database we create a select statement and if that returns “SQLITE_OK” we execute the select statement using sqlite3_step method, which will return “SQLITE_ROW” if the operation is a success and it has one or more rows to return. To get a full list of return codes click here. We then get the coffeeID, coffeename and create “Coffee” objects using “initWithPrimaryKey” method. When reading data from the database, the column index starts from zero instead of one. The coffee object is added to the array and we do this for n number of rows. If the connection to the database fails, only then we close the connection and release any resources associated with the connection object. The static database connection is left open to be used by the “Coffee” class.

We close the database connection in the “finalizeStatements” method which is called from “applicationWillTerminate” which is in SQLAppDelegate.m file.

+ (void) finalizeStatements {

if(database) sqlite3_close(database);
}

Now all we have to do is call the right methods from “applicationDidFinishLaunching” method in SQLAppDelegate.m file. This is how the code looks like

- (void)applicationDidFinishLaunching:(UIApplication *)application {

//Copy database to the user’s phone if needed.
[self copyDatabaseIfNeeded];

//Initialize the coffee array.
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
self.coffeeArray = tempArray;
[tempArray release];

//Once the db is copied, get the initial data to display on the screen.
[Coffee getInitialDataToDisplay:[self getDBPath]];

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

To recap, we first copy the database to the user’s phone if it does not exists, then we initialize the coffee array and get the initial data to display on the UITableView.

Display data in the UITableView
Since the Coffee Array is in the application delegate file (SQLAppDelegate), for simplicity I’ am going to add the “SQLAppDelegate.h” header file in “SQL_Prefix.pch” file so that the file is added to all the files in the project. This is how the file should look like

#ifdef __OBJC__
#import &lt;Foundation/Foundation.h&gt;
#import &lt;UIKit/UIKit.h&gt;
#import "SQLAppDelegate.h"
#endif

Open “RootViewController.h” file and create a variable of type SQLAppDelegate like this

#import &lt;UIKit/UIKit.h&gt;

@class Coffee;

@interface RootViewController : UITableViewController {

SQLAppDelegate *appDelegate;
}

@end

We are able to do the above because “SQLAppDelegate.h” file is added as a header file to all the files in the project.

Open “RootViewController.m” and import “Coffee.h” file, change the “viewDidLoad” method like this

- (void)viewDidLoad {
[super viewDidLoad];

self.navigationItem.rightBarButtonItem = self.editButtonItem;

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

self.title = @”Coffee List”;
}

We get a reference to the application delegate in the second line and everything is self explanatory. Using the appDelegate we can access the array.

Now all we have to do is tell the table view how many rows to expect and set the text property of the UITableViewCell which is returned in “cellForRowAtIndexPath” method.

Method “numberOfRowsInSection” returns the number of rows UITableView should expect in a section. By default “numberOfSectionsInTableView” returns 1, so the UITableView looks like a regular table.

Below is a code snippet of “numberOfRowsInSection”

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [appDelegate.coffeeArray count];
}

and cellForRowAtIndexPath

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

//Get the object from the array.
Coffee *coffeeObj = [appDelegate.coffeeArray objectAtIndex:indexPath.row];

//Set the coffename.
cell.text = coffeeObj.coffeeName;

// Set up the cell
return cell;
}

cellForRowAtIndexPat is called n number of times, where n is the total number of objects in the coffeeArray which is returned in “numberOfRowsInSection” method. We get the Coffee object from the array using “objectAtIndex” method and set the coffeeName as the text of the UITableViewCell.

Conclusion
This concludes part one of the SQLite Tuturial, where we learn how easy it is to manage databases using SQLite Manager and using it in your iPhone applications. Overall SQLite is FREE for all to use. You also learn how to copy database to the user’s phone and perform select operations on the database.

Attachments

If you think information was helpful for you Please Donate


SQLite Tutorial – Selecting Data
Posted by Naeem Tagged with: , , , ,
Aug 252009

These are instructions on how to unlock the iPhone 3GS for use with any GSM cellphone carrier using PurpleSn0w. Geohot has posted stating that the purplesn0w unlock should improve issues with wifi, battery, and the unlock itself. If you would prefer to use the iPhone Dev-Teams UltraSn0w unlock you can find those instructions here

Before you can follow these instructions you must have a jailbroken iPhone and you must be on the 04.26.08 baseband(modem firmware). This means that you must be running the 3.0 firmware and have used PwnageTool, RedSn0w, or PurpleRa1n to jailbreak. YOU MUST ALSO (FOR THE TIME BEING) HAVE ACTIVATED YOUR IPHONE WITH AN OFFICIAL APPLE CARRIER. NO HACTIVATION SUPPORT YET.

To find your firmware and modem firmware(baseband) versions you can follow this tutorial. If you are not on baseband version 04.26.08 then you need to follow one of these tutorials before unlocking: Mac, Windows

If you are on T-Mobile or any carrier without 3G remember to turn it off before starting…

Step One
Press to launch Cydia Installer from your SpringBoard.

Step Two
Press to select the Manage tab at the bottom of the screen.

Step Three
Press to select the large Sources button

Step Four
Press the Edit button at the top right of the screen.

Step Five
Press the Add button at the top left of the screen.

Step Six
Enter http://apt.geohot.com/ as the source url and press the Add Source button.

Step Seven
Once the source has been added press the large Return to Cydia button.

Step Eight
Press the Done button at the top right of the screen.

Step Nine
Press to select apt.geohot.com from the list of user entered sources.

Step Ten
Press to select com.geohot.purplesn0w from the list of packages

Step Eleven
Press the Install button at the top right of the screen.

Step Twelve
Press the Confirm button at the top right of the screen.

Step Thirteen
After installation completes successfully press the large Return to Cydia button.

Step Fourteen
Now press the Home button then power off and power on the iPhone. You do this by holding down the power button for 3 seconds then moving the power slider that appears to the right. Press the power button again to turn on the phone.

You should now be able to insert the SIM of your choice!

If you think information was helpful for you Please Donate


iPhone | How to Unlock the iPhone 3GS Using PurpleSn0w
Posted by Naeem Tagged with: , , , , , , , ,
Aug 252009

These are instructions on how to jailbreak your iPhone 3G on the 3.0.x firmware using RedSn0w for Mac. The Windows version of this tutorial is here. The iPhone 2G version of this tutorial is here

This tutorial can be used to jailbreak before applying the UltraSn0w unlock.

** If you would like to jailbreak on the 3.0.1 firmware you can follow these exact instructions; however, in Step Two restore using this 3.0.1 iPhone firmware file instead of the 3.0 one. Continue with the instructions as indicated.**

Step One
Create a folder on your desktop called Pwnage

Download RedSn0w from here and place it in the Pwnage folder. Likewise, download the latest 3.0 firmware from below and place it in the same folder.

3.0.0 (3G): iPhone1,2_3.0_7A341_Restore.ipsw

Extract the redsn0w application from the RedSn0w zip file to the Pwnage folder.

Step Two
Connect your iPhone to the computer and launch iTunes.

Select your iPhone from the list of devices on the left. Now hold down Option and click the Restore button. Restore is preferred as it won’t create any wasted space on your iPhone.

Navigate to the Pwnage folder on your desktop and select the firmware ipsw. Click the Open button to continue.

Step Three
Once iTunes has finished updating your iPhone to the desired firmware you may launch redsnw0w.app from the Pwnage folder on your desktop.

Step Four
Once RedSn0w opens click the Browse button

Step Five
Select the firmware ipsw we placed in the Pwnage folder on our desktop then click Open

Step Six
Once the firmware has been verified click the Next button to continue.

Step Seven
Make sure Cydia is selected and click the Next button.

Step Eight
Please plug your iPhone into the computer and make sure its OFF then click the Next button

Step Nine
RedSn0w will now guide you through the steps to get into DFU mode. You can find more help with DFU mode here

Hold down both the Home button and the Power button for 10 seconds.

Release the Power button and continue holding the Home button until RedSn0w detects the device.

Step Ten
Your iPhone will now reboot

Step Eleven
RedSn0w will then begin uploading the new RAM Disk.

Step Twelve
Once this is complete you will be notified that RedSn0w is done. Click the Finish button.
When your iPhone finishes reboot (5 minutes or so). It will be jailbroken with Cydia on the SpringBoard.



If you think information was helpful for you Please Donate


How to Jailbreak Your iPhone 3G on OS 3.0.x Using RedSn0w (Mac)
Posted by Naeem Tagged with:
Aug 252009

These are instructions on how to jailbreak your iPhone 3GS on the 3.0.x firmware using RedSn0w for Mac. The Windows version of this tutorial is here. The iPhone 3G version of this tutorial is here. The iPhone 2G version of this tutorial is here

This tutorial can be used to jailbreak before applying the UltraSn0w unlock. You can find instructions on how to apply the unlock here.

** If you would like to jailbreak on the 3.0.1 firmware you can follow these exact instructions; however, in Step Two restore using this 3.0.1 iPhone firmware file instead of the 3.0 one. Continue with the instructions as indicated.**

Step One
Create a folder on your desktop called Pwnage

Download RedSn0w from here and place it in the Pwnage folder. Likewise, download the latest 3.0 firmware from below and place it in the same folder.

3.0.0 (3GS): iPhone2,1_3.0_7A341_Restore.ipsw

Extract the redsn0w application from the RedSn0w zip file to the Pwnage folder.

Step Two
Connect your iPhone to the computer and launch iTunes.

Select your iPhone from the list of devices on the left. Now hold down Option and click the Restore button. Restore is preferred as it won’t create any wasted space on your iPhone.

Navigate to the Pwnage folder on your desktop and select the firmware ipsw. Click the Open button to continue.

*Note: If you are already on the 3.0 firmware and have not jailbroken then you may skip this step.

Step Three
Once iTunes has finished updating your iPhone to the desired firmware you may launch redsnw0w.app from the Pwnage folder on your desktop.

Step Four
Once RedSn0w opens click the Browse button

Step Five
Select the firmware ipsw we placed in the Pwnage folder on our desktop then click Open

Step Six
Once the firmware has been verified click the Next button to continue.

Step Seven
Make sure Cydia is selected and click the Next button.

Step Eight
RedSn0w will now instruct you on how to put your iPhone into recovery mode. Disconnect you iPhone 3GS from the computer then power it off. Then hold down the Home button while reconnecting the device to the computer. Don’t let go of the button until RedSn0w detects the iPhone and begins installation.

Step Nine
Your iPhone will now reboot

Step Ten
RedSn0w will then begin uploading the new RAM Disk and kernel.

Step Eleven
Once this is complete you will be notified that RedSn0w is done. Click the Finish button. When your iPhone finishes rebooting (5 minutes or so) it will be jailbroken with Cydia on the SpringBoard.

If you think information was helpful for you Please Donate


iPhone | How to Jailbreak Your iPhone 3GS on OS 3.0.x Using RedSn0w (Mac)
Posted by Naeem Tagged with: , , , , , , , , ,