Tuesday, February 22, 2011

Using SQLite in an iPhone App






This entry describes using SQLite in an iPhone app for displaying , updating & deleting data in the database

1.First of all create a database using SQLite Database browser


Add the database to your project.Also add libsqlite3.0.dylib to your frameworks




2.  In the AppDelegate class add the below code,

- (void)applicationDidFinishLaunching:(UIApplication *)application { 
[self createEditableCopyOfDatabaseIfNeeded];
[self initializeDatabase];

}

-(void) createEditableCopyOfDatabaseIfNeeded{
BOOL success;
NSFileManager *fileManager=[NSFileManager defaultManager];
NSError *error;
NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory= [paths objectAtIndex:0];
NSString *writableDBPath= [documentsDirectory stringByAppendingPathComponent:@"testt.sqlite"];
success= [fileManager fileExistsAtPath:writableDBPath ];
if(success) return;
NSString *defaultDBPath=[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"lovetarot.sqlite"];
success=[fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error: &error];
}


-(void) initializeDatabase{
NSMutableArray *todoArray=[[NSMutableArray alloc] init];
self.todos=todoArray;
[todoArray release];
NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory= [paths objectAtIndex:0];
NSString *path=[documentsDirectory stringByAppendingPathComponent:@"testt.sqlite"];
if(sqlite3_open ([path UTF8String], &database ) ==SQLITE_OK){
const char *sql="SELECT primaryKey FROM journals";
sqlite3_stmt *statement;
if(sqlite3_prepare_v2(database, sql, -1, &statement ,NULL) ==SQLITE_OK){
while(sqlite3_step(statement) == SQLITE_ROW ){
int primaryKey=sqlite3_column_int(statement,0);
//double date=sqlite3_column_int(statement,1);
GoddessInspiration *td=[[GoddessInspiration alloc] initWithPrimaryKey:primaryKey database:database];
[todos addObject:td];
[td release];
}
}
sqlite3_finalize(statement);
}else{
sqlite3_close(database);
}
}

3. Now , create NSObject class, lets name it test

4. In test.h add the below code,

#import <Foundation/Foundation.h>
#import <sqlite3.h>

@interface test : NSObject {
sqlite3 *database;
NSInteger primaryKey;
NSString *text;
NSString  *desc;
NSString *imagePath, *title;
double journalDate;
NSInteger priority;
NSInteger status;
BOOL dirty;
}


@property(assign,nonatomic,readonly) NSInteger primaryKey;
@property (nonatomic,retain) NSString *text;
@property (nonatomic,assign) double journalDate;
@property (nonatomic,retain) NSString *desc;
@property (nonatomic,retain) NSString *imagePath;
@property (nonatomic,retain) NSString *title;
@property (nonatomic)NSInteger priority;
@property (nonatomic)NSInteger status;

-(id)initWithPrimaryKey:(NSInteger )pk database:(sqlite3 *)db;


@end
5. In the test.m add the below code

#import "test.h"

static sqlite3_stmt *init_statement=nil;

@implementation test

@synthesize primaryKey,text,priority,status, desc,imagePath,title,journalDate;

-(id)initWithPrimaryKey:(NSInteger )pk database:(sqlite3 *)db{
  if(self = [super init] ) {
primaryKey=pk;
database =db;
if(init_statement ==nil){
const char *sql=" SELECT date,description,title,imagePath FROM journals WHERE primaryKey=? ";
if (sqlite3_prepare_v2(database, sql, -1, &init_statement, NULL) != SQLITE_OK){
NSAssert1(0,@"Error: Failed to prepare statement with message '%s'.",sqlite3_errmsg(database));
}
}
sqlite3_bind_int(init_statement,1,primaryKey);
if(sqlite3_step(init_statement) == SQLITE_ROW) {
//double journalDate;
self.journalDate=sqlite3_column_double(init_statement,0);
if(sqlite3_column_text(init_statement,1) != NULL)
self.desc=[ NSString stringWithUTF8String:(char *) sqlite3_column_text(init_statement,1)];
if(sqlite3_column_text(init_statement,3) != NULL)
self.imagePath=[NSString stringWithUTF8String:(char *) sqlite3_column_text(init_statement,3)];
if(sqlite3_column_text(init_statement,2) != NULL)
self.title=[NSString stringWithUTF8String:(char *) sqlite3_column_text(init_statement,2)];
}
else{
self.text=@"Nothing";
}
sqlite3_reset(init_statement);
}
return self;
}

@end


    Now displaying the data stored in the database
    

    test *temptest = (test *)[appDelegate.todos objectAtIndex:someindex];
NSInteger temp=temptest.primaryKey;

    temp will contain the value of primary key column, similarly you can get for other columns


  Deleting an entry from the database
1. In the app delegate add the below code,


-(void)removeEntry:(test *)tarot{

NSUInteger index=[todos indexOfObject:tarot];
if(index == NSNotFound) return;
[tarot deleteFromDatabase:database];
[todos removeObject:tarot];
}


2. In the test.m add the below code

bore @ implementation
static sqlite3_stmt *delete_statement=nil;

after @implementation



-(void)deleteFromDatabase:(sqlite3 *)database1{
if(delete_statement == nil){
const char *sql="DELETE FROM journals WHERE primaryKey=?";
if (sqlite3_prepare_v2(database1, sql, -1, &delete_statement, NULL) != SQLITE_OK){
NSAssert1(0,@"Error: Failed to prepare statement with message '%s'.",sqlite3_errmsg(database1));
}
}
sqlite3_bind_int(delete_statement,1, self.primaryKey);
int success = sqlite3_step(delete_statement);
if(success != SQLITE_DONE){
NSAssert1(0,@"Error: failed to save priority with message '%s'.",sqlite3_errmsg(database1));
}
sqlite3_reset(delete_statement);
}


3.Wherever you want to delete, add the below code

   test *tarot=[appDelegate.todos objectAtIndex:someindex];
   [appDelegate removeEntry:tarot];



 Inserting data into the database
1. In the app delegate file add the below code

-(void)addEntry:(test *)tarot {
[tarot insertNewTodoIntoDatabase:database];
[todos addObject:tarot];
}


2.In the test.m add the below code

before @ implementation
static sqlite3_stmt *insert_statement=nil;

after @implementation

-(NSInteger)insertNewTodoIntoDatabase:(sqlite3 *)database1{
database=database1;
if(insert_statement ==nil){
//NSString temp=td.textField.text
static char *sql="INSERT INTO journals (date,description,title,imagePath) VALUES (?,?,?,?)";
//static char *sql="INSERT INTO todo (text) VALUES (td.textFld.text)";
        if (sqlite3_prepare_v2(database1, sql, -1, &insert_statement, NULL) != SQLITE_OK){
NSAssert1(0,@"Error: Failed to prepare statement with message '%s'.",sqlite3_errmsg(database1));
}
}
sqlite3_bind_double(insert_statement,1,journalDate);
sqlite3_bind_text(insert_statement,2,[self.desc UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(insert_statement,3,[self.title UTF8String], -1, SQLITE_TRANSIENT); 
sqlite3_bind_text(insert_statement,4,[self.imagePath UTF8String], -1, SQLITE_TRANSIENT);
int success=sqlite3_step(insert_statement);
sqlite3_reset(insert_statement);
if(success != SQLITE_ERROR){
primaryKey = sqlite3_last_insert_rowid(database1);
return primaryKey;
}
NSAssert1(0,@"Error: failed to insert into the database with message '%s'.",sqlite3_errmsg(database1));
return -1;
}

3 Now, wherever you want to add the data into database add the below code

 test *test1 = [[test alloc] init];
[test1 setJournalDate:milliSecs];
[test1 setTitle:self.titleTextField.text];
[test1 setDesc:self.descrpnText.text];
[test1 setImagePath:@"NO"];
[appDelegate addEntry:test1];


 Updating data into the database
1.In the app delegate class, add the below code
-(void)updateEntry:(test *)tarot {
NSUInteger index=[todos indexOfObject:tarot];
if(index == NSNotFound) return;
[tarot updateIntoDatabase:database];
}
2. In the test.m add the below code
before @ implementation
static sqlite3_stmt *update_statement=nil;

after @implementation

-(NSInteger)updateIntoDatabase:(sqlite3 *)database1{
database=database1;
if(update_statement == nil){
const char *sql="UPDATE journals SET date=?, description=?, title=?, imagePath=? WHERE primaryKey=?";
if (sqlite3_prepare_v2(database1, sql, -1, &update_statement, NULL) != SQLITE_OK){
NSAssert1(0,@"Error: Failed to prepare statement with message '%s'.",sqlite3_errmsg(database1));
}
}
sqlite3_bind_int(update_statement,5, self.primaryKey);
sqlite3_bind_double(update_statement,1,journalDate);
sqlite3_bind_text(update_statement,2,[self.desc UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(update_statement,3,[self.title UTF8String], -1, SQLITE_TRANSIENT); 
sqlite3_bind_text(update_statement,4,[self.imagePath UTF8String], -1, SQLITE_TRANSIENT); 
int success = sqlite3_step(update_statement);
if(success != SQLITE_DONE){
NSAssert1(0,@"Error: failed to save with message '%s'.",sqlite3_errmsg(database1));
}
sqlite3_reset(update_statement);
return self.primaryKey;
}

3. Now after editing the entry , you need to save it in the database for that add the below code

 test *test1=[appDelegate.todos objectAtIndex:(someindex)];
[test1 setJournalDate:milliSecs];
[test1 setTitle:self.titleTextField.text];
[test1 setDesc:self.descrpnText.text];
[test1 setImagePath:imageValue];
[appDelegate updateTarot:test1];

Monday, February 21, 2011

Embedding youtube video in a webview


This post describes how you can embed you tube video in a webView which is in a table cell & on row selection autoplay youtube video

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

UIWebView  *wbView = (UIWebView *)[cell.contentView viewWithTag:1];
NSString *embedHTML = @"\
<html><head>\
<style type=\"text/css\">\
body {\
background-color: transparent;\
color: white;\
}\
</style>\
</head><body style=\"margin:0\">\
<embed id=\"yt\" src=\"%@\" type=\"application/x-shockwave-flash\" \
width=\"%0.0f\" height=\"%0.0f\"></embed>\
</body></html>";
NSString *html = [NSString stringWithFormat:embedHTML,url, 64.0, 64.0];
[wbView loadHTMLString:html baseURL:nil];



}

 "url" is the youtube video url


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [category_table cellForRowAtIndexPath:indexPath];
UIWebView  *wbView = (UIWebView *)[cell.contentView viewWithTag:1];
UIButton *btn = [self findButtonInView:wbView];
[btn sendActionsForControlEvents:UIControlEventTouchUpInside];
}


// detecting the play button on the you tube video thumbnail

- (UIButton *)findButtonInView:(UIView *)view {
UIButton *button = nil;
if ([view isMemberOfClass:[UIButton class]]) {
return (UIButton *)view;
}
if (view.subviews && [view.subviews count] > 0) {
for (UIView *subview in view.subviews) {
button = [self findButtonInView:subview];
if (button) return button;
}
}
return button;
}


- (void)webViewDidFinishLoad:(UIWebView *)_webView {
UIButton *button = [self findButtonInView:_webView];
[button sendActionsForControlEvents:UIControlEventTouchUpInside];
}


Displaying images from server in a table cell in a thread


We mostly use this concept when we need to load many images from server .Using this concept, the main Thread does not gets blocked till each image is loaded & user can interact with the UI. However, threading is used when you don't want your main thread to block till your aspected task is completed. 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

UIImageView *imgView= (UIImageView *)[cell.contentView viewWithTag:IMAGE_TAG];
id object = [imageDict objectForKey:[NSString stringWithFormat:@"%i,%i", indexPath.section, indexPath.row]];
if(!object){
[NSThread detachNewThreadSelector:@selector(displayingSmallImage:) toTarget:self withObject:indexPath];
}
else
{
if(![object isKindOfClass:[NSNull class]]){
UIImage *img = (UIImage *)object;
imgView.image = img;
}
}

}


imageDict is a global NSMutableDictionary


- (void) displayingSmallImage:(NSIndexPath *)indexPath{

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSString *imageUrl = [url to be used for fetching image from the server];
imageUrl = [(NSString *)CFURLCreateStringByAddingPercentEscapes(nil, (CFStringRef)imageUrl, NULL, NULL, kCFStringEncodingUTF8)autorelease];
NSURL *url = [NSURL URLWithString:imageUrl];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];

if(image){
[imageDict setObject:image forKey:[NSString stringWithFormat:@"%i,%i",indexPath.section,indexPath.row]];
}
else{
[imageDict setObject:[NSNull null] forKey:[NSString stringWithFormat:@"%i,%i",indexPath.section,indexPath.row]];
}

[self performSelectorOnMainThread:@selector(setImageInCell:) withObject:indexPath waitUntilDone:NO];
[pool release];

}


- (void)setImageInCell:(NSIndexPath *)indexPath{

[tableView_meals reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
}

Programmatically image resizing in an iPhone App




Resizing Image

UIImage *image = [entry videoImage];
UIImage *tempImage = nil;
CGSize targetSize = CGSizeMake(196,110);
UIGraphicsBeginImageContext(targetSize);
CGRect thumbnailRect = CGRectMake(0, 0, 0, 0);
thumbnailRect.origin = CGPointMake(0.0,0.0);
thumbnailRect.size.width  = targetSize.width;
thumbnailRect.size.height = targetSize.height;
[image drawInRect:thumbnailRect];
tempImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();


Now, you can use tempImage ( resized Image ) 

Playing video stored on a server in an iPhone App


- (void)viewDidLoad {

[super viewDidLoad];
[self playVideo:@"test url" frame:CGRectMake(20, 70, 280, 250)];
}


- (void)playVideo:(NSString *)urlString frame:(CGRect)frame {
NSString *embedHTML = @"\
<html><head>\
<style type=\"text/css\">\
body {\
background-color: transparent;\
color: white;\
}\
</style>\
<script>\
function load(){document.getElementById(\"yt\").play();}\
</script>\
</head><body onload=\"load()\"style=\"margin:0\">\
<video id=\"yt\" src=\"%@\" \
width=\"%0.0f\" height=\"%0.0f\" autoplay controls></video>\
</body></html>";
NSString *html = [NSString stringWithFormat:embedHTML, urlString, frame.size.width, frame.size.height];
UIWebView *videoView = [[UIWebView alloc] initWithFrame:frame];
[videoView loadHTMLString:html baseURL:nil];
[self.view addSubview:videoView];
[videoView release];
NSLog(@"%@",html);
}

Wednesday, February 16, 2011

Localizing an iPhone App




Introduction

Internationalization is the process of designing and building an application to facilitate localization. Localization, in turn, is the cultural and linguistic adaptation of an internationalized application to two or more culturally-distinct markets. When users launch a well-localized application, they should feel fully at home with it and not feel like it originated from some other country.
Internationalization and localization are complementary activities. By internationalizing an application, you create the programmatic infrastructure needed to support localized content. By localizing that application, you then add resources that are customized for people of a different culture

Steps below reflects localization only,


a. When app is closed & restarted
b. All the localized text is set using code not using your nib files

Steps for integrating Localization in an App


1. First of all add new file





Now you can see the added file in Xcode as shown in the picure below


2. We need to add localization to this file

For this,
a) Select .strings file
b) Right click on the file & select getInfo, you will see something like




3. Now, select the bottom button ( Make File Localization ), you can see the screen as shown in the picture below



4 . Now, select the General Tab on top (in the above picture)



5. Here you can add the languages you want to support, by clicking on bottom button ( Add Localization)
Now, you can see the files as shown in the picture below


6. Next step is to set text in the localized file

In the string file, add

/* Questions */ 
"Questions" = "test2";

/* Settings */
"Settings" = "test3";


Left part (Questions) act as key & right part act as corresponding value (test2)


7. Using localized text in the code,

For example you want to set a label's text, then use

lbl.text = NSLocalizedString(@"Questions", @"Questions");



As you can see in the above picture value for key "Questions" is " test2", so for English the label's text would be "test2"



8. In .main, add the below code

int main(int argc, char *argv[]) {
 [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 NSArray *arr = [[NSMutableArray alloc]initWithObjects: @"en",
 @"de",
 @"fr",
 @"es",
 @"it",
 @"ja",
 @"nl",
 @"pt",
 @"pt-PT",
 @"da",
 @"fi",
 @"nb",
 @"sv",
 @"ko",
 @"zh-Hans",
 @"zh-Hant",
 @"ru",
 @"pl",
 @"tr",
 @"uk", nil];

 if(![[[NSUserDefaults standardUserDefaults] objectForKey:@"Localization"] isEqualToString:@"NO"]){

 NSArray* preferredLangs = [NSLocale preferredLanguages]; 
 NSLog(@"%d",[preferredLangs count]);

 if (!([[preferredLangs objectAtIndex:0] isEqualToString:@"en"] 
 ||[[preferredLangs objectAtIndex:0] isEqualToString:@"es"] 
 ||[[preferredLangs objectAtIndex:0] isEqualToString:@"de"] 
 ||[[preferredLangs objectAtIndex:0] isEqualToString:@"fr"]
 ||[[preferredLangs objectAtIndex:0] isEqualToString:@"it"]

 )){

 [[NSUserDefaults standardUserDefaults] setObject:arr forKey:@"AppleLanguages"];
 }

 }else{
 [[NSUserDefaults standardUserDefaults] setObject:arr forKey:@"AppleLanguages"];
 }

 [arr release];
 int retVal = UIApplicationMain(argc, argv, nil, nil);
 [pool release];
 return retVal;
}

Above example supports english(en), spanish (es), German (de), French (fr) & italian (it)


9.Providing language change option, something like changing  language on row selection

Auto detects device default language.It is the first row in the code below

In the code,




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

 UITableViewCell *cell=[table cellForRowAtIndexPath:indexPath];
 UILabel *lbl=(UILabel *)[cell.contentView viewWithTag:101];
 NSUserDefaults *def = [NSUserDefaults standardUserDefaults];

 if(useLocalization == YES){

 NSIndexPath *indexPath1 = [NSIndexPath indexPathForRow:appDel.languageRow inSection:0];
 UITableViewCell *cell1=[tableView1 cellForRowAtIndexPath:indexPath1];
 UIImageView *imgView=(UIImageView *)[cell1.contentView viewWithTag:102];
 imgView.image=nil;

 NSArray *lang;

 if(indexPath.row == 0 || indexPath.row == 1 || indexPath.row ==2 || indexPath.row ==4 || indexPath.row == 3 || indexPath.row == 5){

 [appDel setLanguageRow:indexPath.row];

 [def setInteger:appDel.languageRow forKey:@"languageRow"];

 appDel.languageUsed = lbl.text;
 [def setObject:appDel.languageUsed forKey:@"languageUsed"];

 if(indexPath.row == 0){

 [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"AppleLanguages"];

 }

 if(indexPath.row != 0){

 if(indexPath.row == 1)

 lang = [NSArray arrayWithObjects:@"en", nil];

 else if (indexPath.row == 2)

 lang = [NSArray arrayWithObjects:@"de", nil];

 else if (indexPath.row == 3)

 lang = [NSArray arrayWithObjects:@"es", nil];

 else if (indexPath.row == 4)

 lang = [NSArray arrayWithObjects:@"fr", nil];

 else if (indexPath.row == 5)

 lang = [NSArray arrayWithObjects:@"it", nil];
 }

 if(indexPath.row !=0)
 [[NSUserDefaults standardUserDefaults] setObject:lang forKey:@"AppleLanguages"];

 }

 [tableView1 reloadData];

 [NSUserDefaults resetStandardUserDefaults];
 }

}

Please create the necessary variables in the app delegate.In the above code , I am just changing the language on row selection.