{"id":17279806,"url":"https://github.com/fnc12/sqlite-orm-swift","last_synced_at":"2025-04-14T08:35:30.480Z","repository":{"id":41386368,"uuid":"405730047","full_name":"fnc12/sqlite-orm-swift","owner":"fnc12","description":"🧡 Easy to use SQLite ORM library written with love and Swift","archived":false,"fork":false,"pushed_at":"2023-05-21T23:52:23.000Z","size":508,"stargazers_count":42,"open_issues_count":2,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-27T22:07:29.461Z","etag":null,"topics":["database","orm","sql","sqlite","sqlite3","sqliteorm","swift","swift5"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fnc12.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"fnc12","custom":"https://paypal.me/fnc12"}},"created_at":"2021-09-12T19:06:38.000Z","updated_at":"2025-03-22T06:02:47.000Z","dependencies_parsed_at":"2023-02-09T13:01:19.911Z","dependency_job_id":null,"html_url":"https://github.com/fnc12/sqlite-orm-swift","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fnc12%2Fsqlite-orm-swift","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fnc12%2Fsqlite-orm-swift/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fnc12%2Fsqlite-orm-swift/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fnc12%2Fsqlite-orm-swift/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fnc12","download_url":"https://codeload.github.com/fnc12/sqlite-orm-swift/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248846449,"owners_count":21170968,"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":["database","orm","sql","sqlite","sqlite3","sqliteorm","swift","swift5"],"created_at":"2024-10-15T09:18:39.663Z","updated_at":"2025-04-14T08:35:30.436Z","avatar_url":"https://github.com/fnc12.png","language":"Swift","readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/fnc12/sqlite-orm-swift/blob/master/logo.png\" alt=\"Sublime's custom image\" width=\"600\"/\u003e\n\u003c/p\u003e\n\n# SQLiteORM for Swift\n\n![example workflow](https://github.com/fnc12/sqlite-orm-swift/actions/workflows/swift.yml/badge.svg)\n\nSQLiteORM-Swift is an ORM library for SQLite3 built with Swift 5\n\n# Advantages\n\n* **No raw string queries**\n* **Intuitive syntax**\n* **Comfortable interface - one call per single query**\n* **CRUD support**\n* **Aggregate functions support**\n* **Does not depend on `Codable` protocol**\n* **The only dependency** - SQLite3\n* **In memory database support** - provide `:memory:` or empty filename\n* **Transactions support**\n* **Migrations functionality**\n* **LIMIT support**\n\n`SQLiteORM` library allows to create easy data model mappings to your database schema. It is built to manage (CRUD) objects with a primary key and without it. It also allows you to specify table names and column names explicitly no matter how your classes actually named. And it does not depend on `Codable` protocol. Take a look at example:\n\n```swift\nimport SQLiteORM\n\nstruct User : Initializable {\n    var id = 0\n    var firstName = \"\"\n    var lastName = \"\"\n    var birthDate = \"\"\n    var imageUrl: String?\n    var typeId = 0\n}\n\nstruct UserType : Initializable {\n    var id = 0\n    var name = \"\"\n}\n```\n\nSo we have database with predefined schema like \n\n```sql\nCREATE TABLE users (\n    id INTEGER PRIMARY KEY NOT NULL, \n    first_name TEXT NOT NULL, \n    last_name TEXT NOT NULL, \n    birth_date INTEGER NOT NULL, \n    image_url TEXT, \n    type_id INTEGER NOT NULL)\n    \nCREATE TABLE user_types (\n    id INTEGER PRIMARY KEY NOT NULL, \n    name TEXT NOT NULL)\n```\n\nNow we tell `SQLiteORM` library about our schema and provide database filename. We create `storage` helper object that has CRUD interface. Also we create every table and every column. All code is intuitive and minimalistic.\n\n```swift\nlet path = getDocumentsDirectory() + \"/db.sqlite\"\ndo {\n    let storage = try Storage(filename: path,\n                              tables:\n                                Table\u003cUser\u003e(name: \"users\",\n                                            columns:\n                                               Column(name: \"id\", keyPath: \\User.id, constraints: primaryKey(), notNull()),\n                                               Column(name: \"first_name\", keyPath: \\User.firstName, constraints: notNull()),\n                                               Column(name: \"last_name\", keyPath: \\User.lastName, constraints: notNull()),\n                                               Column(name: \"birth_date\", keyPath: \\User.birthDate, constraints: notNull()),\n                                               Column(name: \"image_url\", keyPath: \\User.imageUrl),\n                                               Column(name: \"type_id\", keyPath: \\User.typeId, constraints: notNull())),\n                                Table\u003cUserType\u003e(name: \"user_types\",\n                                                columns:\n                                                    Column(name: \"id\", keyPath: \\UserType.id, constraints: primaryKey(), notNull()),\n                                                    Column(name: \"name\", keyPath: \\UserType.name, constraints: notNull())))\n}catch{\n    print(\"error happened \\(error)\")\n}\n```\n\nToo easy isn't it? To create a column you have to pass two arguments at least: its name in the table and your mapped class keypath. You can also add extra arguments to tell your storage about column's constraints like `primaryKey()`, `notNull()`, `unique()`.\n\n# CRUD\n\nLet's create and insert new `User` into our database. First we need to create a `User` object with any id and call `insert` function. It will return id of just created user or throw exception if something goes wrong. If you want to insert a user with id you specified then you need to use `replace` function instead of `insert`.\n\n```swift\nvar user = User(id: 0, firstName: \"John\", lastName: \"Doe\", birthDate: 664416000, imageUrl: \"url_to_heaven\", typeId: 3)\nlet insertedId = try storage.insert(object: user)\nprint(\"insertedId = \\(insertedId)\")\nuser.id = Int(insertedId)\n\nlet secondUser = User(id: 2, firstName: \"Alice\", lastName: \"Inwonder\", birthDate: 831168000, imageUrl: nil, typeId: 2)\ntry storage.replace(object: secondUser) //  insert with 'id' 2\n```\n\nNext let's get our user by id.\n\n```swift\nif let user1: User = try storage.get(id: 1) {\n    print(\"user = \\(user1.firstName) \\(user1.lastName)\")\n} else {\n    print(\"user with id 1 does not exist\")\n}\n```\n\nWe can also update our user. Storage updates row by id provided in `user` object and sets all other non `primary_key` fields to values stored in the passed `user` object. So you can just assign fields to `user` object you want and call `update`:\n\n```swift\nuser.firstName = \"Nicholas\"\nuser.imageUrl = \"https://cdn1.iconfinder.com/data/icons/man-icon-set/100/man_icon-21-512.png\"\ntry storage.update(object: user)\n```\n\nAnd delete. To delete you have to pass a whole object.\n\n```swift\ntry storage.delete(object: user)\n```\n\nAlso we can extract all objects into `Array`:\n\n```swift\nlet allUsers: [User] = try storage.getAll()\nprint(\"allUsers (\\(allUsers.count):\")\nfor user in allUsers {\n    print(user)\n}\n```\n\n# Aggregate functions\n\n```swift\n//  SELECT AVG(id) FROM users\nlet averageId = try storage.avg(\\User.id)\n\n//  SELECT AVG(birth_date) FROM users\nlet averageBirthDate = try storage.avg(\\User.birthDate)\n\n//  SELECT COUNT(*) FROM users\nlet usersCount = try storage.count(all: User.self)\n\n//  SELECT COUNT(id) FROM users\nlet countId = try storage.count(\\User.id)\n\n//  SELECT COUNT(image_url) FROM users\nlet countImageUrl = try storage.count(\\User.imageUrl)\n\n//  SELECT GROUP_CONCAT(id) FROM users\nlet concatedUserId = try storage.groupConcat(\\User.id)\n\n//  SELECT GROUP_CONCAT(id, '---') FROM users\nlet concatedUserIdWithDashes = try storage.groupConcat(\\User.id, separator: \"---\")\n\n//  SELECT MAX(id) FROM users\nif let maxId = try storage.max(\\User.id) {\n    print(\"maxId = \\(maxId)\")\n} else {\n    print(\"maxId is nil\")\n}\n\n//  SELECT MIN(id) FROM users\nif let minId = try storage.min(\\User.id) {\n    print(\"minId = \\(minId)\")\n} else {\n    print(\"maxId is nil\")\n}\n\n//  SELECT SUM(id) FROM users\nif let sumId = try storage.sum(\\User.id) {\n    print(\"sumId = \\(sumId)\")\n} else {\n    print(\"sumId is nil\")\n}\n\n//  SELECT TOTAL(id) FROM users\nlet totalId = try storage.total(\\User.id)\n```\n\n# Where conditions\n\nYou also can select objects with custom where conditions with `==`, `!=`, `\u003e`, `\u003e=`, `\u003c`, `\u003c=`.\n\nFor example: let's select users with id lesser than 10:\n```swift\n//  SELECT * FROM users WHERE id \u003c 10\nlet idLesserThan10: [User] = try storage.getAll(where_(\\User.id \u003c 10))\nprint(\"idLesserThan10 count = \\(idLesserThan10.count)\")\nfor user in idLesserThan10 {\n    print(\"\\(user)\")\n}\n```\nOr select all users who's first name is not equal \"John\":\n```swift\n//  SELECT * FROM users WHERE first_name != 'John'\nlet notJohn: [User] = try storage.getAll(where_(\\User.firstName != \"John\"))\nprint(\"notJohn count = \\(notJohn.count)\")\nfor user in idLesserThan10 {\n    print(\"\\(user)\")\n}\n```\n\n# Migrations functionality\n\nThere are no explicit `up` and `down` functions that are used to be used in migrations. Instead `SQLiteORM` offers `syncSchema` function that takes responsibility of comparing actual db file schema with one you specified in `Storage` init call and if something is not equal it alters or drops/creates schema.\n\n```swift\ntry storage.syncSchema(preserve: true)\n```\n\nPlease beware that `syncSchema` doesn't guarantee that data will be saved. It *tries* to save it only. Below you can see rules list that `syncSchema` follows during call:\n* if there are excess tables exist in db they are ignored (not dropped)\n* every table from storage is compared with it's db analog and \n    * if table doesn't exist it is created\n    * if table exists its colums are being compared with table_info from db and\n        * if there are columns in db that do not exist in storage (excess) table will be dropped and recreated if `preserve` is `false`, and table will be copied into temporary table without excess columns, source table will be dropped, copied table will be renamed to source table (sqlite remove column technique) if `preserve` is `true`. Beware that setting it to `true` may take time for copying table rows.\n        * if there are columns in storage that do not exist in db they will be added using 'ALTER TABLE ... ADD COLUMN ...' command and table data will not be dropped but if any of added columns is null but has not default value table will be dropped and recreated\n        * if there is any column existing in both db and storage but differs by any of properties (pk, notnull) table will be dropped and recreated (dflt_value isn't checked cause there can be ambiguity in default values, please beware).\n\nThe best practice is to call this function right after storage creation.\n\n# Notes\n\nTo work well your data model class must inherit from `Initializable` which required only `init()` with no arguments existance and must not have const fields mapped to database cause they are assigned during queries. Otherwise code won't compile.\n","funding_links":["https://github.com/sponsors/fnc12","https://paypal.me/fnc12"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffnc12%2Fsqlite-orm-swift","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffnc12%2Fsqlite-orm-swift","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffnc12%2Fsqlite-orm-swift/lists"}