SecureDatabaseStore

public class SecureDatabaseStore

SecureDatabaseStore


The Secure Storage component provides transparent 256-bit AES encryption of data stored either in a SQLite database or a persistent key-value store. See https://www.sqlite.org/docs.html for more information about SQLite.

Note: This component is only using encryption mechanisms which are available in iOS.

Usage

There are four main classes in the SecureStorage module:

  1. SecureDatabaseStore - represents a single SQLite database, used for executing SQL statements. The SecureDatabaseStore is not thread safe. For thread safety see SecureDatabaseQueue and the Thread Safety section.
  2. SecureDatabaseQueue - use this class to perform queries and updates on multiple threads. See Thread Safety for details.

  3. SecureDatabaseResultSet - represents the results of executing a query on a SecureDatabaseStore.

  4. SecureKeyValueStore - represents a key-value store, used for storing data in a non-relational way.

Store Creation

Every store (either a SecureDatabaseStore, a SecureDatabaseQueue or a SecureKeyValueStore) is created with its constructor. A database or a key-value store type can be created with one of the following three options:

  1. A default location for the database file. No parameters need to be passed to the constructor.
  2. A databaseFileName: String parameter that specifies the file name and the file extension of the database file.
  3. A fullDatabasePath: String parameter that specifies the full file path of the database.

The SecureKeyValueStore is backed by a SQLite database, which is why its creation methods can be called with a file name and a directory.

A SecureDatabaseQueue is a wrapper around a SecureDatabaseStore and must be initialized with a database store object.

Setting an Encryption Key

After initializing SecureDatabaseStore or SecureKeyValueStore an initial encryption key is not set. To set the encryption key of a store, call the open(encryptionKey: String) method. This sets the encryption key when the store is opened for the first time and when data is written afterwards.

Database

Creating

// for example if you want to store your file in the document directory:
let fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fullPath = fileURL.appendingPathComponent("myDatabase.db").path

//or let fullPath: String = "<#/path/to/database/directory/database.db#>"

let db = SecureDatabaseStore(fullDatabasePath: fullPath)

Opening

You must open the store before you can interact with it. The store fails to open if there are insufficient resources or permissions to open and/or create the store. The store can also fail to open if the encryption key is an empty string or the wrong key is used. When the store is opened for the first time, the provided key is set to that of the database’s encryption key:

do {
    try db.open(with: "encryptionKey")
} catch let error {
    // log the error using Logger
    logger.error("An error occurred while opening the database.", error: error)
}

Executing Updates

Any SQL statement that is not a SELECT statement qualifies as an update, including CREATE, UPDATE, INSERT, ALTER, COMMIT, BEGIN, DETACH, DELETE, DROP, END, EXPLAIN, VACUUM, and REPLACE statements (and many more). Basically, if your SQL statement does not begin with SELECT, it is an update statement.

Executing updates can throw errors if something fails:

do {
    try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [1])
    try db.executeUpdate("UPDATE myTable SET column=:value", parameterDictionary: [":value" : 2])
} catch let error {
    logger.error("An error occurred while executing an update or an insert on the database.", error: error)
}

Executing Queries

A SELECT statement is a query and executes via one of the executeQuery(...) methods.

Executing queries returns a SecureDatabaseResultSet object if successful, and throws an error upon failure.

You typically use a while() loop to iterate over the results of your query. You also need to step from one record to the next. The easiest way to do this is by using this code:

do {
    let rs = try db.executeQuery("SELECT * FROM myTable")
    defer {
        rs.close()
    }

    while try rs.next() {
        //retrieve values for each record
    }
} catch let error {
    logger.error("An error occurred while executing a query on the database.", error: error)
}

You must always invoke SecureDatabaseResultSet.next() before attempting to access the values returned in a query, even if you’re only expecting one:

do {
    let rs = try db.executeQuery("SELECT COUNT(*) FROM myTable")
    defer {
        rs.close()
    }

    if try rs.next() {
        let totalCount = rs.int(forColumnIndex: 0)
    }
} catch let error {
    logger.error("An error occurred while executing a query on the database.", error: error)
}

A SecureDatabaseResultSet contains many methods for retrieving different data types in an appropriate format:

  • int(forColumnName columnName: String) -> Int?
  • int64(forColumnName columnName: String) -> Int64?
  • uint64(forColumnName columnName: String) -> UInt64?
  • bool(forColumnName columnName: String) -> Bool?
  • double(forColumnName columnName: String) -> Double?
  • string(forColumnName columnName: String) -> String?
  • date(forColumnName columnName: String) -> Date?
  • data(forColumnName columnName: String) -> Data?
  • object<T: SQLiteDatatypeBridgeable>(forColumnName columnName: String) throws -> T?

Each of these methods also has a {type}(forColumnIndex columnIndex: Int) -> {type}? variant that is used to retrieve the data based on the position of the column in the results, as opposed to the column’s name.

Typically, there’s no need to close() a SecureDatabaseResultSet yourself, since that happens when either the result set is deallocated, or the parent database is closed. However, we recommend closing the result set explicitly.

Closing

When you finish executing queries and updates on the database, close() the SecureDatabaseStore so that SQLite relinquishes any resources it has acquired during the course of its operation:

db.close()

Multiple Statements and Batch Commands

You can use SecureDatabaseStore‘s executeStatements(sql:, resultBlock:) to execute multiple statements in a string:

let sql = "CREATE TABLE bulktest1 (id INTEGER PRIMARY KEY AUTOINCREMENT, x TEXT);" +
    "CREATE TABLE bulktest2 (id INTEGER PRIMARY KEY AUTOINCREMENT, y TEXT);" +
    "CREATE TABLE bulktest3 (id INTEGER PRIMARY KEY AUTOINCREMENT, z TEXT);" +
    "INSERT INTO bulktest1 (x) VALUES ('XXX');" +
    "INSERT INTO bulktest2 (y) VALUES ('YYY');" +
    "INSERT INTO bulktest3 (z) VALUES ('ZZZ');"

do {
    try db.executeStatements(sql)
} catch let error {
    logger.error("An error occurred while executing a statements on the database.", error: error)
}
let sql = "SELECT COUNT(*) AS count FROM bulktest1;" +
    "SELECT COUNT(*) AS count FROM bulktest2;" +
    "SELECT COUNT(*) AS count FROM bulktest3;"

do {
    try db.executeStatements(sql) { resultDictionary in
        if let count = resultDictionary["count"] {
            self.logger.info("bulktest1 contains \(count) rows")
            return true
        } else {
            self.logger.info("Something went wrong. Stop executing all following statements.")
            return false
        }
    }
} catch let error {
    logger.error("An error occurred while executing statements on the database.", error: error)
}

Data Sanitization

When providing a SQL statement, use the standard SQLite binding syntax. Use placeholders for values to be inserted, updated or used in WHERE clauses in SELECT statements.

Do not construct the SQL String yourself from variables like this:

let sql = "INSERT INTO myTable VALUES (\(value1), \(value1), \(value1))"

Instead, use the standard SQLite binding syntax like this:

let sql = "INSERT INTO myTable VALUES (?, ?, ?)"
let argsArray = ["MyId", "MyName", "MyValue"]
try db.executeUpdate(sql, values: argsArray)

The ? character is recognized by SQLite as a placeholder for a value to be inserted. The execution methods all accept a representation of arguments in an Array or a Dictionary, which are then properly escaped for you.

Alternatively, you can use named parameter syntax:

let sql = "INSERT INTO myTable VALUES (:id, :name, :value)"
let argsDict = ["id" : "MyId", "name" : "MyName", "value" : "MyValue"]
try db.executeUpdate(sql, parameterDictionary: argsDict)

The parameters must start with a colon. SQLite supports other characters, but internally the Dictionary keys are prefixed with a colon.

Do not include the colon in your dictionary keys.

SQLite does not allow the binding syntax for table, column or schema names.

Shared store

The SecureDatabaseStore doesn’t support sharing capability. If the shared store is mandatory for a scenario it is recommended to use the KeychainStorage. For more information about KeychainStorage and the details about sharing store see the KeychainStorage documentation.

Thread Safety

It is not safe to use a single instance of a SecureDatabaseStore from multiple threads. You can make a SecureDatabaseStore object per thread, but do not share a single instance across threads or across multiple threads at the same time.

Do not instantiate a single SecureDatabaseStore object and use it across multiple threads directly.

Instead, use a SecureDatabaseQueue. The SecureDatabaseQueue class is a wrapper around a SecureDatabaseStore, which can perform multiple database calls on a serial queue making more calls thread safe at a time. Therefore, it is thread-safe with respect to direct calls on the SecureDatabaseQueue. First, make a queue with your SecureDatabaseStore instance:

let queue = SecureDatabaseQueue(databaseStore: db)

Then you can perform operations on the database such as:

do {
    try queue.inDatabase { db in
        try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [1])
        try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [2])

        let rs = try db.executeQuery("SELECT * from myTable")
        defer {
            rs.close()
        }

        while try rs.next() {
            //...
        }
    }
} catch let error {
    logger.error("An error occurred while executing a updates on a database queue.", error: error)
}

You can also wrap multiple operations in a transaction like this:

do {
    try queue.inExclusiveTransaction { db in
        do {
            try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [1])
            try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [2])
        } catch {
            return true // perform a rollback
        }
    return false
    }
} catch let error {
    logger.error("An error occurred while executing a updates on a database queue.", error: error)
}

Key-Value Store

The SecureKeyValueStore is based on a SQLite database. It can store any values that conform to the NSCoding protocol.

Its lifecycle is similar to that of a SecureDatabaseStore (see the Database section above). It provides methods to open and close the store, as well as for changing the encryption key. The encryption key of a SecureKeyValueStore is initially set when you make the first call to open(enryptionKey: String).

A SecureKeyValueStore has many methods to set and retrieve different data types in an appropriate format:

Put

  • put<T: NSCoding>(_ value: T?, forKey key: String) throws
  • put<T: SQLiteDatatypeBridgeable>(_ value: T?, forKey key: String) throws
  • putAll(_ keyValueDict: Dictionary<String, NSCoding?>) throws

Get

You must specify the type of data to retrieve it from the key value store. Conversions between different types of numbers may produce erroneous results. Such as putting an Int64.max for a specific key and getting this value with type: Int8. Conversions are handled in accordance to the behavior of NSNumber. See https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSNumber_Class/index.html for more information.

  • get<T: NSCoding>(_ key: String) throws -> T?
  • get<T: SQLiteDatatypeBridgeable>(_ key: String) throws -> T?
  • getString(_ key: String) throws -> String?
  • getInt8(_ key: String) throws -> Int8?
  • getInt16(_ key: String) throws -> Int16?
  • getInt32(_ key: String) throws -> Int32?
  • getInt64(_ key: String) throws -> Int64?
  • getInt(_ key: String) throws -> Int?
  • getUInt8(_ key: String) throws -> UInt8?
  • getUInt16(_ key: String) throws -> UInt16?
  • getUInt32(_ key: String) throws -> UInt32?
  • getUInt64(_ key: String) throws -> UInt64?
  • getUInt(_ key: String) throws -> UInt?
  • getFloat(_ key: String) throws -> Float?
  • getDouble(_ key: String) throws -> Double?
  • getBool(_ key: String) throws -> Bool?
  • getData(_ key: String) throws -> NSData?

Get with default value

These methods work the same way as the get methods, except that you can specify a defaultValue if no mapping of the desired type exists for the given key or if a nil value is explicitly associated with the given key:

  • get<T: NSCoding>(_ key: String, defaultValue: T?) throws -> T?
  • get<T: SQLiteDatatypeBridgeable>(_ key: String, defaultValue: T?) throws -> T?
  • getString(_ key: String, defaultValue: String?) throws -> String?
  • getInt8(_ key: String, defaultValue: Int8?) throws -> Int8?
  • getInt16(_ key: String, defaultValue: Int16?) throws -> Int16?
  • getInt32(_ key: String, defaultValue: Int32?) throws -> Int32?
  • getInt64(_ key: String, defaultValue: Int64?) throws -> Int64?
  • getInt(_ key: String, defaultValue: Int?) throws -> Int?
  • getUInt8(_ key: String, defaultValue: UInt8?) throws -> UInt8?
  • getUInt16(_ key: String, defaultValue: UInt16?) throws -> UInt16?
  • getUInt32(_ key: String, defaultValue: UInt32?) throws -> UInt32?
  • getUInt64(_ key: String, defaultValue: UInt64?) throws -> UInt64?
  • getUInt(_ key: String, defaultValue: UInt?) throws -> UInt?
  • getFloat(_ key: String, defaultValue: Float?) throws -> Float?
  • getDouble(_ key: String, defaultValue: Double?) throws -> Double?
  • getBool(_ key: String, defaultValue: Bool?) throws -> Bool?
  • getData(_ key: String, defaultValue: NSData?) throws -> NSData?

SecureStorage component Logger ID

This component uses the following name prefix for logging: 'SAP.Foundation.SecureStorage’

SecureDatabaseStore class

Represents a single SQLite database, can be used by executing SQL statements. The SecureDatabaseStore instance is not thread safe. Using directly from multiple threads can lead to unexpected behaviors. To use it a thread-safe way use the SecureDatabaseQueue. To initialize a default SecureDatabaseStore you would use the following lines somewhere central in your application as soon as you have an encryption key for that store.

var secureDatabaseStore = SecureDatabaseStore()

try secureDatabaseStore.open(with: "your_encryption_key")

You can read from and write to the SecureDatabaseStore instance as long as it is not being closed.

To create the SecureDatabaseStore at a custom location, please use the following code:

let customDatabasePath = "/your_custom_dir/your_custom_name.db"
var secureDatabaseStore = SecureDatabaseStore(fullDatabasePath: customDatabasePath)

try secureDatabaseStore.open(with: "your_encryption_key")
  • The URL of the database.

    Declaration

    Swift

    public let databaseURL: URL
  • Initialize a new SecureDatabaseStore in the default location with the specified or default database file name.

    Declaration

    Swift

    public convenience init(databaseFileName: String = "SAPcpSDKDefaultSecureDatabaseStore.db")

    Parameters

    databaseFileName

    a database file name with a default value

  • Initialize a new SecureDatabaseStore in the specified path.

    Declaration

    Swift

    public convenience init(fullDatabasePath: String)

    Parameters

    fullDatabasePath

    the full path of the database file name

  • Opens the SecureStore with the respective encryption key. This call sets the encryption key of a store if the store is opened for the first time and if data is written afterwards. The underlying store is opened for read and write access and it is created at the specified path if it does not already exist.

    Note

    The actual database encryption key is drived from the String encryptionKey with PBKDF2.

    Note

    In case the store is already opened the method doesn’t have any effect and it won’t validate the passcode.

    Throws

    Throws:

    Declaration

    Swift

    public func open(with encryptionKey: String, initializationHandler: ((SecureDatabaseStore) -> Error?) = SecureDatabaseStore.defaultInitializationHandler ) throws

    Parameters

    encryptionKey

    the secure store’s encryption key as String.

    initializationHandler

    called when the SecureDatabaseStore is created the first time. Not called if the database file exists before the open. Provide your database initialization code here. The closure is called serially.

  • Opens the SecureStore with the respective encryption key. The underlying store is opened for read and write access. The store at the specified path is created if it does not exist.

    Warning

    The binary encryptionKeyData data is used directly as the database encryption key. It must be exactly 256 bit in length.

    Throws

    Throws:

    Declaration

    Swift

    public func open(with encryptionKeyData: Data, initializationHandler: (SecureDatabaseStore) -> Error? = SecureDatabaseStore.defaultInitializationHandler) throws

    Parameters

    encryptionKeyData

    the secure store’s binary encryption key as Data. It must be exactly 256 bit in length.

    initializationHandler

    called when the SecureDatabaseStore is created the first time. Not called if the database file exists before the open. Provide your database initialization code here. The closure is called serially.

  • Returns the state of the store

    Declaration

    Swift

    public func isOpen() -> Bool

    Return Value

    true if the store is open and can be used false otherwise

  • Closes the database. No further access is possible.

    Declaration

    Swift

    public func close()
  • Changes the encryption key of the SecureStore if it has been opened. Otherwise SecureStorageError.Closed is thrown and the encryption key remains unchanged. Changing the key means re-encryption of the whole database which can take a long time on larger databases.

    Throws

    SecureStorageError.Closed if the store is locked with its encryption key.

    Throws

    SecureStorageError.EncryptionKeyChangeFailed if an error occured while changing the encryption key.

    Declaration

    Swift

    public func changeEncryptionKey(with newEncryptionKey: String) throws

    Parameters

    newEncryptionKey

    the SecureStore’s new encryption key as String.

  • Changes the encryption key of the SecureStore if it has been opened. Otherwise SecureStorageError.Closed is thrown and the encryption key remains unchanged. Changing the key means re-encryption of the whole database which can take a long time on larger databases.

    Throws

    SecureStorageError.Closed if the store is locked with its encryption key.

    Throws

    SecureStorageError.EncryptionKeyChangeFailed if an error occured while changing the encryption key.

    Declaration

    Swift

    public func changeEncryptionKey(with newEncryptionKeyData: Data) throws

    Parameters

    newEncryptionKeyData

    the SecureStore’s new encryption key.

  • Execute single update statement

    This method executes a single SQL update statement (i.e. any SQL that does not return results, such as UPDATE, INSERT, or DELETE). This method employs sqlite3_prepare_v2.

    try db.executeUpdate("INSERT INTO myTable VALUES ('value')")
    

    Throws

    Declaration

    Swift

    public func executeUpdate(_ sql: String) throws

    Parameters

    sql

    The SQL to be performed.

  • Execute single update statement

    This method executes a single SQL update statement (i.e. any SQL that does not return results, such as UPDATE, INSERT, or DELETE). This method employs sqlite3_prepare_v2 and sqlite3_bind binding any ? placeholders in the SQL with the optional list of parameters.

    The optional values provided to this method must be SQLiteDatatypeBridgeable compliant types. The following table contains the binding relations for these types to SQLite3 types.

    Swift Type SQLite3 Type
    nil NULL
    Data BLOB
    Date INTEGER
    Int, Int8-Int64, UInt, UInt8-UInt64, Bool INTEGER
    Float, Double REAL
    String TEXT

    More information can be found at http://www.sqlite.org/datatype3.html. Date objects are stored as timestamps in the format of seconds since 01.01.1970 without timezone information. Sub-second time information is lost.

    try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [1])
    

    Throws

    Declaration

    Swift

    public func executeUpdate(_ sql: String, values: [SQLiteDatatypeBridgeable?]) throws

    Parameters

    sql

    The SQL to be performed, with optional ? placeholders.

    values

    An Array<SQLiteDatatypeBridgeable?> of objects to be used when binding values to the ? placeholders in the SQL statement.

  • Execute single update statement

    This method executes a single SQL update statement (i.e. any SQL that does not return results, such as UPDATE, INSERT, or DELETE). This method employs sqlite3_prepare_v2 and sqlite3_bind binding any : placeholders in the SQL with the optional list of parameters.

    The optional values provided to this method must be SQLiteDatatypeBridgeable compliant types. See executeUpdate(sql:, values:) for more information.

    try db.executeUpdate("INSERT INTO myTable VALUES (:value)", parameterDictionary: ["value" : 1])
    

    Throws

    Declaration

    Swift

    public func executeUpdate(_ sql: String, parameterDictionary: [String : SQLiteDatatypeBridgeable?]) throws

    Parameters

    sql

    The SQL to be performed, with optional : placeholders.

    parameterDictionary

    A [String: SQLiteDatatypeBridgeable?] of objects keyed by column placeholder names (without colons) that will be used when binding values to the : placeholders in the SQL statement.

  • Execute multiple SQL statements

    This executes a series of SQL statements that are combined in a single string (e.g. the SQL generated by the sqlite3 command line .dump command). This accepts no value parameters, but rather simply expects a single string with multiple SQL statements, each terminated with a semicolon. This uses sqlite3_exec.

    Attention

    To avoid vulnerability CVE-2018-8740 present in the lastest SQLCipher/SQLite libraries the following rules need to be followed:
  • An application should never execute ‘CREATE TABLE AS’ via ‘executeStatements’. Instead, create an empty table with ‘CREATE TABLE’ and insert the data in a separate statement afterwards.
  • If the application uses ‘executeStatements’, it should never expose it in a way that statements from an external source could be executed without proper validation.
  • For more information please read SAP Note 2696962.

    let sql = "CREATE TABLE bulktest1 (id INTEGER PRIMARY KEY AUTOINCREMENT, x TEXT);" +
        "CREATE TABLE bulktest2 (id INTEGER PRIMARY KEY AUTOINCREMENT, y TEXT);" +
        "CREATE TABLE bulktest3 (id INTEGER PRIMARY KEY AUTOINCREMENT, z TEXT);"
    
    do {
        try db.executeStatements(sql)
    } catch let error {
        logger.error("An error occurred while executing statements.", error: error)
    }
    

    Throws

    Declaration

    Swift

    public func executeStatements(_ sql: String) throws

    Parameters

    sql

    The SQL to be performed.

  • Execute multiple SQL statements with callback handler

    This executes a series of SQL statements that are combined in a single string (e.g. the SQL generated by the sqlite3 command line .dump command). This accepts no value parameters, but rather simply expects a single string with multiple SQL statements, each terminated with a semicolon. This uses sqlite3_exec.

    Attention

    To avoid vulnerability CVE-2018-8740 present in the lastest SQLCipher/SQLite libraries the following rules need to be followed:
  • An application should never execute ‘CREATE TABLE AS’ via ‘executeStatements’. Instead, create an empty table with ‘CREATE TABLE’ and insert the data in a separate statement afterwards.
  • If the application uses ‘executeStatements’, it should never expose it in a way that statements from an external source could be executed without proper validation.
  • For more information please read SAP Note 2696962.

    let sql = "SELECT COUNT(*) AS count FROM bulktest1;" +
    "SELECT COUNT(*) AS count FROM bulktest2;" +
    "SELECT COUNT(*) AS count FROM bulktest3;"
    
    do {
        try db.executeStatements(sql) { resultDictionary in
            if let count = resultDictionary["count"] {
                self.logger.info("bulktest1 contains \(count) rows")
                return true
            } else {
                self.logger.info("Something went wrong. Stop executing all following statements.")
                return false
            }
        }
    } catch let error {
        logger.error("An error occurred while executing statements.", error: error)
    }
    

    Throws

    Declaration

    Swift

    public func executeStatements(_ sql: String, resultBlock: @escaping (_ results: [String: String?]) -> Bool) throws

    Parameters

    sql

    The SQL to be performed.

    resultBlock

    A block that will be called for any result row returned by any SQL statements. Note, if you supply this block, it must return a Bool value; true upon success, false upon failure (which will stop the bulk execution of the SQL). If a statement returns values, the block will be called with a Dictionary of the result row from the query as a parameter. The Dictionary is keyed with the column names and contains result values as if they were obtained with stringForColumn(columnName: String) on a result set.

  • Last insert rowid

    Each entry in an SQLite table has a unique 64-bit signed integer key called the rowid. The rowid is always available as an undeclared column named ROWID, OID, or _ROWID_ as long as those names are not also used by explicitly declared columns. If the table has a column of type INTEGER PRIMARY KEY then that column is another alias for the rowid.

    This routine returns the rowid of the most recent successful INSERT into the database from the database connection in the first argument. This routine records the last insert rowid of both ordinary tables and virtual tables. If no successful INSERTs have ever occurred on that database connection, zero is returned.

    Throws

    SecureStorageError.closed if the SecureDatabaseStore is locked with its encryption key.

    Declaration

    Swift

    public func lastInsertRowId() throws -> Int64

    Return Value

    The rowid of the last inserted row. 0 if no successful INSERTs have ever occurred.

  • The number of rows changed by the previous SQL statement.

    This function returns the number of database rows that were changed or inserted or deleted by the most recently completed SQL statement on the database. Only changes that are directly specified by the INSERT, UPDATE, or DELETE statement are counted.

    Throws

    SecureStorageError.closed if the SecureDatabaseStore is locked with its encryption key.

    Declaration

    Swift

    public func changedRowCount() throws -> Int

    Return Value

    The number of rows changed by the previous SQL statement.

  • Execute select statement

    Executing queries returns an SecureDatabaseResultSet object if successful, and throws an error upon failure.

    try executeQuery("SELECT COUNT(*) FROM myTable")
    

    Throws

    Declaration

    Swift

    public func executeQuery(_ sql: String) throws -> SecureDatabaseResultSet

    Parameters

    sql

    The SELECT statement to be performed.

    Return Value

    A SecureDatabaseResultSet for the result set upon success.

  • Execute select statement

    Executing queries returns an SecureDatabaseResultSet object if successful, and throws an error upon failure. The optional values provided to this method must be SQLiteDatatypeBridgeable compliant types. See executeUpdate(sql:, values:) for more information.

    try executeQuery("SELECT * FROM myTable WHERE columnName=?", values: ["MyValue"])
    

    Throws

    Declaration

    Swift

    public func executeQuery(_ sql: String, values: [SQLiteDatatypeBridgeable?]) throws -> SecureDatabaseResultSet

    Parameters

    sql

    The SELECT statement to be performed, with optional ? placeholders.

    values

    An Array<SQLiteDatatypeBridgeable?> of objects to be used when binding values to the ? placeholders in the SQL statement.

    Return Value

    A SecureDatabaseResultSet for the result set upon success.

  • Execute select statement

    Executing queries returns an SecureDatabaseResultSet object if successful, and throws an error upon failure. The optional values provided to this method must be SQLiteDatatypeBridgeable compliant types. See executeUpdate(sql:, values:) for more information.

    try executeQuery("SELECT * FROM myTable WHERE columnName=:value", values: ["value" : "MyValue"])
    

    Throws

    Declaration

    Swift

    public func executeQuery(_ sql: String, parameterDictionary: [String : SQLiteDatatypeBridgeable?]) throws -> SecureDatabaseResultSet

    Parameters

    sql

    The SELECT statement to be performed, with optional : placeholders.

    parameterDictionary

    A [String: SQLiteDataTypConvertible?] of objects keyed by column placeholder names (without colons) that will be used when binding values to the : placeholders in the SQL statement.

    Return Value

    A SecureDatabaseResultSet for the result set upon success.

  • Begin an exclusive transaction. Therefore, exclusive locks are acquired on the database and no other connection will be able to read or write to the database until the transaction is complete.

    Throws

    Declaration

    Swift

    public func beginExclusiveTransaction() throws
  • Begin a deferred transaction. Therefore, no locks are acquired on the database until the database is first accessed.

    Throws

    Declaration

    Swift

    public func beginDeferredTransaction() throws
  • Commit a transaction

    Throws

    Declaration

    Swift

    public func commit() throws
  • Rollback a transaction

    Throws

    Declaration

    Swift

    public func rollback() throws
  • Identify whether currently in a transaction or not

    Throws

    SecureStorageError.closed if the SecureDatabaseStore is locked with its encryption key.

    Declaration

    Swift

    public func isInTransaction() throws -> Bool

    Return Value

    true if currently within transaction; false otherwise.

  • Whether the database should cache statements or not. true if it should cache, false otherwise.

    Declaration

    Swift

    public var shouldCacheStatements: Bool { get set }
  • Clear cached statements

    Declaration

    Swift

    public func clearCachedStatements()
  • Whether the database has any open result sets. true if there are open result sets, false otherwise.

    Declaration

    Swift

    public var hasOpenResultSets: Bool { get }
  • Close all open result sets

    Declaration

    Swift

    public func closeOpenResultSets()
  • The default initialization handler for the SecureStore that does nothing end returns nil.

    Declaration

    Swift

    public static func defaultInitializationHandler(_ store: SecureDatabaseStore) -> Error?