Swift for Xojo Developers - Part 11 - SQLite Wrapper

Previous to Swift 4, in order to use SQLite it required a bunch of obsure references and wrappers to ObjC. And even when Apple added the SQLite3 framework it was still not a comfortable and easy as the Xojo Syntax is/was (my opinion). So I created a series of classes that replicated that syntax as best I could.

The actual code module is over 1100 lines, so all I am posting here is the structure and signatures for the public methods and functions.

Comments and feedback on this or any of the other postings is welcome. It helps to know if others find these posts helpful? interesting etc.

NOTE : This DOES NOT SUPPORT ENCRYPTED DATABASES, as this is a function of the background SQLite install,

//
//  SQLiteRDS.swift
//  A Swift5 Wrapper for SQLite Database
//
//  Created by David Sisemore on 22Oct2017
//  Copyright © 2017 David Sisemore. All rights reserved.
//

import UIKit
import Foundation
import SQLite3

class SQLiteDatabase {
    var DatabaseFile : String {}
    var ShortColumnNames : Bool {}
    var Error          : Bool   {}
    var ErrorCode      : Int    {}
    var ErrorMessage   : String {}
    var LibraryVersion : String {}
    var isConnected    : Bool   {}
    var LastRowID      : Int    {} // FUTURE
    func connect()            -> Bool{ }
    func CreateDatabaseFile() -> Bool { }
    func AttachDatabase(file:String,databaseName:String,_ Password:String="") -> Bool {}
    func DetachDatabase(databaseName:String) {}
    func Close() {}
    func commit() { /* FUTURE */ }
    func Rollback() { /* FUTURE */ }
    func TableExists(_ tableName:String) -> Bool {}
    func ViewExists(_ viewName:String) -> Bool {}
    func TableSchema() -> SQLiteRecordSet {}
    func ViewSchema() -> SQLiteRecordSet {}
    func FieldSchema(_ tableName:String) -> SQLiteRecordSet {}
    func IndexSchema(_ tableName:String) -> SQLiteRecordSet {}
    func SQLSelect(_ sql:String, parameters:[AnyObject]?=nil)->SQLiteRecordSet {}
    func SQLExecute(_ sql:String, parameters:[AnyObject]?=nil) -> CInt {}
} 

class SQLiteRecordSet {
    var BOF         : Bool {}
    var EOF         : Bool {}
    var recordCount : Int  {}
    var fieldCount  : Int  {}
    func MoveFirst()    {}
    func MoveLast()     {}
    func MoveNext()     {}
    func MovePrevious() {}
    func Close() {}
    func ColumnType(_ fieldName:String) -> CInt {}
    func ColumnType(_ keyIndex:Int) -> CInt {}
    func Field(_ fieldName:String) -> SQLiteDatabaseField {}
    func Field(_ keyIndex:Int) -> SQLiteDatabaseField {}
    func IdxField(_ index:Int) -> SQLiteDatabaseField {}
}

class SQLiteDatabaseField {
    var name : String { }
    func boolValue() -> Bool {}
    func textValue() -> String {}
    func stringValue()->String {}
    func integerValue()->Int {}
    func int32Value()  -> Int32 {}
    func int64Value()  -> Int64 {}
    func floatValue()  -> Float {}
    func doubleValue() -> Double {}
    func dateValue()   -> Date? {}
    func nativeValue() -> Data? {}
    func description() -> String {}
}

class SQLRow {
    var data = [String : SQLiteDatabaseField]()
    subscript(key: String) -> SQLiteDatabaseField? {}
    func description()->String { return data.description }
}

private class SQLiteException {
    func SetError(_ errCode:CInt,errMsg:String="") {}
    var Error : Bool {}
    var ErrorCode : CInt {}
    var ErrorMessage : String {}
}
1 Like

Thanks for this Dave, how to handle SQLite database is on my list of topics to research as Sleep Aid uses one extensively. I knew there would be a way, I just had no idea how.

I have updated this series of classes for anyone interested…

  1. Added a BACKUP function, that copies the entire database to another
 let f : folderitem = SpecialFolder.applicationSupport.child("backup.db")
 let _ = DB.backup(f))
  1. removed the PREPAREDSTATEMENT and corresponding BIND functions , this function is now an extension of SQLExecute
 SQL="Insert into \(mainTABLE) values(?,?,?,?)"
 let _ = DB.SQLExecute(SQL, [1,2,"F","D"])
let _  = DB.SQLExecute("Insert into \(mainTABLE) values(11,22,'ff','dd')")

First example is “injection safe”, Second is not

EDIT:
Also added a function to quickly connect to a SQLite Database

public func openDatabase(_ path:folderitem,createNew:Bool=false) -> SQLiteDatabase? 

returns the DB instance if successful, or NIL if there was an error

Note : folderitem is in another of my framework modules

This same group of classes is compatible with macOS, iOS and tvOS

Also supports this syntax for Prepared Statement

 _ = DB.SQLExecute(SQL,123,234,"Fff","Ddd")

No need for brackets

 public func SQLExecute(_ sql:String,_ arg : Any...) -> CInt {