Fmdb
A Cocoa / Objective-C wrapper around SQLite
Install / Use
/learn @ccgus/FmdbREADME
FMDB v2.7
<!--[](http://cocoadocs.org/docsets/Alamofire)-->This is an Objective-C wrapper around SQLite.
The FMDB Mailing List:
https://groups.google.com/group/fmdb
Read the SQLite FAQ:
https://www.sqlite.org/faq.html
Since FMDB is built on top of SQLite, you're going to want to read this page top to bottom at least once. And while you're there, make sure to bookmark the SQLite Documentation page: https://www.sqlite.org/docs.html
Contributing
Do you have an awesome idea that deserves to be in FMDB? You might consider pinging ccgus first to make sure he hasn't already ruled it out for some reason. Otherwise pull requests are great, and make sure you stick to the local coding conventions. However, please be patient and if you haven't heard anything from ccgus for a week or more, you might want to send a note asking what's up.
Installing
CocoaPods
FMDB can be installed using CocoaPods.
If you haven't done so already, you might want to initialize the project, to have it produce a Podfile template for you:
$ pod init
Then, edit the Podfile, adding FMDB:
# Uncomment the next line to define a global platform for your project
# platform :ios, '12.0'
target 'MyApp' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
# Pods for MyApp2
pod 'FMDB'
# pod 'FMDB/FTS' # FMDB with FTS
# pod 'FMDB/standalone' # FMDB with latest SQLite amalgamation source
# pod 'FMDB/standalone/FTS' # FMDB with latest SQLite amalgamation source and FTS
# pod 'FMDB/SQLCipher' # FMDB with SQLCipher
end
Then install the pods:
$ pod install
Then open the .xcworkspace rather than the .xcodeproj.
For more information on Cocoapods visit https://cocoapods.org.
If using FMDB with SQLCipher you must use the FMDB/SQLCipher subspec. The FMDB/SQLCipher subspec declares SQLCipher as a dependency, allowing FMDB to be compiled with the -DSQLITE_HAS_CODEC flag.
Carthage
Once you make sure you have the latest version of Carthage, you can open up a command line terminal, navigate to your project's main directory, and then do the following commands:
$ echo ' github "ccgus/fmdb" ' > ./Cartfile
$ carthage update
You can then configure your project as outlined in Carthage's Getting Started (i.e. for iOS, adding the framework to the "Link Binary with Libraries" in your target and adding the copy-frameworks script; in macOS, adding the framework to the list of "Embedded Binaries").
Swift Package Manager
Declare FMDB as a package dependency.
.package(
name: "FMDB",
url: "https://github.com/ccgus/fmdb",
.upToNextMinor(from: "2.7.12")),
Use FMDB in target dependencies
.product(name: "FMDB", package: "FMDB")
Swift Package Manager with SQLCipher Encryption
If you want to use SQLCipher with FMDB Swift Package you can specify the SQLCipher trait when consuming FMDB Swift Package.
depedencies: [
.package(url: "https://github.com/ccgus/fmdb", from: "2.7.12", traits: ["SQLCipher"])
]
As of Xcode 16.4 (16F6), there's no direct way in the Xcode UI to select trait variations so you'll need to use a local wrapper package to pull in the FMDB dependency with the SQLCipher trait enabled:
// swift-tools-version: 6.1
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "AppDependencies",
platforms: [
.macOS(.v10_14),
.iOS(.v13),
.macCatalyst(.v13),
.watchOS(.v8),
.tvOS(.v15),
.visionOS(.v1)
],
products: [
.library(
name: "AppDependencies",
targets: ["AppDependencies"]),
],
dependencies: [
.package(
url: "https://github.com/ccgus/fmdb",
from: "2.7.12",
traits: ["SQLCipher"])
],
targets: [
.target(
name: "AppDependencies",
dependencies: [
.product(
name: "FMDB",
package: "FMDB")
]
)
]
)
Within Xcode add your local AppDependencies wrapper package as a package dependency and FMDB with SQLCipher functionality will be accessible.
Using the SQLCipher trait will cause FMDB to include a dependency on SQLCipher.swift and enable FMDatabase+SQLCipher methods to set the database key and encrypt a new database:
import FMDB
let db = FMDatabase(path: NSTemporaryDirectory().appending("tmp.db"))
guard db.open() else {
print("Unable to open datbase")
return
}
db.setKey("sup3rs3cr3t")
// perform database operations to read from/write to the encrypted database
db.close()
To encrypt an existing database:
// path to unencrypted db
let plaintextDb = FMDatabase(path: NSTemporaryDirectory().appending("unencrypted.db"))
guard plaintextDb.open() else {
print("Unable to open database")
return
}
// path to export encrypted db to (non-existent prior to export)
let encryptedDbPath = NSTemporaryDirectory().appending("encrypted.db")
// attach encrypted db using desired key
try plaintextDb.executeUpdate("ATTACH DATABASE ? AS encrypted KEY ?", values: [encryptedDbPath, "encryptedPass"])
// sqlcipher_export providing the alias of the attached encrypted db
plaintextDb.executeStatements("SELECT sqlcipher_export('encrypted');")
// detach the encrypted db
plaintextDb.executeStatements("DETACH DATABASE encrypted;")
FMDB Class Reference:
https://ccgus.github.io/fmdb/html/index.html
Automatic Reference Counting (ARC) or Manual Memory Management?
You can use either style in your Cocoa project. FMDB will figure out which you are using at compile time and do the right thing.
What's New in FMDB 2.7
FMDB 2.7 attempts to support a more natural interface. This represents a fairly significant change for Swift developers (audited for nullability; shifted to properties in external interfaces where possible rather than methods; etc.). For Objective-C developers, this should be a fairly seamless transition (unless you were using the ivars that were previously exposed in the public interface, which you shouldn't have been doing, anyway!).
Nullability and Swift Optionals
FMDB 2.7 is largely the same as prior versions, but has been audited for nullability. For Objective-C users, this simply means that if you perform a static analysis of your FMDB-based project, you may receive more meaningful warnings as you review your project, but there are likely to be few, if any, changes necessary in your code.
For Swift users, this nullability audit results in changes that are not entirely backward compatible with FMDB 2.6, but is a little more Swifty. Before FMDB was audited for nullability, Swift was forced to defensively assume that variables were optional, but the library now more accurately knows which properties and method parameters are optional, and which are not.
This means, though, that Swift code written for FMDB 2.7 may require changes. For example, consider the following Swift 3/Swift 4 code for FMDB 2.6:
queue.inTransaction { db, rollback in
do {
guard let db == db else {
// handle error here
return
}
try db.executeUpdate("INSERT INTO foo (bar) VALUES (?)", values: [1])
try db.executeUpdate("INSERT INTO foo (bar) VALUES (?)", values: [2])
} catch {
rollback?.pointee = true
}
}
Because FMDB 2.6 was not audited for nullability, Swift inferred that db and rollback were optionals. But, now, in FMDB 2.7, Swift now knows that, for example, neither db nor rollback above can be nil, so they are no longer optionals. Thus it becomes:
queue.inTransaction { db, rollback in
do {
try db.executeUpdate("INSERT INTO foo (bar) VALUES (?)", values: [1])
try db.executeUpdate("INSERT INTO foo (bar) VALUES (?)", values: [2])
} catch {
rollback.pointee = true
}
}
Custom Functions
In the past, when writing custom functions, you would have to generally include your own @autoreleasepool block to avoid problems when writing functions that scanned through a large table. Now, FMDB will automatically wrap it in an autorelease pool, so you don't have to.
Also, in the past, when retrieving the values passed to the function, you had to drop down to the SQLite C API and include your own sqlite3_value_XXX calls. There are now FMDatabase methods, valueInt, valueString, etc., so you can stay within Swift and/or Objective-C, without needing to call the C functions yourself. Likewise, when specifying the return values, you no longer need to call sqlite3_result_XXX C API, but rather you can use FMDatabase methods, resultInt, resultString, etc. There is a new enum for valueType called SqliteValueType, which can be used for checking the type of parameter passed to the custom function.
Thus, you can do something like (as of Swift 3):
db.makeFunctionNamed("RemoveDiacritics", arguments: 1) { context, argc, argv in
guard db.valueType(argv[0]) == .text || db.valueType(argv[0]) == .null else {
db.resultError("Expected string parameter", context: context)
return
}
if let string = db.valueString(argv[0])?.foldin
