A Swift wrapper for SQLite databases

Overview

Squeal, a Swift interface to SQLite

Squeal provides access to SQLite databases in Swift. Its goal is to provide a simple and straight-forward base API, allowing developers to build on top in ways that make sense for their apps. The API provides direct SQL access, as well as a complete set of helpers to reduce SQL drudgery. It's not a goal of this project to hide SQL from the developer, or to provide a generic object-mapping on top of SQLite.

Features

  • Small, straightforward Swift interface for accessing SQLite databases via SQL.
  • Helper methods for most common types of SQL statements.
  • Easy database schema versioning and migration DSL.
  • Simple DatabasePool implementation for concurrent access to a database.

Basic Usage

import Squeal

let db = Database()

// Create:
try db.createTable("contacts", definitions: [
    "id INTEGER PRIMARY KEY",
    "name TEXT",
    "email TEXT NOT NULL"
])

// Insert:
let contactId = try db.insertInto(
    "contacts",
    values: [
        "name": "Amelia Grey",
        "email": "[email protected]"
    ]
)

// Select:
struct Contact {
    let id:Int
    let name:String?
    let email:String
    
    init(row:Statement) throws {
        id = row.intValue("id") ?? 0
        name = row.stringValue("name")
        email = row.stringValue("email") ?? ""
    }
}

let contacts:[Contact] = try db.selectFrom(
    "contacts",
    whereExpr:"name IS NOT NULL",
    block: Contact.init
)

// Count:
let numberOfContacts = try db.countFrom("contacts")

The above example can be found in Squeal.playground to allow further exploration of Squeal's interface.

Migrations

Any non-trivial app will need to change its database schema as features are added or updated. Unfortunately, SQLite provides only minimal support for updating a database's schema. Things like removing a column or removing a NON NULL require the entire database to be re-created with the new schema.

Squeal makes migrations easy by including a Schema class with a simple DSL for declaring your database migrations. Once defined, the Schema can be used to migrate your database to the latest version.

Here's an example:

import Squeal

// Define a Schema:
let AppSchema = Schema(identifier:"contacts") { schema in
    // Version 1:
    schema.version(1) { v1 in
        // Create a Table:
        v1.createTable("contacts") { contacts in
            contacts.primaryKey("id")
            contacts.column("name", type:.Text)
            contacts.column("email", type:.Text, constraints:["NOT NULL"])
        }

        // Add an index
        v1.createIndex(
            "contacts_email",
            on: "contacts",
            columns: [ "email" ]
        )
    }
    
    // Version 2:
    schema.version(2) { v2 in        
        // Arbitrary SQL:
        v2.execute { db in
            try db.deleteFrom("contacts", whereExpr: "name IS NULL")
        }        
        // Tables can be altered in many ways.
        v2.alterTable("contacts") { contacts in
            contacts.alterColumn(
                "name",
                setConstraints: [ "NOT NULL" ]
            )
            contacts.addColumn("url", type: .Text)            
        }
    }
}

let db = Database()

// Migrate to the latest version:
let didMigrate = try AppSchema.migrate(db)

// Get the database version:
let migratedVersion = try db.queryUserVersionNumber()

// Reset the database:
try AppSchema.migrate(db, toVersion: 0)

The above example can be found in Migrations.playground.

Installation

Squeal can be installed via Carthage or CocoaPods.

Carthage

To install using Carthage, simply add the following line to your Cartfile:

github "nerdyc/Squeal"

CocoaPods

To install using Carthage, simply add the following to the appropriate target in your Podfile:

pod "Squeal"

License

Squeal is released under the MIT License. Details are in the LICENSE.txt file in the project.

Contributing

Contributions and suggestions are very welcome! No contribution is too small. Squeal (like Swift) is still evolving and feedback from the community is appreciated. Open an Issue, or submit a pull request!

The main requirement is for new code to be tested. Nobody appreciates bugs in their database.

Comments
  • Swift 2.0 Compatibility

    Swift 2.0 Compatibility

    Trying out Xcode 7 and there are a lot of Swift 2.0 compatibility issues, some are easily fixable but others I am not sure what to do.

    for example

    public func bindStringValue(stringValue:String, atIndex index:Int, error:NSErrorPointer = nil) -> Bool {
        let cString = stringValue.cStringUsingEncoding(NSUTF8StringEncoding)
    
        let negativeOne = UnsafeMutablePointer<Int>(bitPattern: -1)
        let opaquePointer = COpaquePointer(negativeOne)
        let transient = CFunctionPointer<((UnsafeMutablePointer<()>) -> Void)>(opaquePointer)
    
        let resultCode = sqlite3_bind_text(sqliteStatement, Int32(index), cString!, -1, transient)
        if resultCode != SQLITE_OK {
            if error != nil {
                error.memory = database.sqliteError
            }
            return false
        }
    
        return true
    }
    

    gives an error CFunctionPointer is unavailable, use a function type @convention(c) (T)->U

    opened by tyczj 8
  • installation problem

    installation problem

    Hi,

    I can't install Squeal to my Swift project. I followed all installation instructions, but I still get "Use of unresolved identifier 'Database'" error.

    Thanks for any help.

    opened by eriktelepovsky 7
  • Squeal should use non-generic class name

    Squeal should use non-generic class name

    I'm just going to throw this out there but I kinda cringed when I saw that the main Squeal interface was just called "Database." Keep in mind that I'm new to Swift (and most of my background is Ruby, which has a more-than-healthy obsession with namespacing) so there might be things I can do to namespace/locally rename the Squeal Database class or whatnot, so please correct me if I'm making a mountain out of a molehill (In ESCMAScript 6, for instance, any imported module can be opaquely aliased in the client code). That said, I believe there are a couple problems with this naming:

    (1) It's just not descriptive. When I inject a Squeal instance into my repository class at runtime, and a mock in-memory instance into the class for tests, they're both "Database" from the perspective of what they do in relation to the code that is using them, but they need easily distinguishable names. I'd much rather be dealing with instances of Squeal() (or Squeal::Database() would be ideal, but my understanding is that swift namespacing is way too clever to allow that), and MemoryStore() or whatever, simply for readability.

    (2) Other than the testing case, one could conceivably have multiple database interfaces, with Squeal being but one. No one library should assume that it is used in a vacuum.

    (3) Collisions with app code are not impossible by any means. Another (not iOS related) project I'm working on deals with databases and all the associated rigmarole as data objects, not code. Think a control panel or whatnot. Now my Database model class and my internal SQL connection library are at best very similarly named.

    Other than the naming thing, I have no complaints :)

    opened by endash 7
  • Swift 2 query method equivalent

    Swift 2 query method equivalent

    Hi, I used the query(string) method to make a generic method for select data. With the update of Squeal for Swift2, i cant use this method.

    var rows = try db.query(request) for row in rows{ //Treat row }

    I dont find equivalent of this...

    Regards

    opened by Persilos 5
  • dyld: Library not loaded: @rpath/Squeal.framework/Squeal

    dyld: Library not loaded: @rpath/Squeal.framework/Squeal

    Followed installation instructions. Works great on a simulator. Crashes on a device with following error: dyld: Library not loaded: @rpath/Squeal.framework/Squeal Referenced from: /private/var/mobile/Containers/Bundle/Application/91F3700E-F93C-4259-9E47-861E0B6069B7/MyApp.app/MyApp Reason: image not found

    I know it's not an issue with a framework itself, but I could not find any fix for it. Any advice? Thanks.

    opened by ruslankmlv 5
  • Failed to Code Sign Squeal-iOS

    Failed to Code Sign Squeal-iOS

    Hello,

    First off - thank you for making Squeal. I've been working on a relatively simple app that makes heavy use of an existing sqlite database, and I've found Squeal to be very intuitive and fast.

    I'm running into an issue now where I appear to need to be on the Squeal team in order to include the framework as part of my project. When I go to Build Phases -> Embed Frameworks and add Squeal.framework, I am told I need to code sign the Squeal.xcodeproj. When I try to do this, I am told I can't because I am not a part of team D7K65AYKY9. I have tried hitting "Fix Issue" and changing the team and signing types within xcode, but to no avail. I am currently unable to build due to this issue, which I suspect may be related to an update of xcode. I was wondering if this is an issue anyone else is experiencing, or whether anyone has any advice on how I should proceed.

    Any help is very much appreciated.

    Best,

    Marvin

    screen shot 2015-05-13 at 9 57 41 pm

    opened by cornerbodega 4
  • Unable to open database file

    Unable to open database file

    I'm creating a DAO class in swift using Squeal, however, when I try to create a database using the example in the documentation, I run into the "unable to open database file" error with SQLite.

    Is there a particular way I am supposed to pass in the file path to the constructor?

    opened by inaoumov 4
  • Not detecting Database class in my swift files

    Not detecting Database class in my swift files

    I followed your installation instructions and added the call let onDiskDatabase = Database(...) and it does not find the Database class. Am I missing an import or a framework setup on my project?

    opened by Barbur01 4
  • Can't put Database in another folder as App

    Can't put Database in another folder as App

    I've tried googling this to see if there's a way to do it, but I'm currently trying to make the database for my App in ~/STEiN/(AppName)/(db) - I've tried setting the database string to be the full directory, I've tried making a temporary database, then using system() to move it to where I want it (which got it where I wanted it!) BUT then when I go to use it I keep getting nil. Any help would be greatly appreciated!!

    opened by Stein-Net 3
  • insertInto(tableName:values:error:) throws build error

    insertInto(tableName:values:error:) throws build error

    when I try to use insertInto(tableName:values:error:) xcode throws an error saying Missing argument for parameter 'columns' in call

    If I use db?.insertInto(tableName, columns:, values:, error: err) it builds fine

    opened by tyczj 3
  • "missing required module 'sqlite3_ios'" when building unit test

    When I try to build a unit test that does an import of the whole project, it fails to compile with the error message given in the title. I added $(PROJECT_DIR)/Externals/Squeal/modules to the Swift import paths list, so don't know why this is happening.

    opened by ijt 3
  • Migration: Table already exists

    Migration: Table already exists

    First thank you for this very simple to use library.

    I'm trying to use Migrations. It's very easy to setup. BUT one thing I cannot figure out is why exceptions are thrown for the table existing already. The migrations don't run since there's an exception.

    I've setup a schema like this:

    func schema() -> Schema {
            return Schema(identifier: "workouts") { (schemaBuilder:SchemaBuilder) in
                schemaBuilder.version(0) { (v1:VersionBuilder) in
                    
                    //WORKOUTS
                    v1.createTable(workoutsTableName) { (workouts:TableBuilder) in
                        workouts.primaryKey("id", autoincrement: true)
                        workouts.column("title", .Text)
                        workouts.column("notes", .Text)
                        workouts.column("timerType", type: .Integer)
                        workouts.column("countdown", type: .Real)
                        workouts.column("rounds", type: .Integer)
                        workouts.column("work", type: .Real)
                        workouts.column("rest", type: .Real)
                        workouts.column("asPerscribed", type: .Integer)
                        workouts.column("asPerscribedPlus", type: .Integer)
                        workouts.column("completedRounds", type: .Integer)
                        workouts.column("completedReps", type: .Integer)
                        workouts.column("startDate", type: .Real)
                        workouts.column("endDate", type: .Real)
                    }
                    
                    //LAPS
                    v1.createTable(workoutLapsTableName) { (workoutLaps:TableBuilder) in
                        workoutLaps.primaryKey("id", autoincrement: true)
                        workoutLaps.column("workoutId", type: .Integer)
                        workoutLaps.column("lap", type: .Real)
                    }
                }
                
                //ADD TOTAL DURATION COLUMN.
                schemaBuilder.version(1) { (v2:VersionBuilder) in
    
                    v2.alterTable("workouts") { (workouts:TableAlterer) in
                        workouts.addColumn("totalDuration", .Real)
                    }
    
                }
                
            }
        }
    

    And my init code is this:

    func initDatabase() throws {
            let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
            guard paths.count > 0 else {
                throw GTDBError.noDocumentPath
            }
            let docPathURL = URL(fileURLWithPath: paths[0])
            let dbPath = docPathURL.appendingPathComponent(sqliteFileName)
            do {
                try db = Database(path: dbPath.absoluteString)
            } catch {
                throw GTDBError.noDatabaseFile
            }
            do {
                try _ = self.schema().migrate(db)
            } catch let error as NSError where error.domain == sqliteErrorDomain {
                var continueThrow = true
                
                if error.code == 1 { //table exists?
                    continueThrow = false
                }
                
                if continueThrow {
                    print(error)
                    throw GTDBError.sqliteError
                }
                
            } catch let error {
                print(error)
                throw GTDBError.otherError
            }
        }
    

    The issue I'm hitting is exceptions thrown for the "workouts" table already existing, which causes the migrations to not run:

    [logging] table "workouts" already exists in "CREATE TABLE "workouts" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT,"title" TEXT,"notes" TEXT,"timerType" INTEGER,"countdown" REAL,"rounds" INTEGER,"work" REAL,"rest" REAL,"asPerscribed" INTEGER,"asPerscribedPlus" INTEGER,"completedRounds" INTEGER,"completedReps" INTEGER,"startDate" REAL,"endDate" REAL )"
    
    opened by gngrwzrd 0
Releases(v1.2.0)
Owner
Christian Niles
Christian Niles
A stand-alone Swift wrapper around the SQLite 3 client library.

Perfect - SQLite Connector This project provides a Swift wrapper around the SQLite 3 library. This package builds with Swift Package Manager and is pa

PerfectlySoft Inc. 47 Nov 19, 2022
A Cocoa / Objective-C wrapper around SQLite

FMDB v2.7 This is an Objective-C wrapper around SQLite. The FMDB Mailing List: https://groups.google.com/group/fmdb Read the SQLite FAQ: https://www.s

August 13.7k Dec 28, 2022
rTracker is a complete iOS application for creating local on-device databases ('trackers') to log data with timestamps.

rTracker is a complete iOS application for creating local on-device databases ('trackers') to log data with timestamps. Trackers can use a va

Rob Miller 40 Dec 10, 2022
Sharing SQL queries between Server and Mobile databases

Sharing SQL queries between Server and Mobile databases Overview As we all know, code is expensive to maintain, and the more complex the code, the mor

Aaron LaBeau 4 May 24, 2022
SQLite.swift - A type-safe, Swift-language layer over SQLite3.

SQLite.swift provides compile-time confidence in SQL statement syntax and intent.

Stephen Celis 8.7k Jan 3, 2023
YapDB is a collection/key/value store with a plugin architecture. It's built atop sqlite, for Swift & objective-c developers.

YapDatabase is a collection/key/value store and so much more. It's built atop sqlite, for Swift & Objective-C developers, targeting macOS, iOS, tvOS &

Yap Studios 3.3k Dec 29, 2022
Swift APIs for SQLite: Type-safe down to the schema. Very, very, fast. Dependency free.

Lighter Lighter is a set of technologies applying code generation to access SQLite3 databases from Swift, e.g. in iOS applications or on the server. L

Lighter.swift 330 Dec 26, 2022
Realm is a mobile database: a replacement for Core Data & SQLite

Realm is a mobile database that runs directly inside phones, tablets or wearables. This repository holds the source code for the iOS, macOS, tvOS & wa

Realm 15.7k Jan 1, 2023
Implement Student Admission System using SQlite

StudentAdmissionSQLiteApp Implement Student Admission System using SQlite. #Func

Hardik 2 Apr 27, 2022
BucketServer - Small API with SQLite database that saves notes for an iOS appliction called Bucket list

BucketList Server-Side Small API with SQLite database that saves notes for an iO

null 0 Dec 30, 2021
macOS Sqlite tableView 샘플 - objective c

목적 Objective C언어를 이용하여 macOS를 개발해본다. Sqlite를 이용하여 데이터를 저장하고, 불러와본다. FMDB를 이용한다. 데이터를 NSTableView를 이용하여 불러와본다. 추가적으로 NSOutlineView 구현해본다. 추가적으로 KVOCont

HyunSu Park 0 Jan 10, 2022
MagicData - A replacement of SQlite, CoreData or Realm.

MagicData A replacement of SQlite, CoreData or Realm. It is very easy to use and is a light version. Guides MagicData We use MagicData manage all the

Underthestars-zhy 20 Jul 4, 2022
A stand-alone Swift wrapper around the mongo-c client library, enabling access to MongoDB servers.

This package is deprecated in favour of the official Mongo Swift Driver. We advise users to switch to that pack

PerfectlySoft Inc. 54 Jul 9, 2022
CoreData/Realm sweet wrapper written in Swift

What is SugarRecord? SugarRecord is a persistence wrapper designed to make working with persistence solutions like CoreData in a much easier way. Than

Modo 2.1k Dec 9, 2022
A stand-alone Swift wrapper around the MySQL client library, enabling access to MySQL servers.

Perfect - MySQL Connector This project provides a Swift wrapper around the MySQL client library, enabling access to MySQL database servers. This packa

PerfectlySoft Inc. 118 Jan 1, 2023
A stand-alone Swift wrapper around the libpq client library, enabling access to PostgreSQL servers.

Perfect - PostgreSQL Connector This project provides a Swift wrapper around the libpq client library, enabling access to PostgreSQL servers. This pack

PerfectlySoft Inc. 51 Nov 19, 2022
A stand-alone Swift wrapper around the FileMaker XML Web publishing interface, enabling access to FileMaker servers.

Perfect - FileMaker Server Connector This project provides access to FileMaker Server databases using the XML Web publishing interface. This package b

PerfectlySoft Inc. 33 Jul 13, 2022
A Swift wrapper for system shell over posix_spawn with search path and env support.

AuxiliaryExecute A Swift wrapper for system shell over posix_spawn with search path and env support. Usage import AuxiliaryExecute AuxiliaryExecute.l

Lakr Aream 11 Sep 13, 2022
An Objective-C wrapper for RocksDB - A Persistent Key-Value Store for Flash and RAM Storage.

ObjectiveRocks ObjectiveRocks is an Objective-C wrapper of Facebook's RocksDB - A Persistent Key-Value Store for Flash and RAM Storage. Current RocksD

Iskandar Abudiab 56 Nov 5, 2022