Code Monkey home page Code Monkey logo

swiftdata's Introduction

SwiftData

Working with SQLite is a pain in Swift - that's where SwiftData comes in.

SwiftData is a simple and effective wrapper around the SQLite3 C API written completely in Swift.

##Features

  • Execute SQL statements directly
  • Bind objects conveniently to a string of SQL
  • Queries return an easy to use array
  • Support for transactions and savepoints
  • Inline error handling
  • Completely thread-safe by default
  • Convenience functions for common tasks, including table and index creation/deletion/querying
  • Database connection operations (opening/closing) are automatically handled

##Installation

Currently, it's as easy as adding the file 'SwiftData.swift' as a git submodule, and dragging it into your project. Ensure that you've added 'libsqlite3.dylib' as a linked framework and that you've added #import "sqlite3.h" to your Briding-Header.h file.

##System Requirements

Xcode Version:

  • Xcode 6

Can be used in applications with operating systems:

  • iOS 7.0+
  • Mac OS X 10.9+

##Usage

This section runs through some sample usage of SwiftData.

The full API documentation can be found here

###Table Creation

By default, SwiftData creates and uses a database called 'SwiftData.sqlite' in the 'Documents' folder of the application.

To create a table in the database, you may use the convenience function:

if let err = SD.createTable("Cities", withColumnNamesAndTypes: ["Name": .StringVal, "Population": .IntVal, "IsWarm": .BoolVal, "FoundedIn": .DateVal]) {
    //there was an error during this function, handle it here
} else {
    //no error, the table was created successfully
}

Similar convenience functions are provided for:

  • deleting a table:
let err = SD.deleteTable("TableName")
  • finding all existing tables in the database:
let (tables, err) = SD.existingTables()

Alternatively, a table could be created using a SQL statement directly, as shown in the 'Execute A Change' section below.

================= ###Execute A Change

The SD.executeChange() function can be used to execute any non-query SQL statement (e.g. INSERT, UPDATE, DELETE, CREATE, etc.).

To create a table using this function, you could use the following:

if let err = SD.executeChange("CREATE TABLE Cities (ID INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT, Population INTEGER, IsWarm BOOLEAN, FoundedIn DATE)") {
    //there was an error during this function, handle it here
} else {
    //no error, the table was created successfully
}

The table created by this function call is the equivalent of the convenience function used in the earlier section.

Now that we've created our table, "Cities", we can insert a row into it like so:

if let err = SD.executeChange("INSERT INTO Cities (Name, Population, IsWarm, FoundedIn) VALUES ('Toronto', 2615060, 0, '1793-08-27')") {
    //there was an error during the insert, handle it here
} else {
    //no error, the row was inserted successfully
}

#####Binding Values

Alternatively, we could insert a row with object binding:

//from user input
let name: String = //user input
let population: Int = //user input
let isWarm: Bool = //user input
let foundedIn: NSDate = //user input

if let err = SD.executeChange("INSERT INTO Cities (Name, Population, IsWarm, FoundedIn) VALUES (?, ?, ?, ?)", withArgs: [name, population, isWarm, foundedIn]) {
    //there was an error during the insert, handle it here
} else {
    //no error, the row was inserted successfully
}

The provided objects will be escaped and will bind to the '?' characters (in order) in the string of SQL.

Be aware that although this uses similar syntax to prepared statements, it actually uses the public function:

let escValue = SD.escapeValue(object)

to escape objects internally, which you may also use yourself. This means that the objects will attempt to bind to ALL '?'s in the string of SQL, including those in strings and comments.

The objects are escaped and will bind to a SQL string in the following manner:

  • A String object is escaped and surrounded by single quotes (e.g. 'sample string')
  • An Int object is left untouched (e.g. 10)
  • A Double object is left untouched (e.g. 10.8)
  • A Bool object is converted to 0 for false, or 1 for true (e.g. 1)
  • An NSDate object is converted to a string with format 'yyyy-MM-dd HH:mm:ss' and surrounded by single quotes (e.g. '2014-08-26 10:30:28')
  • An NSData object is prefaced with an 'X' and converted to a hexadecimal string surrounded by single quotes (e.g. X'1956a76c')
  • A UIImage object is saved to disk, and the ID for retrieval is saved as a string surrounded by single quotes (e.g. 'a98af5ca-7700-4abc-97fb-60737a7b6583')

All other object types will bind to the SQL string as 'NULL', and a warning message will be printed to the console.

#####Binding Identifiers

If an identifier (e.g. table or column name) is provided by the user and needs to be escaped, you can use the characters 'i?' to bind the objects like so:

//from user input
let columnName1 = //user input
let columnName2 = //user input

if let err = SD.executeChange("INSERT INTO Cities (Name, Population, i?, i?) VALUES (?, ?, ?, ?)", withArgs: [columnName1, columnName2, name, population, isWarm, foundedIn]) {
    //there was an error during the insert, handle it here
} else {
    //no error, the row was inserted successfully
}

The objects 'columnName1' and 'columnName2' will bind to the characters 'i?' in the string of SQL as identifiers. Double quotes will be placed around each identifier. You may escape an identifier string yourself using the function:

let escIdentifier = SD.escapeIdentifier(identifier)

Objects provided to bind as identifiers must be of type String.

==================== ###Execute A Query

Now that our table has some data, we can query it:

let (resultSet, err) = SD.executeQuery("SELECT * FROM Cities")
if err != nil {
    //there was an error during the query, handle it here
} else {
    for row in resultSet {
        if let name = row["Name"]?.asString() {
            println("The City name is: \(name)")
        }
        if let population = row["Population"]?.asInt() {
            println("The population is: \(population)")
        }
        if let isWarm = row["IsWarm"]?.asBool() {
            if isWarm {
                println("The city is warm")
            } else {
                println("The city is cold")
            }
        }
        if let foundedIn = row["FoundedIn"]?.asDate() {
            println("The city was founded in: \(foundedIn)")
        }
    }
}

A query function returns a tuple of:

  • the result set as an Array of SDRow objects
  • the error code as an Optional Int

An SDRow contains a number of corresponding SDColumn objects. The values for each column can be obtained by using the column name in subscript format, much like a Dictionary. In order to obtain the column value in the correct data type, you may use the convenience functions:

  • asString()
  • asInt()
  • asDouble()
  • asBool()
  • asDate()
  • asData()
  • asAnyObject()
  • asUIImage()

If one of the above functions is not used, the value will be an SDColumn object.

For example, if you want the string value for the column "Name":

if let name = row["Name"]?.asString() {
    //the value for column "Name" exists as a String
} else
    //the value is nil, cannot be cast as a String, or the column requested does not exist
}

You may also execute a query using object binding, similar to the row insert example in an earlier section:

let (resultSet, err) = SD.executeQuery("SELECT * FROM Cities WHERE Name = ?", withArgs: ["Toronto"])
if err != nil {
    //there was an error during the query, handle it here
} else {
    for row in resultSet {
        if let name = row["Name"]?.asString() {
            println("The City name is: \(name)") //should be "Toronto"
        }
        if let population = row["Population"]?.asInt() {
            println("The population is: \(population)")
        }
        if let isWarm = row["IsWarm"]?.asBool() {
            if isWarm {
                println("The city is warm")
            } else {
                println("The city is cold")
            }
        }
        if let foundedIn = row["FoundedIn"]?.asDate() {
            println("The city was founded in: \(foundedIn)")
        }
    }
}

The same binding rules apply as described in the 'Execute a Change' section.

================= ###Error Handling

You have probably noticed that almost all SwiftData functions return an 'error' value.

This error value is an Optional Int corresponding to the appropriate error message, which can be obtained by calling the function:

let errMsg = SD.errorMessageForCode(err)

It is recommended to compare the error value with nil to see if there was an error during the operation, or if the operation was executed successfully.

By default, error and warning messages are printed to the console when they are encountered.

================= ###Creating An Index

To create an index, you may use the provided convenience function:

if let err = SD.createIndex("NameIndex", onColumns: ["Name"], inTable: "Cities", isUnique: true) {
    //there was an error creating the index, handle it here
} else {
    //the index was created successfully
}

Similar convenience functions are provided for:

  • removing an index:
let err = removeIndex("IndexName")
  • finding all existing indexes:
let (indexes, err) = existingIndexes()
  • finding all indexes for a specified table:
let (indexes, err) = existingIndexesForTable("TableName")

================= ###Custom Connection

You may create a custom connection to the database and execute a number of functions within a provided closure. An example of this can be seen below:

let task: ()->Void = {
    if let err = SD.executeChange("INSERT INTO Cities VALUES ('Vancouver', 603502, 1, '1886-04-06')") {
        println("Error inserting city")
    }
    if let err = SD.createIndex(name: "NameIndex", onColumns: ["Name"], inTable: "Cities", isUnique: true) {
        println("Index creation failed")
    }
}

if let err = SD.executeWithConnection(.ReadWrite, task) {
    //there was an error opening or closing the custom connection
} else {
    //no error, the closure was executed
}

The available custom connection flags are:

  • .ReadOnly (SQLITE_OPEN_READONLY)
  • .ReadWrite (SQLITE_OPEN_READWRITE)
  • .ReadWriteCreate (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)

All operations that occur within the provided closure are executed on the single custom connection.

For more information, see the SQLite documentation for opening a new database connection.

================= ###Transactions and Savepoints

If we wanted to execute the above closure task: ()->Void inside an exclusive transaction, it could be done like so:

if let err = transaction(task) {
    //there was an error starting, closing, committing, or rolling back the transaction as per the error code
} else {
    //the transaction was executed without errors
}

Similarly, a savepoint could be executed like so:

if let err = savepoint(task) {
    //there was an error starting, closing, releasing, or rolling back the savepoint as per the error code
} else {
    //the savepoint was executed without errors
}

It should be noted that transactions cannot be embedded into another transaction or savepoint. Unlike transactions, savepoints may be embedded into other savepoints or transactions.

For more information, see the SQLite documentation for transactions and savepoints.

================= ###Using UIImages

Convenience functions are provided for working with UIImages.

To easily save a UIImage to disk and insert the corresponding ID into the database:

let image = UIImage(named:"SampleImage")
if let imageID = SD.saveUIImage(image) {
    if let err = SD.executeChange("INSERT INTO SampleImageTable (Name, Image) VALUES (?, ?)", withArgs: ["SampleImageName", imageID]) {
        //there was an error inserting the new row, handle it here
    }
} else {
    //there was an error saving the image to disk
}

Alternatively, object binding can also be used:

let image = UIImage(named:"SampleImage")
if let err = SD.executeChange("INSERT INTO SampleImageTable (Name, Image) VALUES (?, ?)", withArgs: ["SampleImageName", image]) {
    //there was an error inserting the new row, handle it here
} else {
    //the image was saved to disk, and the ID was inserted into the database as a String
}

In the examples above, a UIImage is saved to disk and the returned ID is inserted into the database as a String. In order to easily obtain the UIImage from the database, the function '.asUIImage()' called on an SDColumn object may be used:

let (resultSet, err) = SD.executeQuery("SELECT * FROM SampleImageTable")
if err != nil {
    //there was an error with the query, handle it here
} else {
    for row in resultSet {
        if let image = row["Image"]?.asUIImage() {
            //'image' contains the UIImage with the ID stored in this column
        } else {
            //the ID is invalid, or the image could not be initialized from the data at the specified path
        }
    }
}

The '.asUIImage()' function obtains the ID as a String and returns the UIImage associated with this ID (or will return nil if the ID was invalid or a UIImage could not be initialized).

If you would like to delete the photo, you may call the function:

if SD.deleteUIImageWithID(imageID) {
    //image successfully deleted
} else {
    //there was an error deleting the image with the specified ID
}

This function should be called to delete the image with the specified ID from disk before the row containing the image ID is removed. Removing the row containing the image ID from the database does not delete the image stored on disk.

================= ###Thread Safety

All SwiftData operations are placed on a custom serial queue and executed in a FIFO order.

This means that you can access the SQLite database from multiple threads without the need to worry about causing errors.

##API Documentation

Full API Documentation can be found here

##Contact

Please open an issue for any comments, concerns, ideas, or potential pull requests - all welcome :)

##License

SwiftData is released under the MIT License.

See the LICENSE file for full details.

swiftdata's People

Contributors

ryanfowler avatar slobo avatar stewjacks avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swiftdata's Issues

add column in existing database

@ryanfowler I want to add column name in existing database table. How can I add it by using this library.

#################
Update:
I have sorted out this problem by adding new function in the library. I used ALTER query to modify existing table.

How create a database?

How create a database? ...
Much examples incomplets ...
Need a project using all functions ...

Check return value of transaction closure

Hi,

public static func transaction(transactionClosure: ()->Bool) -> Int? returns an Int with the error code, or nil if there was no error committing or rolling back the transaction.

But is it possible to check somehow if the transaction was committed or rollbacked?

Swift 2

Will you be updating to swift 2? If so any eta?

Thanks

Cleanup

I stole this for use at Mozilla :) But I wound up needing a few changes originally, and then slowly a lot more. My current work is at:

wesj/firefox-ios-1@debc4c2

It breaks API compatibility (a lot) and I just deleted a lot of code we didn't need. We'd love to not have a fork though. I'm happy (and another guy on our team has said he would be happy) to bring it back in line with your stuff and uplift things.

Some things I added:

  • custom file names
  • binds using sqlite_bind. I think this should remove the need for all the escaping, but I'd love to know if I'm wrong....
  • user_version
  • multiple connections (All calls now something like db.requestConnection(flags) { connection in
    // do stuff with the connection
    } I was hoping this would let me make things concurrent, but I don't think I have that working yet...
  • results are returned in a "Cursor" object right now. That's just a dumb wrapper we have around arrays like things, but it lets you dynamically grab rows as you want them instead of loading everything into memory. We can probably break that into its own piece, but we'd still need a way to get back the stmt from a query.
  • Use NSError rather than ints
  • Probably other things I've forgotten

I removed the:

  • Database creation helpers
  • savepoint support (I'd be happy to put that back, just removing cruft...)
  • image/date support. Again, I'd be happy to put that back. I originally revamped it to be a bit more general (i.e. You could register custom handlers for different classes), and I still think that would be a neat feature for it to have.
  • a lot of other helper methods I think

Any of this sound like things you'd like uplifted? I can pull them out one by one and try to split off our custom code where I can. Thanks!

DoNotBackup FLAG

Hello!

Is it possible to add a DoNotBackup flag or store the database file in /tmp ?

Thank you,
schube

Combining queries in transaction and outside transactions from multiple threads

I have an app that downloads bunch of data and saves it into DB, I save this data in transaction in background thread.
I also make simple SELECT queries from main thread that are not in any transaction. There are some weird crashes in my app (transaction closure, not sure where exactly) that led me to check this method:

private static func putOnThread(task: ()->Void) {
    if SQLiteDB.sharedInstance.inTransaction || SQLiteDB.sharedInstance.savepointsOpen > 0 || SQLiteDB.sharedInstance.openWithFlags {
        task()
    } else {
        dispatch_sync(SQLiteDB.sharedInstance.queue) {
            task()
        }
    }
}

If I am reading this correctly then it means that if I execute any query while one of transactions is in progress (in background thread), that new query will not be run in SD queue but it will be executed immediately. Is this intentional? Can it cause some problems in situation I described above?

it was not working swift3 here is the new code

//
// SwiftData.swift
//
// Copyright (c) 2015 Ryan Fowler
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import Foundation
import UIKit

// MARK: - SwiftData

public struct SwiftData {

// MARK: - Public SwiftData Functions


// MARK: - Execute Statements

/**
 Execute a non-query SQL statement (e.g. INSERT, UPDATE, DELETE, etc.)
 
 This function will execute the provided SQL and return an Int with the error code, or nil if there was no error.
 It is recommended to always verify that the return value is nil to ensure that the operation was successful.
 
 Possible errors returned by this function are:
 
 - SQLite errors (0 - 101)
 
 :param: sqlStr  The non-query string of SQL to be executed (INSERT, UPDATE, DELETE, etc.)
 
 :returns:       An Int with the error code, or nil if there was no error
 */
public static func executeChange(_ sqlStr: String) -> Int? {
    
    var error: Int? = nil
    let task: ()->Void = {
        if let err = SQLiteDB.sharedInstance.open() {
            error = err
            return
        }
        error = SQLiteDB.sharedInstance.executeChange(sqlStr)
        SQLiteDB.sharedInstance.close()
    }
    putOnThread(task)
    return error
    
}

/**
 Execute a non-query SQL statement (e.g. INSERT, UPDATE, DELETE, etc.) along with arguments to be bound to the characters "?" (for values) and "i?" (for identifiers e.g. table or column names).
 
 The objects in the provided array of arguments will be bound, in order, to the "i?" and "?" characters in the SQL string.
 The quantity of "i?"s and "?"s in the SQL string must be equal to the quantity of arguments provided.
 Objects that are to bind as an identifier ("i?") must be of type String.
 Identifiers should be bound and escaped if provided by the user.
 If "nil" is provided as an argument, the NULL value will be bound to the appropriate value in the SQL string.
 For more information on how the objects will be escaped, refer to the functions "escapeValue()" and "escapeIdentifier()".
 Note that the "escapeValue()" and "escapeIdentifier()" include the necessary quotations ' ' or " " to the arguments when being bound to the SQL.
 
 It is recommended to always verify that the return value is nil to ensure that the operation was successful.
 
 Possible errors returned by this function are:
 
 - SQLite errors (0 - 101)
 - binding errors (201 - 203)
 
 :param: sqlStr    The non-query string of SQL to be executed (INSERT, UPDATE, DELETE, etc.)
 :param: withArgs  An array of objects to bind to the "?" and "i?" characters in the sqlStr
 
 :returns:         An Int with the error code, or nil if there was no error
 */
public static func executeChange(_ sqlStr: String, withArgs: [AnyObject]) -> Int? {
    
    var error: Int? = nil
    let task: ()->Void = {
        if let err = SQLiteDB.sharedInstance.open() {
            error = err
            return
        }
        error = SQLiteDB.sharedInstance.executeChange(sqlStr, withArgs: withArgs)
        SQLiteDB.sharedInstance.close()
    }
    putOnThread(task)
    return error
    
}

/**
 Execute multiple SQL statements (non-queries e.g. INSERT, UPDATE, DELETE, etc.)
 
 This function will execute each SQL statment in the provided array, in order, and return an Int with the error code, or nil if there was no error.
 
 Possible errors returned by this function are:
 
 - SQLite errors (0 - 101)
 
 :param: sqlArr  An array of non-query strings of SQL to be executed (INSERT, UPDATE, DELETE, etc.)
 
 :returns:       An Int with the error code, or nil if there was no error
 */
public static func executeMultipleChanges(_ sqlArr: [String]) -> Int? {
    
    var error: Int? = nil
    let task: ()->Void = {
        if let err = SQLiteDB.sharedInstance.open() {
            error = err
            return
        }
        for sqlStr in sqlArr {
            if let err = SQLiteDB.sharedInstance.executeChange(sqlStr) {
                SQLiteDB.sharedInstance.close()
                if let index = sqlArr.index(of: sqlStr) {
                    print("Error occurred on array item: \(index) -> \"\(sqlStr)\"")
                }
                error = err
                return
            }
        }
        SQLiteDB.sharedInstance.close()
    }
    putOnThread(task)
    return error
    
}

/**
 Execute a SQLite query statement (e.g. SELECT)
 
 This function will execute the provided SQL and return a tuple of:
 - an Array of SDRow objects
 - an Int with the error code, or nil if there was no error
 
 The value for each column in an SDRow can be obtained using the column name in the subscript format similar to a Dictionary, along with the function to obtain the value in the appropriate type (.asString(), .asDate(), .asData(), .asInt(), .asDouble(), and .asBool()).
 Without the function call to return a specific type, the SDRow will return an object with type AnyObject.
 Note: NULL values in the SQLite database will be returned as 'nil'.
 
 Possible errors returned by this function are:
 
 - SQLite errors (0 - 101)
 
 :param: sqlStr  The query String of SQL to be executed (e.g. SELECT)
 
 :returns:       A tuple containing an Array of "SDRow"s, and an Int with the error code or nil if there was no error
 */
public static func executeQuery(_ sqlStr: String) -> (result: [SDRow], error: Int?) {
    
    var result = [SDRow] ()
    var error: Int? = nil
    let task: ()->Void = {
        if let err = SQLiteDB.sharedInstance.open() {
            error = err
            return
        }
        (result, error) = SQLiteDB.sharedInstance.executeQuery(sqlStr)
        SQLiteDB.sharedInstance.close()
    }
    putOnThread(task)
    return (result, error)
    
}

/**
 Execute a SQL query statement (e.g. SELECT) with arguments to be bound to the characters "?" (for values) and "i?" (for identifiers e.g. table or column names).
 
 See the "executeChange(sqlStr: String, withArgs: [AnyObject?])" function for more information on the arguments provided and binding.
 
 See the "executeQuery(sqlStr: String)"  function for more information on the return value.
 
 Possible errors returned by this function are:
 
 - SQLite errors (0 - 101)
 - binding errors (201 - 203)
 
 :param: sqlStr    The query String of SQL to be executed (e.g. SELECT)
 :param: withArgs  An array of objects that will be bound, in order, to the characters "?" (for values) and "i?" (for identifiers, e.g. table or column names) in the sqlStr.
 
 :returns:       A tuple containing an Array of "SDRow"s, and an Int with the error code or nil if there was no error
 */
public static func executeQuery(_ sqlStr: String, withArgs: [AnyObject]) -> (result: [SDRow], error: Int?) {
    
    var result = [SDRow] ()
    var error: Int? = nil
    let task: ()->Void = {
        if let err = SQLiteDB.sharedInstance.open() {
            error = err
            return
        }
        (result, error) = SQLiteDB.sharedInstance.executeQuery(sqlStr, withArgs: withArgs)
        SQLiteDB.sharedInstance.close()
    }
    putOnThread(task)
    return (result, error)
    
}

/**
 Execute functions in a closure on a single custom connection
 
 
 Note: This function cannot be nested within itself, or inside a transaction/savepoint.
 
 Possible errors returned by this function are:
 
 - custom connection errors (301 - 306)
 
 :param: flags    The custom flag associated with the connection. Can be either:
 - .ReadOnly
 - .ReadWrite
 - .ReadWriteCreate
 
 :param: closure  A closure containing functions that will be executed on the custom connection
 
 :returns:        An Int with the error code, or nil if there was no error
 */
public static func executeWithConnection(_ flags: SD.Flags, closure: @escaping ()->Void) -> Int? {
    
    var error: Int? = nil
    let task: ()->Void = {
        if let err = SQLiteDB.sharedInstance.openWithFlags(flags.toSQL()) {
            error = err
            return
        }
        closure()
        if let err = SQLiteDB.sharedInstance.closeCustomConnection() {
            error = err
            return
        }
    }
    putOnThread(task)
    return error
    
}


// MARK: - Escaping Objects

/**
 Escape an object to be inserted into a SQLite statement as a value
 
 NOTE: Supported object types are: String, Int, Double, Bool, NSData, NSDate, and nil. All other data types will return the String value "NULL", and a warning message will be printed.
 
 :param: obj  The value to be escaped
 
 :returns:    The escaped value as a String, ready to be inserted into a SQL statement. Note: Single quotes (') will be placed around the entire value, if necessary.
 */
public static func escapeValue(_ obj: AnyObject?) -> String {
    return SQLiteDB.sharedInstance.escapeValue(obj)
}

/**
 Escape a string to be inserted into a SQLite statement as an indentifier (e.g. table or column name)
 
 :param: obj  The identifier to be escaped. NOTE: This object must be of type String.
 
 :returns:    The escaped identifier as a String, ready to be inserted into a SQL statement. Note: Double quotes (") will be placed around the entire identifier.
 */
public static func escapeIdentifier(_ obj: String) -> String {
    return SQLiteDB.sharedInstance.escapeIdentifier(obj)
}


// MARK: - Tables

/**
 Create A Table With The Provided Column Names and Types
 
 Note: The ID field is created automatically as "INTEGER PRIMARY KEY AUTOINCREMENT"
 
 Possible errors returned by this function are:
 
 - SQLite errors (0 - 101)
 
 :param:  table                The table name to be created
 :param:  columnNamesAndTypes  A dictionary where the key = column name, and the value = data type
 
 :returns:                     An Int with the error code, or nil if there was no error
 */
public static func createTable(_ table: String, withColumnNamesAndTypes values: [String: SwiftData.DataType]) -> Int? {
    
    var error: Int? = nil
    let task: ()->Void = {
        if let err = SQLiteDB.sharedInstance.open() {
            error = err
            return
        }
        error = SQLiteDB.sharedInstance.createSQLTable(table, withColumnsAndTypes: values)
        SQLiteDB.sharedInstance.close()
    }
    putOnThread(task)
    return error
    
}

/**
 Delete a SQLite table by name
 
 Possible errors returned by this function are:
 
 - SQLite errors (0 - 101)
 
 :param:  table  The table name to be deleted
 
 :returns:       An Int with the error code, or nil if there was no error
 */
public static func deleteTable(_ table: String) -> Int? {
    
    var error: Int? = nil
    let task: ()->Void = {
        if let err = SQLiteDB.sharedInstance.open() {
            error = err
            return
        }
        error = SQLiteDB.sharedInstance.deleteSQLTable(table)
        SQLiteDB.sharedInstance.close()
    }
    putOnThread(task)
    return error
    
}

/**
 Obtain a list of the existing SQLite table names
 
 Possible errors returned by this function are:
 
 - SQLite errors (0 - 101)
 - Table query error (403)
 
 :returns:  A tuple containing an Array of all existing SQLite table names, and an Int with the error code or nil if there was no error
 */
public static func existingTables() -> (result: [String], error: Int?) {
    
    var result = [String] ()
    var error: Int? = nil
    let task: ()->Void = {
        if let err = SQLiteDB.sharedInstance.open() {
            error = err
            return
        }
        (result, error) = SQLiteDB.sharedInstance.existingTables()
        SQLiteDB.sharedInstance.close()
    }
    putOnThread(task)
    return (result, error)
    
}


// MARK: - Misc


/**
 Obtain the error message relating to the provided error code
 
 :param: code  The error code provided
 
 :returns:     The error message relating to the provided error code
 */
public static func errorMessageForCode(_ code: Int) -> String {
    return SwiftData.SDError.errorMessageFromCode(code)
}

/**
 Obtain the database path
 
 :returns:  The path to the SwiftData database
 */
public static func databasePath() -> String {
    return SQLiteDB.sharedInstance.dbPath
}

/**
 Obtain the last inserted row id
 
 Note: Care should be taken when the database is being accessed from multiple threads. The value could possibly return the last inserted row ID for another operation if another thread executes after your intended operation but before this function call.
 
 Possible errors returned by this function are:
 
 - SQLite errors (0 - 101)
 
 :returns:  A tuple of he ID of the last successfully inserted row's, and an Int of the error code or nil if there was no error
 */
public static func lastInsertedRowID() -> (rowID: Int, error: Int?) {
    
    var result = 0
    var error: Int? = nil
    let task: ()->Void = {
        if let err = SQLiteDB.sharedInstance.open() {
            error = err
            return
        }
        result = SQLiteDB.sharedInstance.lastInsertedRowID()
        SQLiteDB.sharedInstance.close()
    }
    putOnThread(task)
    return (result, error)
    
}

/**
 Obtain the number of rows modified by the most recently completed SQLite statement (INSERT, UPDATE, or DELETE)
 
 Note: Care should be taken when the database is being accessed from multiple threads. The value could possibly return the number of rows modified for another operation if another thread executes after your intended operation but before this function call.
 
 Possible errors returned by this function are:
 
 - SQLite errors (0 - 101)
 
 :returns:  A tuple of the number of rows modified by the most recently completed SQLite statement, and an Int with the error code or nil if there was no error
 */
public static func numberOfRowsModified() -> (rowID: Int, error: Int?) {
    
    var result = 0
    var error: Int? = nil
    let task: ()->Void = {
        if let err = SQLiteDB.sharedInstance.open() {
            error = err
            return
        }
        result = SQLiteDB.sharedInstance.numberOfRowsModified()
        SQLiteDB.sharedInstance.close()
    }
    putOnThread(task)
    return (result, error)
    
}


// MARK: - Indexes

/**
 Create a SQLite index on the specified table and column(s)
 
 Possible errors returned by this function are:
 
 - SQLite errors (0 - 101)
 - Index error (401)
 
 :param: name       The index name that is being created
 :param: onColumns  An array of column names that the index will be applied to (must be one column or greater)
 :param: inTable    The table name where the index is being created
 :param: isUnique   True if the index should be unique, false if it should not be unique (defaults to false)
 
 :returns:          An Int with the error code, or nil if there was no error
 */
public static func createIndex(name: String, onColumns: [String], inTable: String, isUnique: Bool = false) -> Int? {
    
    var error: Int? = nil
    let task: ()->Void = {
        if let err = SQLiteDB.sharedInstance.open() {
            error = err
            return
        }
        error = SQLiteDB.sharedInstance.createIndex(name, columns: onColumns, table: inTable, unique: isUnique)
        SQLiteDB.sharedInstance.close()
    }
    putOnThread(task)
    return error
    
}

/**
 Remove a SQLite index by its name
 
 Possible errors returned by this function are:
 
 - SQLite errors (0 - 101)
 
 :param: indexName  The name of the index to be removed
 
 :returns:          An Int with the error code, or nil if there was no error
 */
public static func removeIndex(_ indexName: String) -> Int? {
    
    var error: Int? = nil
    let task: ()->Void = {
        if let err = SQLiteDB.sharedInstance.open() {
            error = err
            return
        }
        error = SQLiteDB.sharedInstance.removeIndex(indexName)
        SQLiteDB.sharedInstance.close()
    }
    putOnThread(task)
    return error
    
}

/**
 Obtain a list of all existing indexes
 
 Possible errors returned by this function are:
 
 - SQLite errors (0 - 101)
 - Index error (402)
 
 :returns:  A tuple containing an Array of all existing index names on the SQLite database, and an Int with the error code or nil if there was no error
 */
public static func existingIndexes() -> (result: [String], error: Int?) {
    
    var result = [String] ()
    var error: Int? = nil
    let task: ()->Void = {
        if let err = SQLiteDB.sharedInstance.open() {
            error = err
            return
        }
        (result, error) = SQLiteDB.sharedInstance.existingIndexes()
        SQLiteDB.sharedInstance.close()
    }
    putOnThread(task)
    return (result, error)
    
}

/**
 Obtain a list of all existing indexes on a specific table
 
 Possible errors returned by this function are:
 
 - SQLite errors (0 - 101)
 - Index error (402)
 
 :param:  table  The name of the table that is being queried for indexes
 
 :returns:       A tuple containing an Array of all existing index names in the table, and an Int with the error code or nil if there was no error
 */
public static func existingIndexesForTable(_ table: String) -> (result: [String], error: Int?) {
    
    var result = [String] ()
    var error: Int? = nil
    let task: ()->Void = {
        if let err = SQLiteDB.sharedInstance.open() {
            error = err
            return
        }
        (result, error) = SQLiteDB.sharedInstance.existingIndexesForTable(table)
        SQLiteDB.sharedInstance.close()
    }
    putOnThread(task)
    return (result, error)
    
}


// MARK: - Transactions and Savepoints

/**
 Execute commands within a single exclusive transaction
 
 A connection to the database is opened and is not closed until the end of the transaction. A transaction cannot be embedded into another transaction or savepoint.
 
 Possible errors returned by this function are:
 
 - SQLite errors (0 - 101)
 - Transaction errors (501 - 502)
 
 :param: transactionClosure  A closure containing commands that will execute as part of a single transaction. If the transactionClosure returns true, the changes made within the closure will be committed. If false, the changes will be rolled back and will not be saved.
 
 :returns:                   An Int with the error code, or nil if there was no error committing or rolling back the transaction
 */
public static func transaction(_ transactionClosure: @escaping ()->Bool) -> Int? {
    
    var error: Int? = nil
    let task: ()->Void = {
        if let err = SQLiteDB.sharedInstance.open() {
            error = err
            return
        }
        if let err = SQLiteDB.sharedInstance.beginTransaction() {
            SQLiteDB.sharedInstance.close()
            error = err
            return
        }
        if transactionClosure() {
            if let err = SQLiteDB.sharedInstance.commitTransaction() {
                error = err
            }
        } else {
            if let err = SQLiteDB.sharedInstance.rollbackTransaction() {
                error = err
            }
        }
        SQLiteDB.sharedInstance.close()
    }
    putOnThread(task)
    return error
    
}

/**
 Execute commands within a single savepoint
 
 A connection to the database is opened and is not closed until the end of the savepoint (or the end of the last savepoint, if embedded).
 
 NOTE: Unlike transactions, savepoints may be embedded into other savepoints or transactions.
 
 Possible errors returned by this function are:
 
 - SQLite errors (0 - 101)
 
 :param: savepointClosure  A closure containing commands that will execute as part of a single savepoint. If the savepointClosure returns true, the changes made within the closure will be released. If false, the changes will be rolled back and will not be saved.
 
 :returns:                 An Int with the error code, or nil if there was no error releasing or rolling back the savepoint
 */
public static func savepoint(_ savepointClosure: @escaping ()->Bool) -> Int? {
    
    var error: Int? = nil
    let task: ()->Void = {
        if let err = SQLiteDB.sharedInstance.open() {
            error = err
            return
        }
        if let err = SQLiteDB.sharedInstance.beginSavepoint() {
            SQLiteDB.sharedInstance.close()
            error = err
            return
        }
        if savepointClosure() {
            if let err = SQLiteDB.sharedInstance.releaseSavepoint() {
                error = err
            }
        } else {
            if let err = SQLiteDB.sharedInstance.rollbackSavepoint() {
                print("Error rolling back to savepoint")
                SQLiteDB.sharedInstance.savepointsOpen -= 1
                SQLiteDB.sharedInstance.close()
                error = err
                return
            }
            if let err = SQLiteDB.sharedInstance.releaseSavepoint() {
                error = err
            }
        }
        SQLiteDB.sharedInstance.close()
    }
    putOnThread(task)
    return error
    
}

/**
 Convenience function to save a UIImage to disk and return the ID
 
 :param: image  The UIImage to be saved
 
 :returns:      The ID of the saved image as a String, or nil if there was an error saving the image to disk
 */
public static func saveUIImage(_ image: UIImage) -> String? {
    
    let docsPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0] as String
    let imageDirPath = docsPath.stringByAppendingPathComponent(path: "SwiftDataImages")
    if !FileManager.default.fileExists(atPath: imageDirPath) {
        
        do {
            try FileManager.default.createDirectory(atPath: imageDirPath, withIntermediateDirectories: false, attributes: nil)
        } catch let error as NSError {
            NSLog("Unable to create directory \(error.debugDescription)")
            return nil
        }
        
    }
    let imageID = UUID().uuidString
    let imagePath = imageDirPath.stringByAppendingPathComponent(path: imageID)
    let imageAsData = UIImagePNGRepresentation(image)
    if !((try? imageAsData!.write(to: URL(fileURLWithPath: imagePath), options: [.atomic])) != nil) {
        print("Error saving image")
        return nil
    }
    return imageID
    
}

/**
 Convenience function to delete a UIImage with the specified ID
 
 :param: id  The id of the UIImage
 
 :returns:   True if the image was successfully deleted, or false if there was an error during the deletion
 */
public static func deleteUIImageWithID(_ id: String) -> Bool {
    
    let docsPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0] as String
    let imageDirPath = docsPath.stringByAppendingPathComponent(path: "SwiftDataImages")
    let fullPath = imageDirPath.stringByAppendingPathComponent(path: id)
    do {
        try FileManager.default.removeItem(atPath: fullPath)
    } catch {
        // Error - handle if required
        return false
    }
    return false
    
    
}


// MARK: - SQLiteDB Class

fileprivate class SQLiteDB {
    
    class var sharedInstance: SQLiteDB {
        struct Singleton {
            static let instance = SQLiteDB()
        }
        return Singleton.instance
    }
    var sqliteDB: OpaquePointer? = nil
    var dbPath = SQLiteDB.createPath()
    var inTransaction = false
    var isConnected = false
    var openWithFlags = false
    var savepointsOpen = 0
    let queue = DispatchQueue(label: "SwiftData.DatabaseQueue", attributes: [])
    
    
    // MARK: - Database Handling Functions
    
    //open a connection to the sqlite3 database
    func open() -> Int? {
        
        if inTransaction || openWithFlags || savepointsOpen > 0 {
            return nil
        }
        if sqliteDB != nil || isConnected {
            return nil
        }
        
        let status = sqlite3_open(dbPath.cString(using: String.Encoding.utf8)!, &sqliteDB)
        if status != SQLITE_OK {
            print("SwiftData Error -> During: Opening Database")
            print("                -> Code: \(status) - " + SDError.errorMessageFromCode(Int(status)))
            if let errMsg = String(validatingUTF8: sqlite3_errmsg(SQLiteDB.sharedInstance.sqliteDB)) {
                print("                -> Details: \(errMsg)")
            }
            return Int(status)
        }

// let key = "helloworld".cString(using: String.Encoding.utf8)
// sqlite3_key(sqliteDB, key!, Int32((key?.count)!))
// if (sqlite3_exec(sqliteDB, "SELECT count(*) FROM sqlite_master".cString(using: String.Encoding.utf8), nil, nil, nil) == SQLITE_OK)
// {
// //print("Password is correct, or a new database has been initialized")
// }
// else{
// //print("Incorrect password")
// }
isConnected = true
return nil

    }
    
    //open a connection to the sqlite3 database with flags
    func openWithFlags(_ flags: Int32) -> Int? {
        
        if inTransaction {
            print("SwiftData Error -> During: Opening Database with Flags")
            print("                -> Code: 302 - Cannot open a custom connection inside a transaction")
            return 302
        }
        if openWithFlags {
            print("SwiftData Error -> During: Opening Database with Flags")
            print("                -> Code: 301 - A custom connection is already open")
            return 301
        }
        if savepointsOpen > 0 {
            print("SwiftData Error -> During: Opening Database with Flags")
            print("                -> Code: 303 - Cannot open a custom connection inside a savepoint")
            return 303
        }
        if isConnected {
            print("SwiftData Error -> During: Opening Database with Flags")
            print("                -> Code: 301 - A custom connection is already open")
            return 301
        }
        let status = sqlite3_open_v2(dbPath.cString(using: String.Encoding.utf8)!, &sqliteDB, flags, nil)
        if status != SQLITE_OK {
            print("SwiftData Error -> During: Opening Database with Flags")
            print("                -> Code: \(status) - " + SDError.errorMessageFromCode(Int(status)))
            if let errMsg = String(validatingUTF8: sqlite3_errmsg(SQLiteDB.sharedInstance.sqliteDB)) {
                print("                -> Details: \(errMsg)")
            }
            return Int(status)
        }
        isConnected = true
        openWithFlags = true
        return nil
        
    }
    
    //close the connection to to the sqlite3 database
    func close() {
        
        if inTransaction || openWithFlags || savepointsOpen > 0 {
            return
        }
        if sqliteDB == nil || !isConnected {
            return
        }
        let status = sqlite3_close(sqliteDB)
        if status != SQLITE_OK {
            print("SwiftData Error -> During: Closing Database")
            print("                -> Code: \(status) - " + SDError.errorMessageFromCode(Int(status)))
            if let errMsg = String(validatingUTF8: sqlite3_errmsg(SQLiteDB.sharedInstance.sqliteDB)) {
                print("                -> Details: \(errMsg)")
            }
        }
        sqliteDB = nil
        isConnected = false
        
    }
    
    //close a custom connection to the sqlite3 database
    func closeCustomConnection() -> Int? {
        
        if inTransaction {
            print("SwiftData Error -> During: Closing Database with Flags")
            print("                -> Code: 305 - Cannot close a custom connection inside a transaction")
            return 305
        }
        if savepointsOpen > 0 {
            print("SwiftData Error -> During: Closing Database with Flags")
            print("                -> Code: 306 - Cannot close a custom connection inside a savepoint")
            return 306
        }
        if !openWithFlags {
            print("SwiftData Error -> During: Closing Database with Flags")
            print("                -> Code: 304 - A custom connection is not currently open")
            return 304
        }
        let status = sqlite3_close(sqliteDB)
        sqliteDB = nil
        isConnected = false
        openWithFlags = false
        if status != SQLITE_OK {
            print("SwiftData Error -> During: Closing Database with Flags")
            print("                -> Code: \(status) - " + SDError.errorMessageFromCode(Int(status)))
            if let errMsg = String(validatingUTF8: sqlite3_errmsg(SQLiteDB.sharedInstance.sqliteDB)) {
                print("                -> Details: \(errMsg)")
            }
            return Int(status)
        }
        return nil
        
    }
    
    
    
    //create the database path
    class func createPath() -> String {
        
        //let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.SlavamShareExtension")
        //let bdpath = containerURL?.appendingPathComponent("salvam.db")
        let fileManager = FileManager.default
        let Sourcepath = Bundle.main.resourcePath?.stringByAppendingPathComponent(path: "salvam.db");
        let docsPath = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.SlavamShareExtension")
        let databaseStr = "salvam.db"
        let dbPath = docsPath?.appendingPathComponent(databaseStr)
        print(dbPath)
        //check for database existance if not exsit than copy the database destination path

// if(fileManager .fileExists(atPath: dbPath) == false) {
// do{
// try fileManager.copyItem(atPath: Sourcepath!, toPath: dbPath)
// }
// catch {
//
// }
// }
print(dbPath)
return "(dbPath)"

    }
    
    //begin a transaction
    func beginTransaction() -> Int? {
        
        if savepointsOpen > 0 {
            print("SwiftData Error -> During: Beginning Transaction")
            print("                -> Code: 501 - Cannot begin a transaction within a savepoint")
            return 501
        }
        if inTransaction {
            print("SwiftData Error -> During: Beginning Transaction")
            print("                -> Code: 502 - Cannot begin a transaction within another transaction")
            return 502
        }
        if let error = executeChange("BEGIN EXCLUSIVE") {
            return error
        }
        inTransaction = true
        return nil
        
    }
    
    //rollback a transaction
    func rollbackTransaction() -> Int? {
        
        let error = executeChange("ROLLBACK")
        inTransaction = false
        return error
        
    }
    
    //commit a transaction
    func commitTransaction() -> Int? {
        
        let error = executeChange("COMMIT")
        inTransaction = false
        if let err = error {
            rollbackTransaction()
            return err
        }
        return nil
        
    }
    
    //begin a savepoint
    func beginSavepoint() -> Int? {
        
        if let error = executeChange("SAVEPOINT 'savepoint\(savepointsOpen + 1)'") {
            return error
        }
        savepointsOpen += 1
        return nil
        
    }
    
    //rollback a savepoint
    func rollbackSavepoint() -> Int? {
        return executeChange("ROLLBACK TO 'savepoint\(savepointsOpen)'")
    }
    
    //release a savepoint
    func releaseSavepoint() -> Int? {
        
        let error = executeChange("RELEASE 'savepoint\(savepointsOpen)'")
        savepointsOpen -= 1
        return error
        
    }
    
    //get last inserted row id
    func lastInsertedRowID() -> Int {
        let id = sqlite3_last_insert_rowid(sqliteDB)
        return Int(id)
    }
    
    //number of rows changed by last update
    func numberOfRowsModified() -> Int {
        return Int(sqlite3_changes(sqliteDB))
    }
    
    //return value of column
    func getColumnValue(_ statement: OpaquePointer, index: Int32, type: String) -> AnyObject? {
        
        switch type {
        case "INT", "INTEGER", "TINYINT", "SMALLINT", "MEDIUMINT", "BIGINT", "UNSIGNED BIG INT", "INT2", "INT8":
            if sqlite3_column_type(statement, index) == SQLITE_NULL {
                return nil
            }
            return Int(sqlite3_column_int(statement, index)) as AnyObject?
        case "CHARACTER(20)", "VARCHAR(255)", "VARYING CHARACTER(255)", "NCHAR(55)", "NATIVE CHARACTER", "NVARCHAR(100)", "TEXT", "CLOB":
            let text = (sqlite3_column_text(statement, index))
            if  text != nil{
                 return String(cString: text!) as AnyObject?
            }
            return nil
        case "BLOB", "NONE":
            let blob = sqlite3_column_blob(statement, index)
            if blob != nil {
                let size = sqlite3_column_bytes(statement, index)
                return Data(bytes: (blob)!, count: Int(size)) as NSData?
            }
            return nil
        case "REAL", "DOUBLE", "DOUBLE PRECISION", "FLOAT", "NUMERIC", "DECIMAL(10,5)":
            if sqlite3_column_type(statement, index) == SQLITE_NULL {
                return nil
            }
            return Double(sqlite3_column_double(statement, index)) as AnyObject?
        case "BOOLEAN":
            if sqlite3_column_type(statement, index) == SQLITE_NULL {
                return nil
            }
            return sqlite3_column_int(statement, index) as AnyObject?
        case "DATE", "DATETIME":

// let dateFormatter = DateFormatter()
// dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
// let text = (sqlite3_column_text(statement, index))
// if text != nil
// {
// let mystring = String(cString: text!) as String?
// return dateFormatter.date(from: mystring!) as AnyObject?
// }

            print("SwiftData Warning -> The text date at column: \(index) could not be cast as a String, returning nil")
            return nil
        default:
            print("SwiftData Warning -> Column: \(index) is of an unrecognized type, returning nil")
            return nil
        }
        
    }
    
    
    // MARK: SQLite Execution Functions
    //execute a SQLite update from a SQL String
    func executeChange(_ sqlStr: String, withArgs: [AnyObject]? = nil) -> Int? {
        
        var sql = sqlStr
        if let args = withArgs {
            let result = bind(args, toSQL: sql)
            if let error = result.error {
                return error
            } else {
                sql = result.string
            }
        }
        var pStmt: OpaquePointer? = nil
        var status = sqlite3_prepare_v2(SQLiteDB.sharedInstance.sqliteDB, sql, -1, &pStmt, nil)
        if status != SQLITE_OK {
            print("SwiftData Error -> During: SQL Prepare")
            print("                -> Code: \(status) - " + SDError.errorMessageFromCode(Int(status)))
            if let errMsg = String(validatingUTF8: sqlite3_errmsg(SQLiteDB.sharedInstance.sqliteDB)) {
                print("                -> Details: \(errMsg)")
            }
            sqlite3_finalize(pStmt)
            return Int(status)
        }
        status = sqlite3_step(pStmt)
        if status != SQLITE_DONE && status != SQLITE_OK {
            print("SwiftData Error -> During: SQL Step")
            print("                -> Code: \(status) - " + SDError.errorMessageFromCode(Int(status)))
            if let errMsg = String(validatingUTF8: sqlite3_errmsg(SQLiteDB.sharedInstance.sqliteDB)) {
                print("                -> Details: \(errMsg)")
            }
            sqlite3_finalize(pStmt)
            return Int(status)
        }
        sqlite3_finalize(pStmt)
        return nil
        
    }
    
    //execute a SQLite query from a SQL String
    func executeQuery(_ sqlStr: String, withArgs: [AnyObject]? = nil) -> (result: [SDRow], error: Int?) {
        
        var resultSet = [SDRow]()
        var sql = sqlStr
        if let args = withArgs {
            let result = bind(args, toSQL: sql)
            if let err = result.error {
                return (resultSet, err)
            } else {
                sql = result.string
            }
        }
        var pStmt: OpaquePointer? = nil
        var status = sqlite3_prepare_v2(SQLiteDB.sharedInstance.sqliteDB, sql, -1, &pStmt, nil)
        if status != SQLITE_OK {
            print("SwiftData Error -> During: SQL Prepare")
            print("                -> Code: \(status) - " + SDError.errorMessageFromCode(Int(status)))
            if let errMsg = String(validatingUTF8: sqlite3_errmsg(SQLiteDB.sharedInstance.sqliteDB)) {
                print("                -> Details: \(errMsg)")
            }
            sqlite3_finalize(pStmt)
            return (resultSet, Int(status))
        }
        var columnCount: Int32 = 0
        var next = true
        while next {
            status = sqlite3_step(pStmt)
            if status == SQLITE_ROW {
                columnCount = sqlite3_column_count(pStmt)
                var row = SDRow()
                for i: Int32 in 0 ..< columnCount {
                    let columnName = String(cString: sqlite3_column_name(pStmt, i))
                    
                
                    if  sqlite3_column_decltype(pStmt, i) != nil
                    {
                        if let columnType = String(validatingUTF8: sqlite3_column_decltype(pStmt, i))?.uppercased()
                        {
                            if let columnValue: AnyObject = getColumnValue(pStmt!, index: i, type: columnType) {
                                row[columnName] = SDColumn(obj: columnValue)
                            }
                        }
                    }
                    
                    else {
                        var columnType = ""
                        switch sqlite3_column_type(pStmt, i) {
                        case SQLITE_INTEGER:
                            columnType = "INTEGER"
                        case SQLITE_FLOAT:
                            columnType = "FLOAT"
                        case SQLITE_TEXT:
                            columnType = "TEXT"
                        case SQLITE3_TEXT:
                            columnType = "TEXT"
                        case SQLITE_BLOB:
                            columnType = "BLOB"
                        case SQLITE_NULL:
                            columnType = "NULL"
                        default:
                            columnType = "NULL"
                        }
                        if let columnValue: AnyObject = getColumnValue(pStmt!, index: i, type: columnType) {
                            row[columnName] = SDColumn(obj: columnValue)
                        }
                    }
                }
                resultSet.append(row)
            } else if status == SQLITE_DONE {
                next = false
            } else {
                print("SwiftData Error -> During: SQL Step")
                print("                -> Code: \(status) - " + SDError.errorMessageFromCode(Int(status)))
                if let errMsg = String(validatingUTF8: sqlite3_errmsg(SQLiteDB.sharedInstance.sqliteDB)) {
                    print("                -> Details: \(errMsg)")
                }
                sqlite3_finalize(pStmt)
                return (resultSet, Int(status))
            }
        }
        sqlite3_finalize(pStmt)
        return (resultSet, nil)
        
    }
    
}


// MARK: - SDRow

public struct SDRow {
    
    var values = [String: SDColumn]()
    public subscript(key: String) -> SDColumn? {
        get {
            return values[key]
        }
        set(newValue) {
            values[key] = newValue
        }
    }
    
}


// MARK: - SDColumn

public struct SDColumn {
    
    var value: AnyObject
    init(obj: AnyObject) {
        value = obj
    }
    
    //return value by type
    
    /**
     Return the column value as a String
     
     :returns:  An Optional String corresponding to the apprioriate column value. Will be nil if: the column name does not exist, the value cannot be cast as a String, or the value is NULL
     */
    public func asString() -> String? {
        return value as? String
    }
    
    /**
     Return the column value as an Int
     
     :returns:  An Optional Int corresponding to the apprioriate column value. Will be nil if: the column name does not exist, the value cannot be cast as a Int, or the value is NULL
     */
    public func asInt() -> Int? {
        return value as? Int
    }
    
    /**
     Return the column value as a Double
     
     :returns:  An Optional Double corresponding to the apprioriate column value. Will be nil if: the column name does not exist, the value cannot be cast as a Double, or the value is NULL
     */
    public func asDouble() -> Double? {
        return value as? Double
    }
    
    /**
     Return the column value as a Bool
     
     :returns:  An Optional Bool corresponding to the apprioriate column value. Will be nil if: the column name does not exist, the value cannot be cast as a Bool, or the value is NULL
     */
    public func asBool() -> Bool? {
        return value as? Bool
    }
    
    /**
     Return the column value as NSData
     
     :returns:  An Optional NSData object corresponding to the apprioriate column value. Will be nil if: the column name does not exist, the value cannot be cast as NSData, or the value is NULL
     */
    public func asData() -> Data? {
        return value as? Data
    }
    
    /**
     Return the column value as an NSDate
     
     :returns:  An Optional NSDate corresponding to the apprioriate column value. Will be nil if: the column name does not exist, the value cannot be cast as an NSDate, or the value is NULL
     */
    public func asDate() -> Date? {
        return value as? Date
    }
    
    /**
     Return the column value as an Float
     
     :returns:  An Optional Float corresponding to the apprioriate column value. Will be nil if: the column name does not exist, the value cannot be cast as an NSDate, or the value is NULL
     */
    public func asFloat() -> Float? {
        return value as? Float
    }
    /**
     Return the column value as an AnyObject
     
     :returns:  An Optional AnyObject corresponding to the apprioriate column value. Will be nil if: the column name does not exist, the value cannot be cast as an AnyObject, or the value is NULL
     */
    public func asAnyObject() -> AnyObject? {
        return value
    }
    
    /**
     Return the column value path as a UIImage
     
     :returns:  An Optional UIImage corresponding to the path of the apprioriate column value. Will be nil if: the column name does not exist, the value of the specified path cannot be cast as a UIImage, or the value is NULL
     */
    public func asUIImage() -> UIImage? {
        
        if let path = value as? String{
            let docsPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0] as String
            let imageDirPath = docsPath.stringByAppendingPathComponent(path: "SwiftDataImages")
            let fullPath = imageDirPath.stringByAppendingPathComponent(path: path)
            if !FileManager.default.fileExists(atPath: fullPath) {
                print("SwiftData Error -> Invalid image ID provided")
                return nil
            }
            if let imageAsData = try? Data(contentsOf: URL(fileURLWithPath: fullPath)) {
                return UIImage(data: imageAsData)
            }
        }
        return nil
        
    }
    
}


// MARK: - Error Handling

fileprivate struct SDError {
    
}

}

// MARK: - Threading

extension SwiftData {

fileprivate static func putOnThread(_ task: ()->Void) {
    if SQLiteDB.sharedInstance.inTransaction || SQLiteDB.sharedInstance.savepointsOpen > 0 || SQLiteDB.sharedInstance.openWithFlags {
        task()
    } else {
        SQLiteDB.sharedInstance.queue.sync {
            task()
        }
    }
}

}

// MARK: - Escaping And Binding Functions

extension SwiftData.SQLiteDB {

func bind(_ objects: [AnyObject], toSQL sql: String) -> (string: String, error: Int?) {
    
    var newSql = ""
    var bindIndex = 0
    var i = false
    for char in sql.characters {
        if char == "?" {
            if bindIndex > objects.count - 1 {
                print("SwiftData Error -> During: Object Binding")
                print("                -> Code: 201 - Not enough objects to bind provided")
                return ("", 201)
            }
            var obj = ""
            if i {
                if let str = objects[bindIndex] as? String {
                    obj = escapeIdentifier(str)
                } else {
                    print("SwiftData Error -> During: Object Binding")
                    print("                -> Code: 203 - Object to bind as identifier must be a String at array location: \(bindIndex)")
                    return ("", 203)
                }
                newSql = newSql.substring(to: newSql.characters.index(before: newSql.endIndex))
            } else {
                obj = escapeValue(objects[bindIndex])
            }
            newSql += obj
            bindIndex += 1
        } else {
            newSql.append(char)
        }
        if char == "i" {
            i = true
        } else if i {
            i = false
        }
    }
    if bindIndex != objects.count {
        print("SwiftData Error -> During: Object Binding")
        print("                -> Code: 202 - Too many objects to bind provided")
        return ("", 202)
    }
    return (newSql, nil)
    
}

//return escaped String value of AnyObject
func escapeValue(_ obj: AnyObject?) -> String {
    
    if let obj: AnyObject = obj {
        if obj is String {
            return "'\(escapeStringValue(obj as! String))'"
        }
        if obj is Double || obj is Int {
            return "\(obj)"
        }
        if obj is Bool {
            if obj as! Bool {
                return "1"
            } else {
                return "0"
            }
        }
        if obj is Data {
            let str = "\(obj)"
            var newStr = ""
            for char in str.characters {
                if char != "<" && char != ">" && char != " " {
                    newStr.append(char)
                }
            }
            return "X'\(newStr)'"
        }
        if obj is Date {
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
            return "\(escapeValue(dateFormatter.string(from: obj as! Date) as AnyObject?))"
        }
        if obj is UIImage {
            if let imageID = SD.saveUIImage(obj as! UIImage) {
                return "'\(escapeStringValue(imageID))'"
            }
            print("SwiftData Warning -> Cannot save image, NULL will be inserted into the database")
            return "NULL"
        }
        print("SwiftData Warning -> Object \"\(obj)\" is not a supported type and will be inserted into the database as NULL")
        return "NULL"
    } else {
        return "NULL"
    }
    
}

//return escaped String identifier
func escapeIdentifier(_ obj: String) -> String {
    return "\"\(escapeStringIdentifier(obj))\""
}


//escape string
func escapeStringValue(_ str: String) -> String {
    var escapedStr = ""
    for char in str.characters {
        if char == "'" {
            escapedStr += "'"
        }
        escapedStr.append(char)
    }
    return escapedStr
}

//escape string
func escapeStringIdentifier(_ str: String) -> String {
    var escapedStr = ""
    for char in str.characters {
        if char == "\"" {
            escapedStr += "\""
        }
        escapedStr.append(char)
    }
    return escapedStr
}

}

// MARK: - SQL Creation Functions

extension SwiftData {

/**
 Column Data Types
 
 :param:  StringVal   A column with type String, corresponds to SQLite type "TEXT"
 :param:  IntVal      A column with type Int, corresponds to SQLite type "INTEGER"
 :param:  DoubleVal   A column with type Double, corresponds to SQLite type "DOUBLE"
 :param:  BoolVal     A column with type Bool, corresponds to SQLite type "BOOLEAN"
 :param:  DataVal     A column with type NSdata, corresponds to SQLite type "BLOB"
 :param:  DateVal     A column with type NSDate, corresponds to SQLite type "DATE"
 :param:  UIImageVal  A column with type String (the path value of saved UIImage), corresponds to SQLite type "TEXT"
 */
public enum DataType {
    
    case stringVal
    case intVal
    case doubleVal
    case boolVal
    case dataVal
    case dateVal
    case uiImageVal
    
    fileprivate func toSQL() -> String {
        
        switch self {
        case .stringVal, .uiImageVal:
            return "TEXT"
        case .intVal:
            return "INTEGER"
        case .doubleVal:
            return "DOUBLE"
        case .boolVal:
            return "BOOLEAN"
        case .dataVal:
            return "BLOB"
        case .dateVal:
            return "DATE"
        }
    }
    
}

/**
 Flags for custom connection to the SQLite database
 
 :param:  ReadOnly         Opens the SQLite database with the flag "SQLITE_OPEN_READONLY"
 :param:  ReadWrite        Opens the SQLite database with the flag "SQLITE_OPEN_READWRITE"
 :param:  ReadWriteCreate  Opens the SQLite database with the flag "SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE"
 */
public enum Flags {
    
    case readOnly
    case readWrite
    case readWriteCreate
    
    fileprivate func toSQL() -> Int32 {
        
        switch self {
        case .readOnly:
            return SQLITE_OPEN_READONLY
        case .readWrite:
            return SQLITE_OPEN_READWRITE
        case .readWriteCreate:
            return SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE
        }
        
    }
    
}

}

extension SwiftData.SQLiteDB {

//create a table
func createSQLTable(_ table: String, withColumnsAndTypes values: [String: SwiftData.DataType]) -> Int? {
    
    var sqlStr = "CREATE TABLE \(table) (ID INTEGER PRIMARY KEY AUTOINCREMENT, "
    var firstRun = true
    for value in values {
        if firstRun {
            sqlStr += "\(escapeIdentifier(value.0)) \(value.1.toSQL())"
            firstRun = false
        } else {
            sqlStr += ", \(escapeIdentifier(value.0)) \(value.1.toSQL())"
        }
    }
    sqlStr += ")"
    return executeChange(sqlStr)
    
}

//delete a table
func deleteSQLTable(_ table: String) -> Int? {
    let sqlStr = "DROP TABLE \(table)"
    return executeChange(sqlStr)
}

//get existing table names
func existingTables() -> (result: [String], error: Int?) {
    let sqlStr = "SELECT name FROM sqlite_master WHERE type = 'table'"
    var tableArr = [String]()
    let results = executeQuery(sqlStr)
    if let err = results.error {
        return (tableArr, err)
    }
    for row in results.result {
        if let table = row["name"]?.asString() {
            tableArr.append(table)
        } else {
            print("SwiftData Error -> During: Finding Existing Tables")
            print("                -> Code: 403 - Error extracting table names from sqlite_master")
            return (tableArr, 403)
        }
    }
    return (tableArr, nil)
}

//create an index
func createIndex(_ name: String, columns: [String], table: String, unique: Bool) -> Int? {
    
    if columns.count < 1 {
        print("SwiftData Error -> During: Creating Index")
        print("                -> Code: 401 - At least one column name must be provided")
        return 401
    }
    var sqlStr = ""
    if unique {
        sqlStr = "CREATE UNIQUE INDEX \(name) ON \(table) ("
    } else {
        sqlStr = "CREATE INDEX \(name) ON \(table) ("
    }
    var firstRun = true
    for column in columns {
        if firstRun {
            sqlStr += column
            firstRun = false
        } else {
            sqlStr += ", \(column)"
        }
    }
    sqlStr += ")"
    return executeChange(sqlStr)
    
}

//remove an index
func removeIndex(_ name: String) -> Int? {
    let sqlStr = "DROP INDEX \(name)"
    return executeChange(sqlStr)
}

//obtain list of existing indexes
func existingIndexes() -> (result: [String], error: Int?) {
    
    let sqlStr = "SELECT name FROM sqlite_master WHERE type = 'index'"
    var indexArr = [String]()
    let results = executeQuery(sqlStr)
    if let err = results.error {
        return (indexArr, err)
    }
    for res in results.result {
        if let index = res["name"]?.asString() {
            indexArr.append(index)
        } else {
            print("SwiftData Error -> During: Finding Existing Indexes")
            print("                -> Code: 402 - Error extracting index names from sqlite_master")
            print("Error finding existing indexes -> Error extracting index names from sqlite_master")
            return (indexArr, 402)
        }
    }
    return (indexArr, nil)
    
}

//obtain list of existing indexes for a specific table
func existingIndexesForTable(_ table: String) -> (result: [String], error: Int?) {
    
    let sqlStr = "SELECT name FROM sqlite_master WHERE type = 'index' AND tbl_name = '\(table)'"
    var indexArr = [String]()
    let results = executeQuery(sqlStr)
    if let err = results.error {
        return (indexArr, err)
    }
    for res in results.result {
        if let index = res["name"]?.asString() {
            indexArr.append(index)
        } else {
            print("SwiftData Error -> During: Finding Existing Indexes for a Table")
            print("                -> Code: 402 - Error extracting index names from sqlite_master")
            return (indexArr, 402)
        }
    }
    return (indexArr, nil)
    
}

}

// MARK: - SDError Functions

extension SwiftData.SDError {

//get the error message from the error code
fileprivate static func errorMessageFromCode(_ errorCode: Int) -> String {
    
    switch errorCode {
        
        //no error
        
    case -1:
        return "No error"
        
    //SQLite error codes and descriptions as per: http://www.sqlite.org/c3ref/c_abort.html
    case 0:
        return "Successful result"
    case 1:
        return "SQL error or missing database"
    case 2:
        return "Internal logic error in SQLite"
    case 3:
        return "Access permission denied"
    case 4:
        return "Callback routine requested an abort"
    case 5:
        return "The database file is locked"
    case 6:
        return "A table in the database is locked"
    case 7:
        return "A malloc() failed"
    case 8:
        return "Attempt to write a readonly database"
    case 9:
        return "Operation terminated by sqlite3_interrupt()"
    case 10:
        return "Some kind of disk I/O error occurred"
    case 11:
        return "The database disk image is malformed"
    case 12:
        return "Unknown opcode in sqlite3_file_control()"
    case 13:
        return "Insertion failed because database is full"
    case 14:
        return "Unable to open the database file"
    case 15:
        return "Database lock protocol error"
    case 16:
        return "Database is empty"
    case 17:
        return "The database schema changed"
    case 18:
        return "String or BLOB exceeds size limit"
    case 19:
        return "Abort due to constraint violation"
    case 20:
        return "Data type mismatch"
    case 21:
        return "Library used incorrectly"
    case 22:
        return "Uses OS features not supported on host"
    case 23:
        return "Authorization denied"
    case 24:
        return "Auxiliary database format error"
    case 25:
        return "2nd parameter to sqlite3_bind out of range"
    case 26:
        return "File opened that is not a database file"
    case 27:
        return "Notifications from sqlite3_log()"
    case 28:
        return "Warnings from sqlite3_log()"
    case 100:
        return "sqlite3_step() has another row ready"
    case 101:
        return "sqlite3_step() has finished executing"
        
        //custom SwiftData errors
        
        //->binding errors
        
    case 201:
        return "Not enough objects to bind provided"
    case 202:
        return "Too many objects to bind provided"
    case 203:
        return "Object to bind as identifier must be a String"
        
        //->custom connection errors
        
    case 301:
        return "A custom connection is already open"
    case 302:
        return "Cannot open a custom connection inside a transaction"
    case 303:
        return "Cannot open a custom connection inside a savepoint"
    case 304:
        return "A custom connection is not currently open"
    case 305:
        return "Cannot close a custom connection inside a transaction"
    case 306:
        return "Cannot close a custom connection inside a savepoint"
        
        //->index and table errors
        
    case 401:
        return "At least one column name must be provided"
    case 402:
        return "Error extracting index names from sqlite_master"
    case 403:
        return "Error extracting table names from sqlite_master"
        
        //->transaction and savepoint errors
        
    case 501:
        return "Cannot begin a transaction within a savepoint"
    case 502:
        return "Cannot begin a transaction within another transaction"
        
        //unknown error
        
    default:
        //what the fuck happened?!?
        return "Unknown error"
    }
    
}

}

public typealias SD = SwiftData

Crash because sqlite3_column_decltype return nil

When trying to run query "Select MAX(rowid) From mytable", I have crash at line 1429

    columnType = String.fromCString(sqlite3_column_decltype(pStmt, i))!.uppercaseString

Somehow sqlite3_column_decltype returns 0x00000000 in this case. I tried sqlite3_column_type and it returns 1 (for INTERGER) correctly. Maybe we have to check the type here by using both sqlite3_column_decltype, sqlite3_column_type .

I tried on Simulator iOS 8.0 using latest code on master

Consider removing implicitly created ID column when creating new table

I checked the code and I did not find the place where this column is used internally (I might have missed something though). Is there any reason why you are creating it? I find that I don't use it in about half of my tables. While the memory and CPU savings wouldn't be very big if it wasn't used where not needed, but still, I would prefer cleaner tables :)

How to use existing database

I cannot get SwiftData to use an already existing database that contains data. I have renamed it to SwiftData.sqlite and placed it in the Supporting Files folder in my project (I have tried a few other locations as well). When I try to query the Players table:

let (resultSet, err) = SD.executeQuery("SELECT * FROM PLAYERS")

I get the error:

SwiftData Error -> During: SQL Prepare
-> Code: 1 - SQL error or missing database
-> Details: no such table: PLAYERS

Fit for swift2.0

Please...I really like it..........I cant remove it from my project at all

One column returns

I'm executing the next code according the documentation, but is just returning one column, other columns are nil.

func getCities() -> NSMutableArray{
        var returnArray = NSMutableArray()
        let (resultSet, err) = SD.executeQuery("SELECT * FROM City order by _order;")
        if err != nil {
            //there was an error during the query, handle it here
        } else {
            for row in resultSet {
                print(row)
                print(row.values)
                if let nam_city = row["id_city"]?.asInt() {
                    println("The City name is: \(nam_city)")

                }

                if let offline = row["nam_city"]?.asString() {
                    println("The City name is: \(offline)")
                }
            }
        }
        return returnArray
    }

Dates

I notice that you're writing dates using NSDateFormatter. I'd really encourage you to specify a timezone for your formatter in case the timezone of the device changes at any point. As you've implemented the date logic, dates are stored in the device's then local time zone which can be problematic.

I'd suggest

  • using a date formatter that specifies time zone (e.g. store date strings as Zulu/GMT/UTC, NSTimeZone(forSecondsFromGMT: 0) and include the timezone in the date string, e.g. Z; and
  • locale (e.g. the one used for RFC 3339/ISO8601 dates is NSLocale(localeIdentifier: "en_US_POSIX"); see Q&A 1480).

You might even want to follow one of the established standards, such as ISO8601 format, e.g. 2015-02-03T15:29:00.000Z, in which the format is sortable and unambiguous.

By the way, while this is a somewhat minor issue (though, IMHO, still important) when dealing with data that the end user writes to the database themselves, it's a huge deal when dealing with a database that the app developer includes in a database that they distribute with the app (in which the timezone and locale is that of the app developer, which is, more than likely, completely different from the users').

Slowness

I wonder why it takes 11 seconds to insert 250 rows with ~12 columns, or ~7 seconds a 80Kb File to String in one row.

Is there a better way to Insert/update?

More descriptive error messages

Lack of descriptive error message makes debugging faulty SQL statements painful. For example, running an insert statement with invalid column simply returns:

Error During Prepare -> SQLITE Code: 1 - SQL error or missing database

Running the same SQL in SQLite Manager returns a much more useful error message:

table bookings has no column named estimated_shopping_rice

I tried fiddling around this area but has been unsuccessful due to my lack of knowledge around SQLite internals. Any help would be welcome :)

please help on docpaths

I am getting this error:"'stringByAppendingPathComponent' is unavailable: Use URLByAppendingPathComponent on NSURL instead." in swift data for the image directories mainly here:
public static func saveUIImage(image: UIImage) -> String? {

    let docsPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0]
    let imageDirPath = NSURL(docsPath.URLByAppendingPathComponent("SwiftDataImages"))
    if !NSFileManager.defaultManager().fileExistsAtPath(imageDirPath) {
        do {
            try NSFileManager.defaultManager().createDirectoryAtPath(imageDirPath, withIntermediateDirectories: false, attributes: nil)
        } catch _ {
            print("Error creating SwiftData image folder")
            return nil
        }
    }
    let imageID = NSUUID().UUIDString
    let imagePath = imageDirPath.stringByAppendingPathComponent(imageID)
    let imageAsData = UIImagePNGRepresentation(image)
    if !imageAsData!.writeToFile(imagePath, atomically: true) {
        print("Error saving image")
        return nil
    }
    return imageID

}

/**
 Convenience function to delete a UIImage with the specified ID

 :param: id  The id of the UIImage

 :returns:   True if the image was successfully deleted, or false if there was an error during the deletion
 */
public static func deleteUIImageWithID(id: String) -> Bool {

    let docsPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0]
    let imageDirPath = docsPath.stringByAppendingPathComponent("SwiftDataImages")
    let fullPath = imageDirPath.stringByAppendingPathComponent(id)
    do {
        try NSFileManager.defaultManager().removeItemAtPath(fullPath)
        return true
    } catch _ as NSError {
        return false
    }


}

-------------------------------------------and here
class func createPath() -> String {

        let docsPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0]
        let databaseStr = "SwiftData.sqlite"
        let dbPath = docsPath.stringByAppendingPathComponent(databaseStr)
        return dbPath

-------------------------------------------and here
public func asUIImage() -> UIImage? {

        if let path = value as? String{
            let docsPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0]
            let imageDirPath = docsPath.stringByAppendingPathComponent("SwiftDataImages")
            let fullPath = imageDirPath.stringByAppendingPathComponent(path)
            if !NSFileManager.defaultManager().fileExistsAtPath(fullPath) {
                print("SwiftData Error -> Invalid image ID provided")
                return nil
            }
            if let imageAsData = NSData(contentsOfFile: fullPath) {
                return UIImage(data: imageAsData)
            }
        }
        return nil

    }

}

custom database

Hi,

how can I open my database file instead of "SwiftData.sqlite"?

Swift Data not working in XCode 11, iOS 13

Not able to fetch data in XCode 11. I am currently working on Swift 5. It's creating a table properly but not able to save data into the database.
It's working properly on XCode 10.3
But not working on XCode 11.

*TableView Recommendation

As a newcomer to IOS and Swift it would be great if you could recommend a tableview type class that would allow an easy 'window' on data rows. This was mentioned in Issue 15.

Indexing hags on insert statement.

Hello sir,

Below i have share your Insert statement. This query i am using in my project for offline but there is a problem what i face right now. The problem is that the indexing is hangs on this line because this query is in only one line but written in multiple lines.

Please let me know that how to resolve this problem as soon as possible.

let str = SD.executeChangeToStr(sqlStr: "INSERT INTO MeetingTable (actual_end_time, actual_start_time,agenda_html,availability,date,day,end_time,meeting_id,is_cancel,meeting_name,meeting_type,parent_id,reoccur,responsible_user_id,responsible_user_name,start_time,totalsnoozeTimeMeeting,user_id,wbs_category_id,is_old) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ", withArgs: [actual_end_time as AnyObject,actual_start_time as AnyObject, agenda_html as AnyObject, availability as AnyObject, date as AnyObject, day as AnyObject, end_time as AnyObject, id as AnyObject, is_cancel as AnyObject, meeting_name as AnyObject, meeting_type as AnyObject, parent_id as AnyObject, reoccur as AnyObject, responsible_user_id as AnyObject, responsible_user_name as AnyObject, start_time as AnyObject, totalsnoozeTimeMeeting as AnyObject, user_id as AnyObject, wbs_category_id as AnyObject, is_old as AnyObject])

Regards
Nitesh

Update for Swift 2.1?

The new release of Swift generates about 120 errors. Some of them are just println replacements but the others are more structural.

Any chance of an update?

Thanks.

SWift 3

many errors on converting my launguage to swift 3
kindly let me do the changes and let us know

/SwiftData.swift:1270:21: 'SQLiteDB' is inaccessible due to 'private' protection level

error description on funcition

extension SwiftData.SQLiteDB {
func bind(objects: [AnyObject], toSQL sql: String) -> (string: String, error: Int?) {

when app enter background then return to foreground in 10 minutes, error

If my app enter background and a lot of apps run in background , it has a high level of risk to get a sqlite 3 error code 21 when app return foreground. I could not always re-capture the error, however, I think the reason is SwiftData is a singleton which has been delloc by OS when app receive memory warning. Is it possible ?
Please give me some tips to avoid it ?
what should I implement in app delegate methods, such as appDidEnterbackground .

Using sqlite with many iOS devices

is it possible to use sqlite db on more than one device. such as iPad and iPhone? I am not familiar with iCloud and core data.. I would like to use swift data cause i understand the syntax.

Thanks
Dave

Multiple threads

All SwiftData operations are placed on a custom serial queue ("SwiftData.DatabaseQueue") and executed in a FIFO order. Thats great, but is there a way I could set my own queue? Imagine following scenario: I am saving a lot of data in background thread, but I don't want to freeze main UI thread in which the data are fetching/selecting. It is happening because all SQL queries are executed in same serial queue. It would be great if I can write into database in one queue and read from database in another.

Check if table exists

Hi, i have a question, it may be a stupid question but i can't seem to figure out how to do it.
I want to check if a certain table exists (in this case the table "users") if it does exist i want to insert a user, if not then create the database and then insert user. (the creation of the database works only it keeps trying to create the same database time after time with each build of the app)

To give some dimension to my question here is the code i have so far.

let (tables, err) = SD.existingTables()
// HERE I WANT TO CHECK IF THE TABLE EXISTS
if tables == "users" {
if let err = SD.executeChange("INSERT INTO users (username, password) VALUES ('awesomeusername','awesomepassword')") {
//there was an error during the insert, handle it here
} else {
//no error, the row was inserted successfully
}
}else{
if let err = SD.executeChange("CREATE TABLE users (ID INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, password TEXT)") {
println("The creation of the database went wrong")
}else {
println("The database is created")
}
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.