Столкнувшись с задачей подключить SQLLite к своему мобильному приложению iOS через FMDB, я не нашел ни одного актуального гайда на русском языке. И тем более для Swift. В этой статье я постараюсь этого исправить.
В этом гайде будут использоваться файлы с objective-c, поэтому не надо ждать порта FMDB на Swift.
Скачать FMDB можно тут.
В FMDB три main class:
FMDatabase — представляет данных SQLite. Используется для выполнения SQL-операторов.
FMResultSet — представляет результаты выполнения запроса по FMDatabase.
FMDatabaseQueue — если вы хотите, чтобы выполнялись запросы и обновления на несколько потоков, можно использовать этот класс. Пример в 8 пункте.
Прежде чем вы сможете взаимодействовать с базой данных, она должен быть открыта. Открытие завершиться с ошибкой, если нет достаточных ресурсов или разрешения на открытие и/или создания базы данных.
if (![db open]) { [db release]; return; }
Шаги:
1) Добавьте ‘libsqlite3’ стандартную библиотеку в настройках проекта и скопируйте FMDB файлы в ваш проект. (да, они на objective-c).
2) Создайте новый файл, который будет называться «FMDB-Bridging-Header.h». Внутри «Bridging-Header.h» напишите следующее: #import «FMDB.h».
3) Зайдите в Build Settings -> Swift Compiler — Code Generation и добавьте к ‘Objective-C Bridging Header’: FMDB-Bridging-Header.h.
Если файл в папке вашего проекта, то так: ИМЯ_ПАПКИ/FMDB-Bridging-Header.h
4) Скопируйте в Ваш проект SQLite database. В этом гайде я буду использовать название ‘tempdb.sqlite’ всего лишь с одной таблицей внутри:
CREATE TABLE test_tb ( test_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, keywordtext TEXT)
5) В вашем AppDelegate.swift’s class AppDelegate добавьте следующие переменные: var dbFilePath: NSString = NSString()
Пример:
import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? var navi: UINavigationController? var dbFilePath: NSString = NSString() func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool { ....
6) Добавьте этот метод в AppDelegate.swift’s class AppDelegate:
// MARK: - FMDB let DATABASE_RESOURCE_NAME = "tempdb" let DATABASE_RESOURCE_TYPE = "sqlite" let DATABASE_FILE_NAME = "tempdb.sqlite" func initializeDb() -> Bool { let documentFolderPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String let dbfile = "/" + DATABASE_FILE_NAME; self.dbFilePath = documentFolderPath.stringByAppendingString(dbfile) let filemanager = NSFileManager.defaultManager() if (!filemanager.fileExistsAtPath(dbFilePath) ) { let backupDbPath = NSBundle.mainBundle().pathForResource(DATABASE_RESOURCE_NAME, ofType: DATABASE_RESOURCE_TYPE) if (backupDbPath == nil) { return false } else { var error: NSError? let copySuccessful = filemanager.copyItemAtPath(backupDbPath, toPath:dbFilePath, error: &error) if !copySuccessful { println("copy failed: \(error?.localizedDescription)") return false } } } return true }
7) Вызовите в AppDelegate.swift’s func application:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool { if self.initializeDb() { NSLog("Successful db copy") }
8) В этом примере мы работаем с данными UITableViewController) используя FMDB:
import UIKit class SecondViewController: UIViewController { // MARK: - .H @IBOutlet var dataTable: UITableView? var dataArray:[MultiField] = [] // MARK: - .M required init(coder: NSCoder) { fatalError("NSCoding not supported") } override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) // Custom initialization } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. self.title = "FMDB Using Swift" let mainDelegate: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate // initialize FMDB let db: FMDatabase = FMDatabase(path:mainDelegate.dbFilePath) if (db.open() == nil) { NSLog("error opening db") } // вставка данных let addQuery = "INSERT INTO test_tb (name, keywordtext) VALUES ('excalibur', 'hot')" let addSuccessful = db.executeUpdate(addQuery, withArgumentsInArray: nil) if !addSuccessful { println("insert failed: \(db.lastErrorMessage())") } // вставка данных - конец // update данных let updateQuery = "UPDATE test_tb SET keywordtext = 'cool' WHERE name = 'excalibur' " let updateSuccessful = db.executeUpdate(updateQuery, withArgumentsInArray: nil) if !updateSuccessful { println("update failed: \(db.lastErrorMessage())") } // update данных - конец // Получение данных из нашей базы и сохранение их в массив UITableView let mainQuery = "SELECT name, keywordtext FROM test_tb" let rsMain: FMResultSet? = db.executeQuery(mainQuery, withArgumentsInArray: []) while (rsMain!.next() == true) { let productName = rsMain?.stringForColumn("name") let keywords = rsMain?.stringForColumn("keywordtext") let multiField = MultiField(aField1: productName!, aField2: keywords!) self.dataArray.append(multiField) } // получение данных - конец // удаление данных let delQuery = "DELETE FROM test_tb WHERE name = 'excalibur' " let deleteSuccessful = db.executeUpdate(delQuery, withArgumentsInArray: nil) if !deleteSuccessful { println("delete failed: \(db.lastErrorMessage())") } // удаление данных - конец // пример: получение номер строк let rsTemp: FMResultSet? = db.executeQuery("SELECT count(*) AS numrows FROM test_tb", withArgumentsInArray: []) rsTemp!.next() let numrows = rsTemp?.intForColumn("numrows") NSLog("numrows: \(numrows)") //пример: получение номер строки - конец db.close() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } // MARK: - TableView DataSource func numberOfSectionsInTableView(tableView: UITableView!) -> Int { return 1 } func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int { return self.dataArray.count } func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! { let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "FMDBTest") let multiField: MultiField = self.dataArray[indexPath.row] let num = indexPath.row + 1 cell.textLabel.text = "\(num). \(multiField.field1!)" cell.detailTextLabel.text = multiField.field2 return cell } // MARK: - UITableViewDelegate func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) { tableView.deselectRowAtIndexPath(indexPath, animated: true) } }
9) Немного разных фишек, использование мультипотока FMDB через FMDatabaseQueue.
var queue: FMDatabaseQueue? func testDatabaseQueue() { let documentsFolder = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String let databasePath = documentsFolder.stringByAppendingPathComponent("test.sqlite") queue = FMDatabaseQueue(path: databasePath) // создание таблицы <source lang="objectivec"> queue?.inDatabase() { db in var success = db.executeUpdate("create table test (id integer primary key autoincrement, a text)", withArgumentsInArray:nil) if !success { println("table create failure: \(db.lastErrorMessage())") return } }
// вставка пяти строк
queue?.inTransaction() { db, rollback in for i in 0 ..< 5 { if !db.executeUpdate("insert into test (a) values (?)", withArgumentsInArray: ["Row \(i)"]) { println("insert \(i) failure: \(db.lastErrorMessage())") rollback.initialize(true) return } } }
//давайте попробуем вставить строки, но сознательно ошибемся и посмотрим, что он откатывает правильно
queue?.inTransaction() { db, rollback in for i in 5 ..< 10 { let success = db.executeUpdate("insert into test (a) values (?)", withArgumentsInArray: ["Row \(i)"]) if !success { println("insert \(i) failure: \(db.lastErrorMessage())") rollback.initialize(true) return } if (i == 7) { rollback.initialize(true) } } }
// проверим, что только первые пять строк там
queue?.inDatabase() { db in if let rs = db.executeQuery("select * from test", withArgumentsInArray:nil) { while rs.next() { println(rs.resultDictionary()) } } else { println("select failure: \(db.lastErrorMessage())") } }
// удаляем таблицу
queue?.inDatabase() { db in let success = db.executeUpdate("drop table test", withArgumentsInArray:nil) if !success { println("table drop failure: \(db.lastErrorMessage())") return } } }
10) Стандартного на закуску. Использование класса executeUpdate(values:) в Swift2:
do { let identifier = 42 let name = "Liam O'Flaherty (\"the famous Irish author\")" let date = NSDate() let comment: String? = nil try db.executeUpdate("INSERT INTO authors (identifier, name, date, comment) VALUES (?, ?, ?, ?)", values: [identifier, name, date, comment ?? NSNull()]) } catch { print("error = \(error)") }
Использование queue:
queue.inTransaction { db, rollback in do { try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [1]) try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [2]) try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [3]) if whoopsSomethingWrongHappened { rollback.memory = true return } try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [4]) } catch { rollback.memory = true print(error) } }
Пример из стандартного описания:
let documents = try! NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false) let fileURL = documents.URLByAppendingPathComponent("test.sqlite") let database = FMDatabase(path: fileURL.path) if !database.open() { print("Unable to open database") return } do { try database.executeUpdate("create table test(x text, y text, z text)", values: nil) try database.executeUpdate("insert into test (x, y, z) values (?, ?, ?)", values: ["a", "b", "c"]) try database.executeUpdate("insert into test (x, y, z) values (?, ?, ?)", values: ["e", "f", "g"]) let rs = try database.executeQuery("select x, y, z from test", values: nil) while rs.next() { let x = rs.stringForColumn("x") let y = rs.stringForColumn("y") let z = rs.stringForColumn("z") print("x = \(x); y = \(y); z = \(z)") } } catch let error as NSError { print("failed: \(error.localizedDescription)") } database.close()
Если что-то не получается, пишете, постараюсь помочь.
ссылка на оригинал статьи https://habrahabr.ru/post/277423/
Добавить комментарий