How To Develop iOS Apps using SQLite Tutorials on developing iPhone and iPad database apps using SQLite

Kevin Languedoc

This book is for sale at http://leanpub.com/iossqlite

This version was published on 2019-02-24

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and many iterations to get reader feedback, pivot until you have the right book and build traction once you do.

© 2014 - 2019 Kevin Languedoc Tweet This Book!

Please help Kevin Languedoc by spreading the word about this book on Twitter! The suggested hashtag for this book is ##iossqliteapps. Find out what other people are saying about the book by clicking on this link to search for this hashtag on Twitter: ##iossqliteapps To all the programmers who struggle day in, day out to write great iOS apps Contents

Updating an iOS SQLite Database ...... 1 SQLite Queries ...... 1 Develop the SQLite iPhone Application ...... 4 Updating an iOS SQLite Database

This chapter is about modifying an existing SQLite database during runtime. It is closely related to the first chapter on creating because I am demonstrating how to add tables, columns, indexes and triggers and not just modifying them. I wanted to group these operations together in their own chapter. SQLite offers some unique capabilities to modify an existing database. What makes SQLite stand out amongst database engines in my opinion is its ability to allow an user to modify a database during runtime which can be very useful. To demonstrate how to modify a database at runtime, I created a sample iOS iPhone application using the Single View template. The UI is very primitive but I wanted to focus on the SQLite queries and how to set them up to be able to modify a database. Another liberty that I am taking is that I have hard coded a lot of new column names, table names, indexes, triggers, and so on that I use to create these new objects in the SQLite database. The objective was not to build a complete database manager app but demonstrate how to setup the queries. In this sample app, I provide the code to alter a table by showing how to add a column and how to rename a table, both of which are part of the Alter Table statement. I am also providing the code to create a table, trigger, column, and view. Likewise, you will find code to drop (delete, remove) a table, view, trigger or index and even the database itself which, technically speaking, is not part of the SQLite SQL api. To manage the SQLite SQL query operations, I created a custom class as a sub-class of the NSObject. This custom class is called DatabaseOperations. I built the query selection process using blocks instead of the switch statement. The reason is because I wanted to be able to refer the selected queries by name instead by number (integer) which is the required parameter data type for the switch statement in . Objective-C doesn’t have a switch statement. Before I get into the actual application and how it is built, I thought it might be a good idea to provide the queries of each of the SQLite operations to modify a database. These are provided in the next section.

SQLite Queries

I took the liberty to hardcode the names of the objects to be modified, preferring to focus on how to setup the query for each type of possible modification instead. In a production environment, the hardcoded values would be replaced with variables with the new value. All the queries use the NSString class to store the query string because I find it easier to manage concatenation than using the C based const char * variable name design. In the following code I am providing just the example queries, in the next section I will demonstrate how to use them. Updating an iOS SQLite Database 2

Alter Table

1 NSString*(^at)(void) = ^{ 2 NSString * alterTbl = [[NSString alloc]init]; 3 alterTbl = @"AlterTabletodoTblRenametobar "; 4 return alterTbl; 5 };

Add Column

1 NSString *(^ac)(void)=^{ 2 NSString * addCol = [[NSString alloc]init]; 3 addCol = @"AlterTablebarAddcolumnbazCHAR(50) "; 4 return addCol; 5 };

Create Table

1 NSString *(^ct)(void)=^{ 2 NSString *addTbl = [[NSString alloc]init]; 3 addTbl = @"createtableifnotexiststodoTbl(todoNamevarchar, 4 todoDescriptionvarchar,todoDatevarchar) "; 5 return addTbl; 6 };

Create Index

1 NSString *(^ci)(void)= ^{ 2 NSString *addIn =[[NSString alloc]init]; 3 addIn = @"Createindexifnotexistsfooindexonbar(todoName) "; 4 return addIn; 5 };

Create Trigger Updating an iOS SQLite Database 3

1 NSString*(^ctrg)(void)=^{ 2 NSString *addTrig =[[NSString alloc]init]; 3 addTrig = @"Create trigger if not exists baztrig after insert on bar begin i\ 4 nsert 5 into todoTblLog(todoName,todoDescription, Time) values('foo','bar', 6 datetime('now')); end"; 7 return addTrig; 8 };

Create View

1 NSString *(^cv)(void)=^{ 2 NSString *addVw =[[NSString alloc]init]; 3 addVw = @"Create view if not exists aView as Select todoName, todoDescriptio\ 4 n 5 from bar"; 6 return addVw; 7 };

Drop Database

See the DeleteDatabase method section in this chapter.

Drop Table

1 NSString*(^dt)(void)= ^{ 2 NSString *dropTbl = [[NSString alloc]init]; 3 4 dropTbl = @"drop if exist bar "; 5 return dropTbl; 6 };

Drop Index Updating an iOS SQLite Database 4

1 NSString*(^di)(void)=^{ 2 NSString *dropIndex = [[NSString alloc]init]; 3 4 dropIndex = @"drop if exists fooIndex"; 5 return dropIndex; 6 };

Drop Trigger

1 NSString*(^dtrg)(void)= ^{ 2 NSString *dropTrig = [[NSString alloc]init]; 3 dropTrig = @"drop if exists baztrig"; 4 return dropTrig; 5 };

Drop View

1 NSString*(^dv)(void) = ^{ 2 NSString *dropView = [[NSString alloc]init]; 3 dropView = @"drop if exists aView"; 4 return dropView; 5 };

Develop the SQLite iPhone Application

Since the purpose of the application is to show you how to write the code to be able to modify a SQLite the UI has limited functionality and I focus primarily on the interaction between the SQLite engine, the queries and the controller. Let’s start by creating a new iOS Single View iPhone app. You can get that set up through Xcode list of app templates by either selecting that template from the new project dashboard or selecting New Project from the Xcode menu. Once the project is created, add the SQLite library through the Linked Libraries and Frameworks section in the Project Summary page. For newbies, select the project root in the project explorer and the right side of the IDE, or main window, this is the Project Summary page. Scroll to the bottom and you will find the Linked Library and Frameworks section. By clicking on the “+” button, a search panel will appear allowing you to enter the “sqlite3” search term. Select the sqlite3.dylib. Updating an iOS SQLite Database 5

Create the Model

To handle the database operations and the interactions with the actually databases, I created a NSObject subclass called DatabaseOperations. This object provides the implementation of the SQLite queries. As you see from the code listing below on the header file, you need to import the SQLite3 library using the import statement followed by the name of the library in angle brackets. Since I will need a pop a warning message and I will need to implement the UIAlert. I need to the UIAlertViewDelegate protocol which provides the interactivity to the UIAlertView. Next I declare a sqlite3 variable which will be our main conduit to the SQLite database engine. I wanted to use constants to declare the various database operations, so I have declare these constants using the FOUNDATION_EXPORT which is part of the NSObjCRuntime class. Mind you, I could have used the extern directive in C. I will use these later in the implementation with a Block which is a closure function or an if you prefer. If you want to be really well organized and will need constants in different parts of the app, you could place all these constants in constants.h file and implementation object. Most of the operations happen inside a SQLite database but we need a method to allow us to create databases. So the BuildDatabase method will take a database name string as input and use that value to create database that will be used for our modifications at runtime. The next method, ExecuteDatabaseOperation, will implement the Block code in order to select the proper query and launch the ModifyDatabase method. You will notice that these methods don’t have any facilities to receive or pass the values of the objects to the changed. I did this on purpose to keep the example simple. In a real app you would need to provide a parameter or parameters to allow a user to provide the values for the requested changes. The blocks are added to a NSDictionary object. The other methods include the DeleteDatabase will be use to delete the database file. The Modify- Database will be used to get the Sqlite database object, get the database path and execute the selected query from the ExecuteDatabaseOperation once the database is opened. I took the liberty of hard coding the database name instead of using the database para- meter. To populate the UIPickerView, I created the GetDatabaseList method that returns a list of SQLite database in the Documents path. The last method, WarningMessage will display the UIAlertView when needed.

1 // 2 // DatabaseOperations.h 3 // UpdateSQLiteDatabase 4 // 5 // CreatedbyKevinLanguedocon4/28/14. 6 // Copyright(c)2014KevinLanguedoc.Allrightsreserved. 7 // 8 9 #import 10 #import 11 Updating an iOS SQLite Database 6

12 @interface DatabaseOperations : NSObject 13 { 14 sqlite3 *db; 15 16 } 17 FOUNDATION_EXPORT NSString *const ERROR_OCCURRED; 18 FOUNDATION_EXPORT NSString *const ERROR_BAD_STMT; 19 FOUNDATION_EXPORT NSString *const DATABASE_EXIST; 20 FOUNDATION_EXPORT NSString *const ALTER_DATABASE; 21 FOUNDATION_EXPORT NSString *const ALTER_TABLE; 22 FOUNDATION_EXPORT NSString *const ADD_COLUMN; 23 FOUNDATION_EXPORT NSString *const CREATE_DATABASE; 24 FOUNDATION_EXPORT NSString *const CREATE_TABLE; 25 FOUNDATION_EXPORT NSString *const CREATE_INDEX; 26 FOUNDATION_EXPORT NSString *const CREATE_VIEW; 27 FOUNDATION_EXPORT NSString *const CREATE_TRIGGER; 28 FOUNDATION_EXPORT NSString *const DROP_DATABASE; 29 FOUNDATION_EXPORT NSString *const DROP_TABLE; 30 FOUNDATION_EXPORT NSString *const DROP_INDEX; 31 FOUNDATION_EXPORT NSString *const DROP_VIEW; 32 FOUNDATION_EXPORT NSString *const DROP_TRIGGER; 33 34 -(void)BuildDatabase:(NSString*)DatabaseName; 35 36 -(NSString *)ExecuteDatabaseOperation : (NSString*) 37 databaseName :(NSString*)operation; 38 39 -(void)WarningMessage :(NSString*)warning; 40 41 -(void)ModifyDatabase:(NSString*)databaseName : (NSString*)query ; 42 43 -(void)DeleteDatabase: (NSString*)dataName; 44 45 -(NSMutableArray*)GetDatabaseList; 46 @end

The implementation is very straightforward. The first thing I will do is initialize the constants. To do this, I use the NSString *const statement followed by the name of the constant and its assigned value as in the code below. These constants can be used anywhere the DatabaseOperations is used as you will see later in the ViewController. The first method I will implement is the -(void)BuildDatabase:(NSString*)DatabaseName. This method creates a new database. The code is very similar in design to the tutorials in chapter 1. Updating an iOS SQLite Database 7

Although I have datababseName as a parameter in the signature that can be used to pass in the value of the new database, I am assuming for the sake of simplicity that the database name is “test” and the extension is “db”. The code obtains the path for the Documents directory because this is the main read/write directory in an app’s sandbox which it’s own unique filesystem. Then I set the path as a string to the new database using the the first element in the array of folders in the Documents path. If your app has multiple sub-folders under the Documents path, you will need to make sure that you are getting the right folder. A possible solution is to set a db sub-folder and reserve it for the databases. With the path to the database in hand the database is created using the sqlite3_open function and it will opened at the same time. If you call this method a second time, the database will be re-opened. The code won’t overwrite the database file. The sqlite3_open function has a dual purpose.

1 // 2 // DatabaseOperations.m 3 // Update SQLite Database 4 // 5 // Created by Kevin Languedoc on 4/28/14. 6 // Copyright (c) 2014 Kevin Languedoc. All rights reserved. 7 // 8 9 #import "DatabaseOperations.h" 10 11 @implementation DatabaseOperations 12 NSString *const ERROR_OCCURRED= @"An error has occurred"; 13 NSString *const ERROR_BAD_STMT = @"There is a problem with statement"; 14 NSString *const DATABASE_EXIST=@"This database already exist"; 15 NSString *const ALTER_DATABASE=@"Alter Database"; 16 NSString *const ALTER_TABLE=@"Alter Table"; 17 NSString *const ADD_COLUMN=@"Add Column"; 18 NSString *const ALTER_VIEW=@"Alter View"; 19 NSString *const ALTER_TRIGGER=@"Alter Trigger"; 20 NSString *const ALTER_INDEX=@"Alter Index"; 21 NSString *const ALTER_COLUMN=@"Alter Column"; 22 NSString *const CREATE_DATABASE=@"Create Database"; 23 NSString *const CREATE_TABLE=@"Create Table"; 24 NSString *const CREATE_INDEX=@"Create Index"; 25 NSString *const CREATE_VIEW=@"Create View"; 26 NSString *const CREATE_TRIGGER=@"Create Trigger"; 27 NSString *const DROP_DATABASE=@"Drop Database"; 28 NSString *const DROP_TABLE=@"Drop Table"; 29 NSString *const DROP_INDEX=@"Drop Index"; 30 NSString *const DROP_VIEW=@"Drop View"; Updating an iOS SQLite Database 8

31 NSString *const DROP_TRIGGER=@"Drop Trigger"; 32 33 -(void)BuildDatabase:(NSString*)DatabaseName{ 34 35 BOOL fileExist; 36 37 //Get list of directories in Document path 38 NSArray * dirPath = NSSearchPathForDirectoriesInDomains 39 (NSDocumentDirectory, 40 NSUserDomainMask, YES); 41 42 //Define new path for database 43 44 /*NSString * documentPath = [[[dirPath objectAtIndex:0] 45 stringByAppendingPathComponent:DatabaseName ] 46 stringByAppendingString:@".db"];*/ 47 48 /* 49 for the sake of simplicity of simplicity 50 */ 51 NSString * documentPath = [[[dirPath objectAtIndex:0] 52 stringByAppendingPathComponent:@"test" ] 53 stringByAppendingString:@".db"]; 54 55 if(!(sqlite3_open([documentPath UTF8String], &db) == SQLITE_OK)) 56 { 57 NSLog(@"%@",ERROR_OCCURRED); 58 [self WarningMessage:ERROR_OCCURRED]; 59 60 } 61 62 63 64 }

The -(void)ExecuteDatabaseOperation : (NSString)databaseName : (NSString)operation method is very interesting because I am using a Block to emulate a switch statement. As you may know Objective-C doesn’t have a switch statement. Since Objective-C is a superset of the C language, you can use any part of the C language directly in Objective-C. In the C language, the expression in the switch statement needs an integer value, however I want to use a string value which is the value that appears in the UIPickerView in the UI. So I am using Blocks, which is a closure construct, instead. The Blocks contain individual queries for each database operation. Each query uses NSString to construct the query string and it is returned to the calling sqlite3_exec function in the ModifyDatabase method. Updating an iOS SQLite Database 9

1 -(NSString *)ExecuteDatabaseOperation : (NSString*)databaseName 2 :(NSString*)operation{ 3 4 NSString*(^at)(void) = ^{ 5 NSString * alterTbl = [[NSString alloc]init]; 6 alterTbl = @"Alter Table todoTbl Rename to bar"; 7 return alterTbl; 8 }; 9 10 // 11 12 NSString *(^ac)(void)=^{ 13 NSString * addCol = [[NSString alloc]init]; 14 addCol = @"Alter Table bar Add column baz CHAR(50)"; 15 return addCol; 16 }; 17 18 // 19 NSString *(^ct)(void)=^{ 20 NSString *addTbl = [[NSString alloc]init]; 21 addTbl = @"create table if not exists todoTbl(todoName varchar, 22 todoDescription varchar, todoDate varchar)"; 23 return addTbl; 24 }; 25 // 26 27 NSString *(^cv)(void)=^{ 28 NSString *addVw =[[NSString alloc]init]; 29 addVw = @"Create view if not exists aView as Select todoName, 30 todoDescription from bar"; 31 return addVw; 32 }; 33 34 //----- 35 36 NSString *(^ci)(void)= ^{ 37 NSString *addIn =[[NSString alloc]init]; 38 addIn = @"Create index if not exists fooindex on bar(todoName)"; 39 return addIn; 40 }; 41 42 NSString*(^ctrg)(void)=^{ 43 NSString *addTrig =[[NSString alloc]init]; Updating an iOS SQLite Database 10

44 addTrig = @"Create trigger if not exists baztrig after insert on 45 bar begin insert into todoTblLog(todoName,todoDescription, Time) 46 values('foo','bar', datetime('now')); end"; 47 return addTrig; 48 }; 49 50 NSString*(^dt)(void)= ^{ 51 NSString *dropTbl = [[NSString alloc]init]; 52 53 dropTbl = @"drop if exists todoTbl "; 54 return dropTbl; 55 }; 56 57 58 NSString*(^di)(void)=^{ 59 NSString *dropIndex = [[NSString alloc]init]; 60 61 dropIndex = @"drop if exists fooIndex"; 62 return dropIndex; 63 }; 64 65 NSString*(^dtrg)(void)= ^{ 66 NSString *dropTrig = [[NSString alloc]init]; 67 dropTrig = @"drop if exists baztrig"; 68 return dropTrig; 69 }; 70 71 72 NSString*(^dv)(void) = ^{ 73 NSString *dropView = [[NSString alloc]init]; 74 dropView = @"drop if exists aView"; 75 return dropView; 76 }; 77 78 79 NSDictionary* selectOps = [[NSDictionary alloc] 80 initWithObjectsAndKeys: 81 at(), ALTER_TABLE, 82 ac(), ALTER_COLUMN, 83 ct(), CREATE_TABLE, 84 cv(), CREATE_VIEW, 85 ci(), CREATE_INDEX, 86 ctrg(), CREATE_TRIGGER, Updating an iOS SQLite Database 11

87 dt(), DROP_TABLE, 88 di(), DROP_INDEX, 89 dtrg(), DROP_TRIGGER, 90 dv(), DROP_VIEW, 91 nil]; 92 93 // if (selectOps != nil) 94 return [selectOps objectForKey:operation]; 95 96 97 }

The -(void)ModifyDatabase: (NSString)databaseName : (NSString)query is very simple and can be used as is in a production grade app, or almost once you build in some error handling. This method uses the sqlite3_exec SQLite 3 function can encapsulates all the necessary operations needed to perform a SQLite query. If the function can’t perform the operation and get a successful return code, the statement will display an error message using the -(void)WarningMessage:(NSString *)warning method which implements the UIAlertView object from the CocoaTouch UIKit framework.

1 -(void)ModifyDatabase:(NSString*)databaseName 2 :(NSString*)query{ 3 4 BOOL fileExist; 5 NSArray * dirPath = NSSearchPathForDirectoriesInDomains 6 (NSDocumentDirectory, NSUserDomainMask, YES); 7 NSString * documentPath = [[[dirPath objectAtIndex:0] 8 stringByAppendingPathComponent:@"test" ] 9 stringByAppendingString:@".db"]; 10 11 12 13 14 15 if((sqlite3_open([documentPath UTF8String], &db) == SQLITE_OK)) 16 { 17 char *emsg; 18 NSString* queryStr = [[NSString alloc]init]; 19 queryStr = [self ExecuteDatabaseOperation:databaseName :query]; 20 21 if(sqlite3_exec(db, [[self ExecuteDatabaseOperation 22 :databaseName :query] UTF8String], NULL, NULL, &emsg) != SQLITE_OK) 23 { 24 NSLog(@"%s",emsg); Updating an iOS SQLite Database 12

25 [self WarningMessage:ERROR_BAD_STMT]; 26 27 } 28 29 }else{ 30 NSLog(@"%@",ERROR_OCCURRED); 31 [self WarningMessage:ERROR_OCCURRED]; 32 } 33 34 }

The DeleteDatabase has a databaseName in it’s signature which it uses to lookup the database in the Documents directory. Then the method uses the removeItemAtPath method in the NSFileManager class to delete the file. The GetDatabaseList uses the contentsOfDirectoryAtPath method in the NSFileManager to get all the database files in the Documents directory. I am making a couple of assumptions here. First, I assume that all the database files are in the root of the Documents directory and second, I am assuming that there is only database files in Documents directory. The solution here is to use a NSPredicate to build a filter string and use the filteredArrayUsingPredicate method in the NSArray class.

1 -(void)DeleteDatabase:(NSString *)dataName{ 2 NSFileManager *fileManager = [NSFileManager defaultManager]; 3 NSArray *paths = NSSearchPathForDirectoriesInDomains 4 (NSDocumentDirectory, 5 NSUserDomainMask, YES); 6 NSString *documentsPath = [paths objectAtIndex:0]; 7 NSString *filePath = [documentsPath 8 stringByAppendingPathComponent:[NSString 9 stringWithFormat:@"%@", dataName]]; 10 [fileManager removeItemAtPath:filePath error:NULL]; 11 } 12 13 14 -(NSMutableArray*)GetDatabaseList{ 15 NSError * err; 16 NSMutableArray * databaseList=[[NSMutableArray alloc]init]; 17 NSArray* dbarr = [[NSArray alloc] init]; 18 NSArray * dirPath = NSSearchPathForDirectoriesInDomains 19 (NSDocumentDirectory, NSUserDomainMask, YES); 20 21 //returns an array of files at a defined path. For the sake of simplicity Updating an iOS SQLite Database 13

22 I am not handling any potential errors. 23 dbarr = [[NSFileManager defaultManager] contentsOfDirectoryAtPath 24 :[dirPath objectAtIndex:0] error:&err]; 25 26 databaseList = [[NSMutableArray alloc] initWithArray:dbarr]; 27 [databaseList addObject:@""]; 28 29 return databaseList; 30 31 }

The final method we will implement is the -(void)DeleteDatabase:(NSString *)dataName. This method doesn’t execute any SQL query code. Instead it deletes the database file. It is called through DoDatabaseOperation method in the view controller. Since you can’t detach a SQLite database like with other database engines, the NSFileManager is called to delete the file in the Documents directory or one of its sub-folders if you choose that design option in your code.

1 -(void)WarningMessage:(NSString *)warning{ 2 UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Warning" 3 message:warning 4 delegate:self 5 cancelButtonTitle:@"OK" 6 otherButtonTitles:nil]; 7 [alert show]; 8 } 9 10 11 @end

This completes the necessary code to handle all available database related update queries. However as I said before, I have hard coded the values in the queries but in a production app you would need to allow the user the supply the values to modify the database.

Create the Controller

The next layer up in the MVC programming pattern that all iOS apps are built on is the Controller which is implemented through the standard ViewController sub-class of the UIViewController class. I will use the ViewController object that was created by default when I chose the Single View app template. The first code listing below is the header file. Since we will need to implement the DatabaseOperations class to create a new object I will add an import statement for the DatabaseOperations header file. Updating an iOS SQLite Database 14

Then we will need to use the UIAlertView object and the UIPickerView, so we will add the UIAlertViewDelegate protocol and the UIPickerViewDataSource protocols. The first will allow us to add a data source in the form of an array (NSArray or NSMutableArray or a dictionary NSDictionary). The second, UIPickerViewDelegate, provides the required methods to animate the UIPickerViews. The dbOperations NSArray is the data source for the database operations pick list. The databases NSMutableArray is the data source for the database pick list. The dbOperation DatabaseOperations’ variable provides the implementation of that class. The selectedDatabase and selectedOperation variables contain the values that will be selected from the corresponding pick list when the app is running. The databasePickerView and the operationsPickerView are IBOutlets which are connections between the UI elements in the storyboard that will be defined in the next section. This also applies to the IBAction, updateDatabase, which creates an interface between the UIButton in the UI and the corresponding View Controller. Take a look at the following screenshot for a reference. Notice the filled in dots in the margin next to these properties. This indicates that the IBAction and IBOutlets are connected (interfaced) with the underlying ViewController object. I will explain how to set this up in the next section for any newbies. The last element to add is the DoDatabaseOperation method which will implement to use the database operations.

Figure 1: IBOutlet and IBAction Connections Updating an iOS SQLite Database 15

1 // 2 // ViewController.h 3 // Update SQLite Database 4 // 5 // Created by Kevin Languedoc on 4/27/14. 6 // Copyright (c) 2014 Kevin Languedoc. All rights reserved. 7 // 8 9 #import 10 #import "DatabaseOperations.h" 11 12 @interface ViewController : UIViewController 14 15 16 @property(nonatomic, strong) NSArray * dbOperations; 17 @property(nonatomic, strong) NSMutableArray *databases; 18 @property(nonatomic, strong) DatabaseOperations *dbOperation; 19 @property(nonatomic, strong) NSString * selectedDatabase; 20 @property(nonatomic, strong) NSString * selectedOperation; 21 22 @property (strong, nonatomic) IBOutlet UIPickerView *databasePickerView; 23 24 @property (strong, nonatomic) IBOutlet UIPickerView *operationsPickerView; 25 26 -(void)DoDatabaseOperation:(NSString*)database :(NSString*)ops; 27 -(IBAction)updateDatabase:(id)sender; 28 29 @end

The first thing we need to do is create the getter setter methods which is accomplished using the @syntheize directive. You can create the properties like I have in the code below, or you can create them individually each on their own line and assign a different name like @synthesize dbOperations = _ dbOps;. I prefer the first way because it is easier to keep track of the properties in larger apps. The database operations data source, dbOperations, will get populated when the app is loaded through the viewDidLoad method. You will notice from the code below that I am using the constants from the DatabaseOperations class. The UIPickView protocols have some mandatory methods. These are numberOfComponentsInPickerView, ** numberOfRowsInComponent, ** titleForRow and ** didSelectRow**. We will look at each of these next along with the DoDatabaseOperation and UpdateDatabase. The numberOfComponentsInPickerView lets you set the number of columns your pick list will have. You can hardcode this value or set it dynamically. For this example, I am setting both Updating an iOS SQLite Database 16

UIPickListViews to 1 column. The numberOfRowsInComponent method allows us to set the number of rows each UIPickListView will have. Since we have two pick list, I have set the tag value to 1 and 2 by selecting each UIPickerView in turn and setting the tag property in the Attribute Inspector which is available from the open Interface Builder and depending depending which of the two is being referenced, I will set the appropriate UIPickerList according to the number of elements in the arrays of their respective data source. You don’t have to explicitly call these methods. Since we implemented the protocols, these methods get called automatically when the app is running. The titleForRow follows the same pattern as the previous method except that I am referencing the each value in the arrays. This displays the actual values in the UIPickListView. The didSelectRow is called when the user makes a selection in either of the pick list. The selected value will be stored in one of two variables, selectedDatabase or selectedOperation, that we will later use as input to let the DatabaseOperations object know which SQL operations to perform on which SQLite database. The DoDatabaseOperation simply determines which DatabaseOperations method to call based on the selectedOperations value. To create a database and delete a database, I chose to use a dedicated method because the logic didn’t follow the same pattern as the other modification operations. At the start of the method, I initialize a DatabaseOperations object called dbOperation which I created in the header file. I then check to see if the ops parameter is equal to CREATE_DATABASE which calls the BuildDatabase method and then calls the reloadAllComponents method of the UIPickerView protocol to call the protocol methods like we did when the app was launched. This will rebuild the data source and allow the UIPickerView to display the new values. If the ops value is DROP_- DATABASE, then the DeleteDatabase method is called. Otherwise the ModifyDatabase method is called passing the database name and the selected database operation that will be executed.

1 // 2 // ViewController.m 3 // Update SQLite Database 4 // 5 // Created by Kevin Languedoc on 4/27/14. 6 // Copyright (c) 2014 Kevin Languedoc. All rights reserved. 7 // 8 9 #import "ViewController.h" 10 11 @interface ViewController () 12 13 @end 14 15 @implementation ViewController 16 @synthesize dbOperations,databases, dbOperation, 17 selectedDatabase,selectedOperation; 18 19 -(void)viewDidLoad Updating an iOS SQLite Database 17

20 { 21 [super viewDidLoad]; 22 /* 23 Setup data source for Db Operations list 24 */ 25 26 dbOperations = [[NSArray alloc] initWithObjects: 27 @"", 28 CREATE_DATABASE, 29 CREATE_INDEX, 30 CREATE_VIEW, 31 CREATE_TRIGGER, 32 ALTER_DATABASE, 33 ALTER_TABLE, 34 DROP_DATABASE, 35 DROP_TABLE, 36 DROP_INDEX, 37 DROP_VIEW, 38 DROP_TRIGGER, 39 nil]; 40 } 41 42 -(void)didReceiveMemoryWarning 43 { 44 [super didReceiveMemoryWarning]; 45 // Dispose of any resources that can be recreated. 46 } 47 48 49 //number of columns 50 - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *) 51 pickerView{ 52 return 1; 53 } 54 55 // returns the # of rows in each component.. 56 - (NSInteger)pickerView:(UIPickerView *)pickerView 57 numberOfRowsInComponent:(NSInteger)component{ 58 if(pickerView.tag==1){ 59 return [databases count]; 60 }else if (pickerView.tag==2){ 61 return [dbOperations count]; 62 }else{ Updating an iOS SQLite Database 18

63 return 0; 64 } 65 } 66 67 -(NSString *)pickerView:(UIPickerView *)pickerView 68 titleForRow:(NSInteger)row forComponent:(NSInteger)component{ 69 if(pickerView.tag==1){ 70 return [databases objectAtIndex:row]; 71 }else if (pickerView.tag==2){ 72 return [dbOperations objectAtIndex:row]; 73 }else{ 74 return @""; 75 } 76 } 77 -(void)pickerView:(UIPickerView *)pickerView 78 didSelectRow:(NSInteger)row inComponent:(NSInteger)component{ 79 if(pickerView.tag==1){ 80 selectedDatabase = [[NSString alloc] 81 initWithString:[databases objectAtIndex:row]]; 82 }else if (pickerView.tag==2){ 83 selectedOperation = [[NSString alloc] 84 initWithString:[dbOperations objectAtIndex:row]]; 85 } 86 87 } 88 89 90 -(void)DoDatabaseOperation:(NSString*)database :(NSString*)ops{ 91 if([ops isEqual: CREATE_DATABASE]){ 92 [dbOperation BuildDatabase:database]; 93 [dbOperation GetDatabaseList]; 94 [self.databasePickerView reloadAllComponents]; 95 }else if([ops isEqual:DROP_DATABASE]){ 96 [dbOperation DeleteDatabase:database]; 97 [dbOperation GetDatabaseList]; 98 [self.databasePickerView reloadAllComponents]; 99 }else{ 100 [dbOperation ModifyDatabase:database :ops]; 101 } 102 } 103 104 - (IBAction)updateDatabase:(id)sender { 105 if(selectedDatabase == nil && selectedOperation ==nil) Updating an iOS SQLite Database 19

106 { 107 UIAlertView *alert = [[UIAlertView alloc] 108 initWithTitle:@"Warning" 109 message:@"You need to select a database 110 and an operation" 111 delegate:self 112 cancelButtonTitle:@"OK" 113 otherButtonTitles:nil]; 114 [alert show]; 115 }else{ 116 117 [self DoDatabaseOperation:selectedDatabase :selectedOperation]; 118 119 } 120 121 } 122 @end

This wraps up the controller section of the application. We have seen how to setup the database operations to modify a SQLite database. The next section is focus on the UI.

Create The View

In the MVC design pattern, the View is the user interface or UI. My little app doesn’t have great UI. It is simply a rudimentary functional UI to provide the basic functioning to my app. Ok that I have hopefully managed expectations, we can begin building the UI. Typically an app will have one Storyboard unless you are building an universal (iPad and iPhone ) app then you would have to Storyboards. Open the Storyboard that was created with the template. For this app, we will need two UIPickerViews, one button (UIButton) and a couple of UILabels all of which you can drag from the palette on the bottom right of the IDE. Updating an iOS SQLite Database 20

Figure 2: Object Library Xcode

Once you have your UI laid out like in the previous screenshot, I want you to select each UIPickerView in turn and select the Attributes inspector view on the right of the IDE and set the tag attribute to 1 for the first UIPickerView and 2 for the second. Below is a screenshot of the utility. Updating an iOS SQLite Database 21

Figure 3: Attributes inspector

Now we will create the connections. Open the header file of the ViewController for the UI by clicking on the tuxedo icon (see screenshot below). In the following the code is already done. To create a connection hold down the control button on the keyboard and drag a line from the top UIPickerView over to the open header file using the mouse. When you release the mouse button a popover panel will appear (figure 5) providing an input field to set the name of the IBOutlet. You will need to set the IBOutlet to databasePickerView and click on the connect button. The code will be added automati- cally to the header file and add the dot in the margin to indicate that a connection was created. Repeat the process for the second UIPickerView, naming that one operationsPickerView. For the UIButton, you follow the same process but you will need to change the connection type to Action (figure 6). Updating an iOS SQLite Database 22

Figure 4: Assistant editor

Figure 5: IBOutlet popover

Figure 6: IBAction editor

To add the configuration for the UIPickerView protocols, select each UIPickerView in turn and control drag a connection line to the proxy icon (first yellow icon below the ViewController). When you release the mouse button, a popover panel will appear allowing you to either select either the dataSource or Delete Outlets. You will have to select both individually for each UIPickerView and create connections for each. The screenshot below (figure 7) provides a visual. Updating an iOS SQLite Database 23

Figure 7: Assistant editor

Now you are ready to go. No more programming is needed. When the app runs for the first time, you will need to create a database, so turn the UIPickerView to the Create Database option and click on the Update button. For the example, the database will be created using the test.db file name. However ideally you would have an inout screen t allow a person to enter a value for the database filename to create. Once the UI refreshes, the top UIPickerView should have test.db in the choices to chose from. Now you will be able to select the database and perform the various options from the available list in the second UIPickerView. Updating an iOS SQLite Database 24

Figure 8: Running app

Conclusion

That is it. This chapter provided a working app to modify a new or existing database. It also provided examples on all the update related queries that SQLite can handle.