Creating our local iPhone database
The easiest way to create a SQLite database is to download the SQLite Firefox extension. This provides a GUI to create and manage any SQLite databases. Simply select "Database/New Database" from the menu and type the name JokeGenLocal as the name. You will be asked for a location of the database, i'd recommend not having it within your XCode Application, especially if ypu're using SVC for source control. I simply created the file in a folder called iWCFDemoDB at the same level as the original iWCFDemo project folder.
The local database format will mirror the server database format (created previously) as much as possible at this stage of development so I create two tables Joke and JokeCategory.
JokeCategory
Joke
We should also create indexes for these database table, but as our record set is very low at this stage I'm not going to do that.
Add the Database File to Your XCode Project
Now we have a database we want to include this as part of the XCode project. Right click the Resources folder in XCode and select "Add/Existing File..". Browse to the location where you saved the JokeGenLocal.sqlite file and click OK. I would not copy the file as local resource, but that decision is up to you.
Next we need to link to the library that contains all of the SQLite functions. To link to that library, from your XCode project right click the frameworks folder and select “Add/Existing Frameworks…”. Browse to the iPhoneSDK folder (mine was in/Developer/Patforms/iPhoneOS.platform/Developer/SDKs/iPkoneOS2.2.sdk/usr/lib/) and select the libsqlite3.0.dylib file.
Adding classes to xCode to hold access the database information
Once you have your database added to the application we need to create proxy classes which mirror the database structure and provide a mechanism for reading the records from the DB File. These are much the same as the Entity Framework classes which are created in Visual Studio, except Microsoft make it far easier for the developer. We also need a class that checks whether the iPhone has the database or not, if not then we copy it to the phone this will be the job of the SQLAppDelegate.
The boolean "isDirty" tells the application if the object was changed in memory or not and "isDetailViewHydrated" tell the application, if the data which shows up on the detail view is fetched from the database or not. Our application allows for very little in the way of editing at present however I'm leaving these in there as best practice and to assist in future development.
Create three new classes with headers and add the following code to each:
Category.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <sqlite3.h>
@interface Category : NSObject {
NSInteger categoryID;
NSString *categoryDescription;
//Intrnal variables to keep track of the state of the object.
BOOL isDirty;
BOOL isDetailViewHydrated;
}
@property (nonatomic, readonly) NSInteger categoryID;
@property (nonatomic, copy) NSString *categoryDescription;
@property (nonatomic, readwrite) BOOL isDirty;
@property (nonatomic, readwrite) BOOL isDetailViewHydrated;
@end
Category.m
#import "Category.h"
@implementation Category
@synthesize categoryID, categoryDescription, isDirty, isDetailViewHydrated;
- (void) dealloc {
[categoryDescription release];
[super dealloc];
}
@end
Joke.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <sqlite3.h>
@interface Joke : NSObject {
NSInteger jokeID;
NSInteger jokeCategory;
NSString *jokeText;
NSString *jokeSource;
NSString *jokeGraphic;
//Intrnal variables to keep track of the state of the object.
BOOL isDirty;
BOOL isDetailViewHydrated;
}
@property (nonatomic, readonly) NSInteger jokeID;
@property (nonatomic, readonly) NSInteger jokeCategory;
@property (nonatomic, copy) NSString *jokeText;
@property (nonatomic, copy) NSString *jokeSource;
@property (nonatomic, copy) NSString *jokeGraphic;
@property (nonatomic, readwrite) BOOL isDirty;
@property (nonatomic, readwrite) BOOL isDetailViewHydrated;
@end
Joke.m
#import "Joke.h"
@implementation Joke
@synthesize jokeID, jokeCategory, jokeText, jokeSource, jokeGraphic, isDirty, isDetailViewHydrated;
- (void) dealloc {
[jokeText release];
[jokeSource release];
[jokeGraphic release];
[super dealloc];
}
@end
SQLAppDelegate.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@class Category;
@class Joke;
@interface SQLAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
UINavigationController *navigationController;
//To hold a list of Category objects
NSMutableArray *categoryArray;
NSMutableArray *jokeArray;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
@property (nonatomic, retain) NSMutableArray *categoryArray;
@property (nonatomic, retain) NSMutableArray *jokeArray;
- (void) copyDatabaseIfNeeded;
- (NSString *) getDBPath;
@end
SQLAppDelegate.m
Creating the contracts
We know the format of the message to be sent and how it will be received from the previous run through and I've bold the important areas.
Client Sends:
WSGetAllRequest
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<GetJokes xmlns="http://tempuri.org/">
<wsJokeRequest>
<RefreshDate>2008-12-09T16:18:36.5706144 08:00</RefreshDate>
</wsJokeRequest>
</GetJokes>
</s:Body>
WSGetAllResponse
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<GetJokesResponse xmlns="http://tempuri.org/">
<GetJokesResult>
<Jokes>
<Joke>
<EntityKey>
<EntitySetName>Joke</EntitySetName>
<EntityContainerName>JokeGenDBEntities</EntityContainerName>
<EntityKeyValues>
<EntityKeyMember>
<Key>ID</Key>
<Value xsi:type="xsd:int">1</Value>
</EntityKeyMember>
</EntityKeyValues>
</EntityKey>
<ID>1</ID>
<Text>"Knock, Knock". "Who's there?", "Sarah", "Sarah Who?", "Sarah doctor in the house."</Text>
<JokeCategoryReference>
<EntityKey>
<EntitySetName>JokeCategory</EntitySetName>
<EntityContainerName>JokeGenDBEntities</EntityContainerName>
<EntityKeyValues>
<EntityKeyMember>
<Key>Category</Key>
<Value xsi:type="xsd:int">1</Value>
</EntityKeyMember>
</EntityKeyValues>
</EntityKey>
</JokeCategoryReference>
</Joke>
... clipped there will be many, many iterations or this .....
</Jokes>
</GetJokesResult>
</GetJokesResponse>
</s:Body>