{"id":20430894,"url":"https://github.com/alexbinary/sqlite.swiftly","last_synced_at":"2026-04-15T16:04:35.690Z","repository":{"id":80884948,"uuid":"172193831","full_name":"alexbinary/SQLite.swiftly","owner":"alexbinary","description":"✨ A simple Swift wrapper for SQLite on iOS.","archived":false,"fork":false,"pushed_at":"2019-05-01T15:02:40.000Z","size":2124,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-15T17:35:55.163Z","etag":null,"topics":["api","database","ios","sqlite","swift","wrapper"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alexbinary.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-02-23T09:05:34.000Z","updated_at":"2022-04-04T06:22:08.000Z","dependencies_parsed_at":null,"dependency_job_id":"b187f212-ebea-4c43-9731-41382ebe2a68","html_url":"https://github.com/alexbinary/SQLite.swiftly","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexbinary%2FSQLite.swiftly","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexbinary%2FSQLite.swiftly/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexbinary%2FSQLite.swiftly/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexbinary%2FSQLite.swiftly/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alexbinary","download_url":"https://codeload.github.com/alexbinary/SQLite.swiftly/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241975134,"owners_count":20051428,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","database","ios","sqlite","swift","wrapper"],"created_at":"2024-11-15T08:09:29.038Z","updated_at":"2025-12-03T16:04:46.947Z","avatar_url":"https://github.com/alexbinary.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"sqlite-logo.jpg\" height=128\u003e\n  \u003cimg src=\"swift-logo.png\" height=128\u003e\n\u003c/p\u003e\n\n\n#  SQLite.swiftly\n\n[![Build Status](https://travis-ci.org/alexbinary/SQLite.swiftly.svg?branch=master)](https://travis-ci.org/alexbinary/SQLite.swiftly)\n\nThe [SQLite database engine](https://sqlite.org/) is available out of the box \nfor iOS apps, but it requires the use of a\n[clumsy and error prone C-style API](https://sqlite.org/cintro.html).\n\n*SQLite.swiftly* is a simple wrapper on top of the C-style SQLite API written in\nSwift that provides a modern and type safe API allowing as many compile time\nchecks as possible, so you can avoid many common mistakes before even running\nthe code.\n\nIn short, *SQLite.swiftly*  makes writing incorrect code harder.\n\n\n## Origins and features\n\n*SQLite.swiftly* was originally part of the source code of the\n[My LEGO Collection](https://github.com/alexbinary/My-LEGO-Collection) project.\nIt has been moved into its own repository so it can have its own life, and why\nnot inspire someone.\n\nAs such, development is driven by the needs of the\n[My LEGO Collection](https://github.com/alexbinary/My-LEGO-Collection) project,\nand only features that are needed by that project are being developped.\n\nAs a result, many features you would expect from a generic SQLite client are \nnot implemented in *SQLite.swiftly*.\n\nSupported features:\n\n- [x] create tables\n- [x] insert into tables\n- [x] select from tables (complete table dump, not suited for very large data sets)\n\nSupported SQLite data types:\n\n- [x] char\n- [x] bool\n\n*SQLite.swiftly* is intended to be used primarily on iOS.\n\n\n## Reference example\n\nOne of the key principle of *SQLite.swiftly* is that it requires you to declare\nyour database structure. That way it can generate the correct SQL queries for\nyou.\n\n```swift\n// First, declare the table\nlet idColumnDescription = ColumnDescription(name: \"id\", type: .int(size: 11), nullable: false)\nlet nameColumnDescription = ColumnDescription(name: \"name\", type: .char(size: 255), nullable: false)\nlet contactTableDescription = TableDescription(name: \"contact\", columns: [idColumnDescription, nameColumnDescription])\n\n// Open a connection to the database\nlet db = Connection(toNewDatabaseAt: \"path/to/db.sqlite\")\n\n// Create the table\ndb.createTable(describedBy: contactTableDescription)\n\n// Prepare a statement that inserts data into the table\nlet insertStatement = db.prepareInsertIntoTable(describedBy: contactTableDescription)\n\n// Insert data into the table\ninsertStatement.insert([\n  idColumnDescription.name: 1,\n  nameColumnDescription.name: \"Alice\"\n])\ninsertStatement.insert([\n  idColumnDescription.name: 2,\n  nameColumnDescription.name: \"Bob\"\n])\n\n// Read data\nlet rows = db.readlAllRows(fromTable: contactTableDescription)\nfor row in rows {\n  for (column, value) in row {\n    print(\"\\(column.name): \\(String(describing: value))\")\n  }\n}\n// \"id: 1\"\n// \"name: Alice\"\n// \"id: 2\"\n// \"name: Bob\"\n```\n\n### Subclassing\n\nFor even more type safety in your app, you can subclass `Connection` and \n`InsertStatement`:\n\n```swift\n\nclass ContactTableDescription: TableDescription {\n  \n  let idColumn = ColumnDescription(name: \"id\", type: .int(size: 11), nullable: false)\n  let nameColumn = ColumnDescription(name: \"name\", type: .char(size: 255), nullable: false)\n\n  init() {\n    super.init(name: \"colors\", columns: [idColumn, nameColumn])\n  }\n\n  struct Row {\n    let id: Int\n    let name: String\n  }\n}\n\nstruct MyDatabaseSchema {\n  static let contactTableDescription = ContactTableDescription()\n}\n\nclass ContactInsertStatement: InsertStatement {\n\n  init(connection: Connection) {\n    super.init(insertingIntoTable: MyDatabaseSchema.contactTableDescription, on: connection)\n  }\n    \n  func insert(id: Int, name: String) {\n    let table = MyDatabaseSchema.contactTableDescription\n    insert([\n      table.idColumn.name: id,\n      table.nameColumn.name: name,\n    ])\n  }\n}\n\nclass MyDatabaseConnection: Connection {\n\n  func prepareContactInsert() -\u003e ContactInsertStatement {\n    return ContactInsertStatement(connection: self)\n  }\n\n  func readAllContacts() -\u003e [ContactTableDescription.Row] {\n    let table = MyDatabaseSchema.contactTableDescription\n    return readAllRows(fromTable: table).map { columnValues in\n      return ContactTableDescription.Row(\n        id: columnValues[table.idColumn.name] as! Int,\n        name: columnValues[table.nameColumn.name] as! String,\n      )\n    }\n  }\n}\n```\n\n\n## Getting started\n\n*SQLite.swiftly* is distributed as a Cocoa Touch Framework.\n\nI recommend installing *SQLite.swiftly* as a git submodule in your project.\n\nFirst, add the submodule:\n\n```bash\n$ cd /path/to/your/repo\n$ git submodule add https://github.com/alexbinary/SQLite.swiftly.git Libs/SQLite.swiftly\n```\n\nThis will clone the *SQLite.swiftly* repository in `Libs/SQLite.swiftly` inside your repository.\n\nThen you must reference the *SQLite.swiftly* framework in your Xcode project.\n\nIn Xcode's navigation pane, select your project, then select the target for your\napp, then under the General tab, scroll down to Linked Frameworks and Librairies,\nand click the + button, then click \"Add other\", and navigate to the file\n`SQLite.Swiftly.xcodeproj` from the submodule. The SQLite.Swiftly project appears\nin your navigation pane under \"Frameworks\". Click the + button again in Linked\nFrameworks and Librairies, and this time select `SQLite_Swiftly.framework`.\n\n![](getting-started.gif)\n\nYou can now `import SQLite_Swiftly` and start using it.\n\n\n## Demo app\n\nA demo iPhone/iPad app is provided in `Demo App/`. You can run it on the simulator\nor on a real device.\n\nWhen you launch the app it creates a database on the device's file system,\nwrites some data then reads it back.\n\n![](demo-app.png)\n\n\n## Motivation and design\n\nThe SQLite C-style APIs make writing incorrect code way too easy.\n\nBesides the fact that the SQLite C-style APIs require the use of low level and\nnot very swifty types such as `UnsafePointer` instead of `String`, connections\nand statement are both manipulated through pointers of the same type\n`OpaquePointer`, which makes it easy to make mistakes and pass one object when\nyou intended the other one.\n\nBy nature, a pointer can point to an uninitialized or destroyed object, and\nthere is no way to know if it points to a valid object at compile time. Keeping\ntrack of what happens to the objects is on the programmer, and programmers\ninevitably make mistakes.\n\nOn top of that, objects have an internal state machine, which the programmer\nmust keep track of in order to make the appropriate calls at the appropriate\ntime. Again, it is easy to make mistakes, and the compiler cannot help at all.\nAll the work is on the programmer, and errors are waiting at the corner.\n\nExample:\n\n```swift\n// -- Open a connection\n\nvar connectionPointer: OpaquePointer!\nsqlite3_open(\"path/to/db.sqlite\", \u0026connectionPointer)\n\n// Easy, right? but what if the connection failed? sure we can check the\n// status of the connection, but if we fail to do the proper checks nothing is \n// preventing us to use an invalid connection pointer.\n\n\n// -- Compile an insert statement\n\nvar statementPointer: OpaquePointer!\nsqlite3_prepare_v2(connectionPointer, \"INSERT INTO table(c1, c2) VALUES(1,2\"), -1, \u0026statementPointer, nil)\n\n// What if we mistakenly swap the connectionPointer and statementPointer? we\n// have to wait for the code to run, only to get an obscure and hard to debug \n// error. Come on we can do better!\n\n// And what if we made a mistake in the SQL query? again, we can check the \n// status of the statement but it is so easy to forget, and we should not have\n// to do runtime checks on something we write in the code anyway.\n\n// And also, what is that -1 doing? and that `nil` at the end?\n\n\n// -- Release resources\n\nsqlite3_finalize(statementPointer)\n\n// So now we have to worry about the fact that anyone can destroy a statement\n// without us knowing? Do we need to check if the object is still valid every\n// time before we use it? And what do we do if it is not?\n\nsqlite3_close(connectionPointer)\n\n// Same problems with the connection.\n```\n\n*SQLite.swiftly* solves all these problems so you can focus on building your app \ninstead of managing pointers and state machines.\n\nThe main design principles are:\n\n1. Higher level objects encapsulate the underlying objects life cycle and expose\nfriendlier APIs.\n\n2. You do not write SQL queries directly but instead describes the database\nschema and lets *SQLite.swiftly* build the SQL queries for you.\n\n\n### Higher level objects\n\nThese objects are `Connection` and `Statement` and its subclasses.\nThey all hold a pointer to the low level SQLite object.\n\nThese objects are immutable as much as possible. Connections are opened in \n`Connection`'s initializer and closed in its deinitializer. Statements\nare compiled in `Statement`'s initializer and destroyed in its\ndeinitializer. This makes it impossible to use an invalid pointer.\n\nFrom the programmer's point of view `Connection` and `Statement`\nare stateless objects. Methods that mutate the underlying object always bring it\nback to a default state before or after they do their work.\n\n`Connection` and `Statement` expose features through simple APIs\nthat leverage all the goodness that Swift has to offer.\n\n\n### Automatic SQL query generation\n\nThe best way to avoid writing bugs is to avoid writing the code all together.\n\n*SQLite.swiftly* writes SQL queries so you don't have to. You provide type safe\ndescriptions of your database schema with tables and columns and let\n*SQLite.swiftly* worry about writing correct SQL code.\n\nThis also has the benefit of having a single source of truth instead of\nspreading the database structure in raw SQL queries that the compiler cannot\nunderstand.\n\n\nFor more details, see the [design notes](design-notes.md).\n\n\n## Development\n\nTo get hacking, start by cloning the repo:\n\n```bash\n$ git clone https://github.com/alexbinary/SQLite.swiftly.git\n```\n\nThen setup the git pre-commit hook:\n\n```bash\n$ ln -s \"$PWD\"/pre-commit.sh .git/hooks/pre-commit\n```\n\nThis will build the framework and run the tests before each commit and abort the\ncommit if the build or tests fail.\n\nThen open the Xcode project `SQLite.Swiftly/SQLite.Swiftly.xcodeproj`.\nThe project has two targets, the first one is for the framework, the other one\nis for the unit tests.\n\nHack into the framework, then test your work by writing unit tests,\neither updating existing tests or adding new ones.\n\nTo contribute on the demo app, open `Demo App/SQLite.swiftly Demo App.xcodeproj`.\n\nIf you have a question, open an issue.\n\nIf you want to contribute code or documentation, open a pull request.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexbinary%2Fsqlite.swiftly","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexbinary%2Fsqlite.swiftly","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexbinary%2Fsqlite.swiftly/lists"}