In previous articles we have utilized NSUserDefaults and .NET web services to persist iPhone data. NSUserDefaults is idol for storing small amounts of data. Web services are used to store the data on a custom server. In this article we will take a look at how to store iPhone data in SQLite database using FMDB wrapper.

FMDB: 

The iOS SDK provides API for persisting data in the SQLite database. Unfortunately, the API is in pure C language which makes it hard for developers to use it.

FMDB is a wrapper on top of the base API and provides easy to use methods to perform database operations using Objective-C language. FMDB is an open source project which can be downloaded using the this link.

Once, FMDB is downloaded you can import the following files in your custom Group in your iOS application.

FMDBDatabase.h 
FMDBDatabase.m 
FMResultSet.h
FMResetSet.m

Also, make sure to add a link to the "libsqlite3.dylib" library. Compile the app and make sure it builds successfully. In the next section we are going to create our SQLite3 database.

Creating SQLite3 database: 

You can easily use the Terminal to create the SQLite3 database. The screenshot below shows how a database named "Customers.db" is created and a single record is inserted into the "customers" table.


There are several plugins that allow to view and edit the information stored in the SQLite3 database. We recommend checking out the SQLite Manager FireFox plugin.

When you are done adding some custom rows to your newly created "Customers.db" database copy it into your "Supporting Files" or "Resources" folder. From there we will transfer the database file to the iPhone "Documents" folder. The advantage of copying the database into Documents folder is that it will be backed up whenever the iPhone is backed up. For iOS5 cloud enabled devices this means your database will be available on the cloud.

In the next section we will copy the SQLite3 database into iPhone "Documents" folder.

Copying Database to iPhone Documents Folder: 

The AppDelegate is responsible for copying the database to the iPhone "Documents" folder. This operation is only performed when the database does not exists in the "Documents" folder. The implementation is shown below:

01 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
02 {
03     self.databaseName = @"Customers.db";
04      
05     NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
06     NSString *documentDir = [documentPaths objectAtIndex:0];
07     self.databasePath = [documentDir stringByAppendingPathComponent:self.databaseName];
08      
09     [self createAndCheckDatabase];
10  
11     // Override point for customization after application launch.
12     return YES;
13 }
14          
15 -(void) createAndCheckDatabase
16 {
17     BOOL success;
18      
19     NSFileManager *fileManager = [NSFileManager defaultManager];
20     success = [fileManager fileExistsAtPath:databasePath];
21      
22     if(success) return;
23      
24     NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:self.databaseName];
25      
26     [fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
27 }

The NSFileManager is responsible for making sure that the database is not overridden if it already exists.

In the next section we are going to display the data in our view. The views and the application flow is created using Storyboards, new feature in iOS5 SDK.

Displaying Customers on UITableView: 

The starting point of the app will be a UINavigationController which will display a UITableViewController. The UITableViewController will display the current customers to the user. We have implemented all the CRUD operations inside our custom file "FMDBDataAccess.m". The "FMDBDataAccess.h" header is implemented below:

01 #import <Foundation/Foundation.h>
02 #import "FMDatabase.h"
03 #import "FMResultSet.h"
04 #import "Utility.h"
05 #import "Customer.h"
06  
07 @interface FMDBDataAccess : NSObject
08 {
09      
10 }
11  
12 -(NSMutableArray *) getCustomers;
13 -(BOOL) insertCustomer:(Customer *) customer;
14 -(BOOL) updateCustomer:(Customer *) customer;
15  
16 @end

The getCustomers method is implemented below:

01 -(NSMutableArray *) getCustomers
02 {
03     NSMutableArray *customers = [[NSMutableArray alloc] init];
04      
05     FMDatabase *db = [FMDatabase databaseWithPath:[Utility getDatabasePath]];
06      
07     [db open];
08      
09     FMResultSet *results = [db executeQuery:@"SELECT * FROM customers"];
10      
11     while([results next])
12     {
13         Customer *customer = [[Customer alloc] init];
14          
15         customer.customerId = [results intForColumn:@"id"];
16         customer.firstName = [results stringForColumn:@"firstname"];
17         customer.lastName = [results stringForColumn:@"lastname"];
18          
19         [customers addObject:customer];
20          
21     }
22      
23     [db close];
24    
25     return customers;
26  
27 }

The FMDatabase instance is responsible for connecting to the SQLite3 database using the path to the database. The FMResultSet is populated with the result of the SQL query. Finally, we loop through the rows and populate the Customer object and add it to the customers array. The populated customers array is returned to the caller.

NOTE: You should always use @try-@catch-@finally blocks when working with database connections and make sure the database connection is closed in the @finally block. This will ensure that the connection is closed even when an exception is thrown.

The getCustomers method is invoked from our CustomersViewController. The populateCustomers method is implemented below:

1 -(void) populateCustomers
2 {
3     self.customers = [[NSMutableArray alloc] init];
4      
5     FMDBDataAccess *db = [[FMDBDataAccess alloc] init];
6      
7     self.customers = [db getCustomers];
8 }

The populateCustomers is triggered from inside the viewDidLoad method. Finally, the cellForRowAtIndexPath is triggered which populates the tableView.

01 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
02 {
03     static NSString *CellIdentifier = @"CustomerCell";
04      
05     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
06  
07     Customer *customer = [self.customers objectAtIndex:[indexPath row]];
08      
09     [[cell textLabel] setText:[NSString stringWithFormat:@"%@ %@",customer.firstName,customer.lastName]];
10      
11     return cell;
12 }

The screenshot below shows the customers populated on the UITableView.

In the next section we are going add new customers to the database.

Adding and Updating New Customers: 

Before iOS 5 developers had to manager the flow of their screens using UINavigationController. iOS 5 introduced Storyboards which allows developers to use Storyboard designer to create all the workflows of the iOS application. To learn more about Storyboards check out the following articles on Ray Winderlech website:

1) Begining Storyboards
2) Intermediate Storyboards

The "+" button in the previous screenshot will take you to a different view which is composed of the static UITableView cells. The screenshot below shows the Add Customer screen.

The Add Customer view simply displays two UITextFields for first name and last name respectively. The user enters the required information and save the new customer by using the save button. The save button invokes the done method and triggers a delegate which passes the new customer to the CustomerViewController and then persist in the database.

01 -(IBAction) done:(id) sender
02 {
03         Customer *customer = [[Customer alloc] init];
04         customer.firstName = self.firstNameTextField.text;
05         customer.lastName = self.lastNameTextField.text;
06          
07         if(![self validate:customer])
08         {
09             [Utility showAlert:@"Error" message:@"Validation Failed!"];
10             return;
11         }
12          
13         [self.delegate addCustomerViewController:self didAddCustomer:customer];
14   
15 }

The protocol declaration for the delegate is shown below:

01 @class Customer;
02 @class AddCustomerViewController;
03  
04 @protocol AddCustomerViewControllerDelegate<NSObject>
05  
06 -(void) addCustomerViewController:(AddCustomerViewController *) controller
07                    didAddCustomer:(Customer *) customer;
08  
09 -(void) addCustomerViewController:(AddCustomerViewController *)controller didEditCustomer:(Customer *)customer;
10  
11 -(void) addCustomerViewControllerDidCancel:(AddCustomerViewController *) controller;
12  
13 @end

The CustomerViewController handles the delegate and persist the new customer into the database. Make sure that the CustomerViewController adheres to the delegate as shown in the code below:

01 @interface CustomersViewController : UITableViewController<AddCustomerViewControllerDelegate>
02 {
03      
04 }
05  
06 @property (nonatomic,strong) NSMutableArray *customers;
07  
08 -(void) populateCustomers;
09  
10 @end

The didAddCustomer method is implemented in the CustomerViewController.m file as shown below:

01 -(void) addCustomerViewController:(AddCustomerViewController *)controller didAddCustomer:(Customer *)customer
02 {
03     FMDBDataAccess *db = [[FMDBDataAccess alloc] init];
04      
05     [db insertCustomer:customer];
06      
07     [self populateCustomers];
08      
09     NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[self.customers count] - 1 inSection:0];
10      
11     [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
12      
13     [self dismissViewControllerAnimated:YES completion:nil];
14 }

The real persisting of data is performed in the FMDBDataAccess class. The insertCustomer method is responsible for saving a new customer to the database. The insertCustomer method is shown below:

01 -(BOOL) insertCustomer:(Customer *) customer
02 {
03     // insert customer into database
04      
05     FMDatabase *db = [FMDatabase databaseWithPath:[Utility getDatabasePath]];
06      
07     [db open];
08      
09     BOOL success =  [db executeUpdate:@"INSERT INTO customers (firstname,lastname) VALUES (?,?);",
10                      customer.firstName,customer.lastName, nil];
11      
12     [db close];
13      
14     return success;
15      
16 }

The executeUpdate method executes the SQL statements which inserts the new customer in the database. The new customer is added to the UITableView inside the didAddCustomer implementation as shown below:

1 NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[self.customers count] - 1 inSection:0];
2      
3     [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];

If you run the app you will notice that when you save nothing happens. This is because you need one additional step which is to prepare the segue between the different views. The implementation is shown below:

01 -(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
02 {
03     if([segue.identifier isEqualToString:@"AddCustomer"])
04     {
05         UINavigationController *navigationController = segue.destinationViewController;
06         AddCustomerViewController *addCustomerViewController = [[navigationController viewControllers] objectAtIndex:0];
07         addCustomerViewController.delegate = self;
08     }
09      
10     else if([segue.identifier isEqualToString:@"EditCustomer"])
11     {
12         UINavigationController *navigationController = segue.destinationViewController;
13         AddCustomerViewController *addCustomerViewController = [[navigationController viewControllers] objectAtIndex:0];
14         addCustomerViewController.delegate = self;
15          
16         NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
17         Customer *customer = [self.customers objectAtIndex:[indexPath row]];
18         addCustomerViewController.customerToEdit = customer;
19          
20     }
21 }

The "AddCustomer" and the "EditCustomer" constants are the unique identifiers of the segue. Now, if you run the above code and add a new customer you will notice that it shows up in the customers UITableView control.

The insert customer method was successful but now we need a way to edit/update the customer. The user can edit the customer once they click on the customer row. The customer row is linked to the add customer view we implemented earlier.

The selected customer is assigned to the customerToEdit property of the AddCustomerViewController instance as shown in the segue code below:

01 -(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
02 {
03      
04     else if([segue.identifier isEqualToString:@"EditCustomer"])
05     {
06         UINavigationController *navigationController = segue.destinationViewController;
07         AddCustomerViewController *addCustomerViewController = [[navigationController viewControllers] objectAtIndex:0];
08         addCustomerViewController.delegate = self;
09          
10         NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
11         Customer *customer = [self.customers objectAtIndex:[indexPath row]];
12         addCustomerViewController.customerToEdit = customer;
13          
14     }
15 }

The AddCustomerViewController checks if the customerToEdit is nil or not. If it is not then it populates the first name and last name UITextFields with the information as shown in the code below:

01 - (void)viewDidLoad
02 {
03     [super viewDidLoad];
04      
05     if(self.customerToEdit != nil)
06     {
07         self.title = [NSString stringWithFormat:@"%@ %@",self.customerToEdit.firstName,self.customerToEdit.lastName];
08         self.firstNameTextField.text = self.customerToEdit.firstName;
09         self.lastNameTextField.text = self.customerToEdit.lastName;
10          
11     }
12      
13 }

The screenshot is shown below:

Now, when the user saves the current customer it will not be inserted but updated in the database. The "done" method makes sure not to insert the customer which is in edit state.

01 -(IBAction) done:(id) sender
02 {
03     if(self.customerToEdit != nil)
04     {
05         self.customerToEdit.firstName = self.firstNameTextField.text;
06         self.customerToEdit.lastName = self.lastNameTextField.text;
07          
08         if(![self validate:self.customerToEdit])
09         {
10             [Utility showAlert:@"Error" message:@"Validation Failed!"];
11             return;
12         }
13          
14         [self.delegate addCustomerViewController:self didEditCustomer:self.customerToEdit];
15     }
16      
17     else
18     {
19         Customer *customer = [[Customer alloc] init];
20         customer.firstName = self.firstNameTextField.text;
21         customer.lastName = self.lastNameTextField.text;
22          
23         if(![self validate:customer])
24         {
25             [Utility showAlert:@"Error" message:@"Validation Failed!"];
26             return;
27         }
28          
29         [self.delegate addCustomerViewController:self didAddCustomer:customer];
30     }
31    
32 }

The didEditCustomer delegate is invoked which triggers the FMDBDataAccess updateCustomer method. The updateCustomer method is implemented below:

01 -(BOOL) updateCustomer:(Customer *)customer
02 {
03     FMDatabase *db = [FMDatabase databaseWithPath:[Utility getDatabasePath]];
04      
05     [db open];
06      
07     BOOL success = [db executeUpdate:[NSString stringWithFormat:@"UPDATE customers SET firstname = '%@', lastname = '%@' where id = %d",customer.firstName,customer.lastName,customer.customerId]];
08      
09     [db close];
10      
11     return success;
12 }

Try it out and edit a customer. Once, you save the customer you will notice the UITableView now displays the updated customer.

Resources:

1) Learning iOS Development Part 13 (Sqlite3 Using FMDB)

Conclusion: 

In this article we demonstrated how to use FMDB API to make CRUD on SQLite3 database very simple for iOS development.

[Download Sample]

Persisting iOS Application Data in SQLite Database Using FMDB的更多相关文章

  1. Objective-C ,ios,iphone开发基础:ios数据库(The SQLite Database),使用终端进行简单的数据库操作

    SQLite  是一个轻量级的免费关系数据库.SQLite最初的设计目标是用于嵌入式系统,它占用资源非常少,在嵌入式设备中,只需要几百K的内存就够了,可以在(http://www.sqlite.org ...

  2. [置顶] Objective-C ,ios,iphone开发基础:ios数据库(The SQLite Database),使用终端进行简单的数据库操作

    SQLite  是一个轻量级的免费关系数据库.SQLite最初的设计目标是用于嵌入式系统,它占用资源非常少,在嵌入式设备中,只需要几百K的内存就够了,可以在(http://www.sqlite.org ...

  3. iOS - SQLite Database 操作数据库

    iOS - SQLite Database 操作数据库   Sqlite 能被用在ios上做数据处理用,只要你懂得一点sql 就很容易使用sqlite 1:创建一个简单的View based appl ...

  4. IOS Application Security Testing Cheat Sheet

    IOS Application Security Testing Cheat Sheet    [hide]  1 DRAFT CHEAT SHEET - WORK IN PROGRESS 2 Int ...

  5. Using SQLite database in your Windows 10 apps

    MVP可以在channel 9上传视频了,所以准备做个英文视频传上去分享给大家,本文做稿子. Hello everyone, As we all know, SQLite is a great and ...

  6. iOS开发数据库篇—SQLite简单介绍

    iOS开发数据库篇—SQLite简单介绍 一.离线缓存 在项目开发中,通常都需要对数据进行离线缓存的处理,如新闻数据的离线缓存等. 说明:离线缓存一般都是把数据保存到项目的沙盒中.有以下几种方式 (1 ...

  7. 【转】 iOS开发数据库篇—SQLite简单介绍

    开始学SQLite啦, 原文: http://www.cnblogs.com/wendingding/p/3868893.html iOS开发数据库篇—SQLite简单介绍 一.离线缓存 在项目开发中 ...

  8. [转]Android Studio SQLite Database Multiple Tables Example

    本文转自:http://instinctcoder.com/android-studio-sqlite-database-multiple-tables-example/ BY TAN WOON HO ...

  9. Demystifying iOS Application Crash Logs

    http://www.raywenderlich.com/23704/demystifying-ios-application-crash-logs This is a blog post by So ...

随机推荐

  1. atan与atan2的区别

    相比较ATan,ATan2究竟有什么不同?本篇介绍一下ATan2的用法及使用条件. 对于tan(θ) = y / x: θ = ATan(y / x)求出的θ取值范围是[-PI/2, PI/2]. θ ...

  2. 【bzoj5017】[Snoi2017]炸弹 线段树优化建图+Tarjan+拓扑排序

    题目描述 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸时,如果另一个炸弹所在位置 Xj 满足:  Xi−Ri≤Xj≤Xi+Ri,那么,该炸弹也会被引爆.  现在 ...

  3. CSS Sprite、CSS雪碧图应用实例

    CSS Sprites技术被国内一些人称为CSS雪碧图,其实就是把网页中一些背景图片整合到一张图片文件中,再利用CSS的“background-image”,“background- repeat”, ...

  4. vue嵌套路由与404重定向实现方法分析

    第一部分: vue嵌套路由 嵌套路由是什么? 嵌套路由就是在一个被路由过来的页面下可以继续使用路由,嵌套也就是路由中的路由的意思. 比如在vue中,我们如果不使用嵌套路由,那么只有一个<rout ...

  5. 【POJ 2387 Til the Cows Come Home】

    Time Limit: 1000MSMemory Limit: 65536K Total Submissions: 59755Accepted: 20336 Description Bessie is ...

  6. 【马克-to-win】—— 学习笔记

    声明 以下学习内容转载自:http://www.mark-to-win.com/ 社区,由马克java社区创始人---"马克-to-win"一人全部独立写作,创作和制作. 非常感谢 ...

  7. 图表绘制工具--Matplotlib 3

    ''' [课程3.] 表格样式创建 表格视觉样式:Dataframe.style → 返回pandas.Styler对象的属性,具有格式化和显示Dataframe的有用方法 样式创建: ① Style ...

  8. bzoj3638 Cf172 k-Maximum Subsequence Sum

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3638 [题解] 看到k<=20就感觉很py了啊 我们用一棵线段树维护选段的过程,能选到 ...

  9. Java并发笔记(二)

    1. 活跃性危险 死锁(最常见) 饥饿 当线程由于无法访问它所需的资源而不能继续执行时,就发生了饥饿.引发饥饿最常见资源就是CPU时钟周期. 活锁 活锁指的是任务或者执行者没有被阻塞,由于某些条件没有 ...

  10. 行为型设计模式之职责链模式(Chain of Responsibility)

    结构 意图 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. 适用性 有多个的对象可以处理一个请求,哪个 ...