{"id":13396530,"url":"https://github.com/ryanfowler/SwiftData","last_synced_at":"2025-03-13T23:31:28.624Z","repository":{"id":20055988,"uuid":"23324535","full_name":"ryanfowler/SwiftData","owner":"ryanfowler","description":"Simple and Effective SQLite Handling in Swift","archived":true,"fork":false,"pushed_at":"2019-10-31T07:27:51.000Z","size":437,"stargazers_count":515,"open_issues_count":32,"forks_count":97,"subscribers_count":45,"default_branch":"master","last_synced_at":"2024-07-31T18:17:41.052Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/ryanfowler.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-08-25T18:44:33.000Z","updated_at":"2024-04-25T06:52:06.000Z","dependencies_parsed_at":"2022-08-31T05:21:21.594Z","dependency_job_id":null,"html_url":"https://github.com/ryanfowler/SwiftData","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/ryanfowler%2FSwiftData","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryanfowler%2FSwiftData/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryanfowler%2FSwiftData/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryanfowler%2FSwiftData/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ryanfowler","download_url":"https://codeload.github.com/ryanfowler/SwiftData/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221421556,"owners_count":16817831,"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":[],"created_at":"2024-07-30T18:00:56.012Z","updated_at":"2024-10-25T11:30:48.005Z","avatar_url":"https://github.com/ryanfowler.png","language":"Swift","readme":"SwiftData\n=========\n\nWorking with SQLite is a pain in Swift - that's where SwiftData comes in.\n\nSwiftData is a simple and effective wrapper around the SQLite3 C API written completely in Swift.\n\n\n##Features\n\n- Execute SQL statements directly\n- Bind objects conveniently to a string of SQL\n- Queries return an easy to use array\n- Support for transactions and savepoints\n- Inline error handling\n- Completely thread-safe by default\n- Convenience functions for common tasks, including table and index creation/deletion/querying\n- Database connection operations (opening/closing) are automatically handled\n\n\n##Installation\n\nCurrently, it's as easy as adding the file 'SwiftData.swift' as a git submodule, and dragging it into your project.\nEnsure that you've added 'libsqlite3.dylib' as a linked framework and that you've added `#import \"sqlite3.h\"` to your Briding-Header.h file.\n\n\n##System Requirements\n\nXcode Version:\n\n- Xcode 6\n\nCan be used in applications with operating systems:\n\n- iOS 7.0+\n- Mac OS X 10.9+\n\n\n##Usage\n\nThis section runs through some sample usage of SwiftData.\n\nThe full API documentation can be found [here](http://ryanfowler.github.io/SwiftData)\n\n\n###Table Creation\n\nBy default, SwiftData creates and uses a database called 'SwiftData.sqlite' in the 'Documents' folder of the application.\n\nTo create a table in the database, you may use the convenience function:\n\n```swift\nif let err = SD.createTable(\"Cities\", withColumnNamesAndTypes: [\"Name\": .StringVal, \"Population\": .IntVal, \"IsWarm\": .BoolVal, \"FoundedIn\": .DateVal]) {\n    //there was an error during this function, handle it here\n} else {\n    //no error, the table was created successfully\n}\n```\n\nSimilar convenience functions are provided for:\n\n- deleting a table:\n```swift\nlet err = SD.deleteTable(\"TableName\")\n```\n- finding all existing tables in the database:\n```swift\nlet (tables, err) = SD.existingTables()\n```\n\nAlternatively, a table could be created using a SQL statement directly, as shown in the 'Execute A Change' section below.\n\n\n=================\n###Execute A Change\n\nThe `SD.executeChange()` function can be used to execute any non-query SQL statement (e.g. INSERT, UPDATE, DELETE, CREATE, etc.).\n\nTo create a table using this function, you could use the following:\n\n```swift\nif let err = SD.executeChange(\"CREATE TABLE Cities (ID INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT, Population INTEGER, IsWarm BOOLEAN, FoundedIn DATE)\") {\n    //there was an error during this function, handle it here\n} else {\n    //no error, the table was created successfully\n}\n```\n\nThe table created by this function call is the equivalent of the convenience function used in the earlier section.\n\nNow that we've created our table, \"Cities\", we can insert a row into it like so:\n\n```swift\nif let err = SD.executeChange(\"INSERT INTO Cities (Name, Population, IsWarm, FoundedIn) VALUES ('Toronto', 2615060, 0, '1793-08-27')\") {\n    //there was an error during the insert, handle it here\n} else {\n    //no error, the row was inserted successfully\n}\n```\n\n#####Binding Values\n\nAlternatively, we could insert a row with object binding:\n\n```swift\n//from user input\nlet name: String = //user input\nlet population: Int = //user input\nlet isWarm: Bool = //user input\nlet foundedIn: NSDate = //user input\n\nif let err = SD.executeChange(\"INSERT INTO Cities (Name, Population, IsWarm, FoundedIn) VALUES (?, ?, ?, ?)\", withArgs: [name, population, isWarm, foundedIn]) {\n    //there was an error during the insert, handle it here\n} else {\n    //no error, the row was inserted successfully\n}\n```\n\nThe provided objects will be escaped and will bind to the '?' characters (in order) in the string of SQL.\n\nBe aware that although this uses similar syntax to prepared statements, it actually uses the public function:\n```swift\nlet escValue = SD.escapeValue(object)\n```\nto escape objects internally, which you may also use yourself.\nThis means that the objects will attempt to bind to *ALL* '?'s in the string of SQL, including those in strings and comments.\n\nThe objects are escaped and will bind to a SQL string in the following manner:\n\n- A String object is escaped and surrounded by single quotes (e.g. 'sample string')\n- An Int object is left untouched (e.g. 10)\n- A Double object is left untouched (e.g. 10.8)\n- A Bool object is converted to 0 for false, or 1 for true (e.g. 1)\n- 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')\n- An NSData object is prefaced with an 'X' and converted to a hexadecimal string surrounded by single quotes (e.g. X'1956a76c')\n- 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')\n\nAll other object types will bind to the SQL string as 'NULL', and a warning message will be printed to the console.\n\n#####Binding Identifiers\n\nIf 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:\n\n```swift\n//from user input\nlet columnName1 = //user input\nlet columnName2 = //user input\n\nif let err = SD.executeChange(\"INSERT INTO Cities (Name, Population, i?, i?) VALUES (?, ?, ?, ?)\", withArgs: [columnName1, columnName2, name, population, isWarm, foundedIn]) {\n    //there was an error during the insert, handle it here\n} else {\n    //no error, the row was inserted successfully\n}\n```\n\nThe 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.\nYou may escape an identifier string yourself using the function:\n```swift\nlet escIdentifier = SD.escapeIdentifier(identifier)\n```\nObjects provided to bind as identifiers must be of type String.\n\n\n====================\n###Execute A Query\n\nNow that our table has some data, we can query it:\n\n```swift\nlet (resultSet, err) = SD.executeQuery(\"SELECT * FROM Cities\")\nif err != nil {\n    //there was an error during the query, handle it here\n} else {\n    for row in resultSet {\n        if let name = row[\"Name\"]?.asString() {\n            println(\"The City name is: \\(name)\")\n        }\n        if let population = row[\"Population\"]?.asInt() {\n            println(\"The population is: \\(population)\")\n        }\n        if let isWarm = row[\"IsWarm\"]?.asBool() {\n            if isWarm {\n                println(\"The city is warm\")\n            } else {\n                println(\"The city is cold\")\n            }\n        }\n        if let foundedIn = row[\"FoundedIn\"]?.asDate() {\n            println(\"The city was founded in: \\(foundedIn)\")\n        }\n    }\n}\n```\n\nA query function returns a tuple of:\n\n- the result set as an Array of SDRow objects\n- the error code as an Optional Int\n\nAn SDRow contains a number of corresponding SDColumn objects.\nThe values for each column can be obtained by using the column name in subscript format, much like a Dictionary.\nIn order to obtain the column value in the correct data type, you may use the convenience functions:\n\n- asString()\n- asInt()\n- asDouble()\n- asBool()\n- asDate()\n- asData()\n- asAnyObject()\n- asUIImage()\n\nIf one of the above functions is not used, the value will be an SDColumn object.\n\nFor example, if you want the string value for the column \"Name\":\n\n```swift\nif let name = row[\"Name\"]?.asString() {\n    //the value for column \"Name\" exists as a String\n} else\n    //the value is nil, cannot be cast as a String, or the column requested does not exist\n}\n```\n\nYou may also execute a query using object binding, similar to the row insert example in an earlier section:\n\n```swift\nlet (resultSet, err) = SD.executeQuery(\"SELECT * FROM Cities WHERE Name = ?\", withArgs: [\"Toronto\"])\nif err != nil {\n    //there was an error during the query, handle it here\n} else {\n    for row in resultSet {\n        if let name = row[\"Name\"]?.asString() {\n            println(\"The City name is: \\(name)\") //should be \"Toronto\"\n        }\n        if let population = row[\"Population\"]?.asInt() {\n            println(\"The population is: \\(population)\")\n        }\n        if let isWarm = row[\"IsWarm\"]?.asBool() {\n            if isWarm {\n                println(\"The city is warm\")\n            } else {\n                println(\"The city is cold\")\n            }\n        }\n        if let foundedIn = row[\"FoundedIn\"]?.asDate() {\n            println(\"The city was founded in: \\(foundedIn)\")\n        }\n    }\n}\n```\n\nThe same binding rules apply as described in the 'Execute a Change' section.\n\n\n=================\n###Error Handling\n\nYou have probably noticed that almost all SwiftData functions return an 'error' value.\n\nThis error value is an Optional Int corresponding to the appropriate error message, which can be obtained by calling the function:\n\n```swift\nlet errMsg = SD.errorMessageForCode(err)\n```\n\nIt 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.\n\nBy default, error and warning messages are printed to the console when they are encountered.\n\n\n=================\n###Creating An Index\n\nTo create an index, you may use the provided convenience function:\n\n```swift\nif let err = SD.createIndex(\"NameIndex\", onColumns: [\"Name\"], inTable: \"Cities\", isUnique: true) {\n    //there was an error creating the index, handle it here\n} else {\n    //the index was created successfully\n}\n```\n\nSimilar convenience functions are provided for:\n- removing an index:\n```swift\nlet err = removeIndex(\"IndexName\")\n```\n- finding all existing indexes:\n```swift\nlet (indexes, err) = existingIndexes()\n```\n- finding all indexes for a specified table:\n```swift\nlet (indexes, err) = existingIndexesForTable(\"TableName\")\n```\n\n\n=================\n###Custom Connection\n\nYou may create a custom connection to the database and execute a number of functions within a provided closure.\nAn example of this can be seen below:\n\n```swift\nlet task: ()-\u003eVoid = {\n    if let err = SD.executeChange(\"INSERT INTO Cities VALUES ('Vancouver', 603502, 1, '1886-04-06')\") {\n        println(\"Error inserting city\")\n    }\n    if let err = SD.createIndex(name: \"NameIndex\", onColumns: [\"Name\"], inTable: \"Cities\", isUnique: true) {\n        println(\"Index creation failed\")\n    }\n}\n\nif let err = SD.executeWithConnection(.ReadWrite, task) {\n    //there was an error opening or closing the custom connection\n} else {\n    //no error, the closure was executed\n}\n```\n\nThe available custom connection flags are:\n\n- .ReadOnly (SQLITE_OPEN_READONLY)\n- .ReadWrite (SQLITE_OPEN_READWRITE)\n- .ReadWriteCreate (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)\n\nAll operations that occur within the provided closure are executed on the single custom connection.\n\nFor more information, see the SQLite documentation for [opening a new database connection](http://www.sqlite.org/c3ref/open.html).\n\n\n=================\n###Transactions and Savepoints\n\nIf we wanted to execute the above closure `task: ()-\u003eVoid` inside an exclusive transaction, it could be done like so:\n\n```swift\nif let err = transaction(task) {\n    //there was an error starting, closing, committing, or rolling back the transaction as per the error code\n} else {\n    //the transaction was executed without errors\n}\n```\n\nSimilarly, a savepoint could be executed like so:\n\n```swift\nif let err = savepoint(task) {\n    //there was an error starting, closing, releasing, or rolling back the savepoint as per the error code\n} else {\n    //the savepoint was executed without errors\n}\n```\n\nIt should be noted that transactions *cannot* be embedded into another transaction or savepoint.\nUnlike transactions, savepoints may be embedded into other savepoints or transactions.\n\nFor more information, see the SQLite documentation for [transactions](http://sqlite.org/lang_transaction.html) and [savepoints](http://www.sqlite.org/lang_savepoint.html).\n\n\n=================\n###Using UIImages\n\nConvenience functions are provided for working with UIImages.\n\nTo easily save a UIImage to disk and insert the corresponding ID into the database:\n\n```swift\nlet image = UIImage(named:\"SampleImage\")\nif let imageID = SD.saveUIImage(image) {\n    if let err = SD.executeChange(\"INSERT INTO SampleImageTable (Name, Image) VALUES (?, ?)\", withArgs: [\"SampleImageName\", imageID]) {\n        //there was an error inserting the new row, handle it here\n    }\n} else {\n    //there was an error saving the image to disk\n}\n```\n\nAlternatively, object binding can also be used:\n\n```swift\nlet image = UIImage(named:\"SampleImage\")\nif let err = SD.executeChange(\"INSERT INTO SampleImageTable (Name, Image) VALUES (?, ?)\", withArgs: [\"SampleImageName\", image]) {\n    //there was an error inserting the new row, handle it here\n} else {\n    //the image was saved to disk, and the ID was inserted into the database as a String\n}\n```\n\nIn the examples above, a UIImage is saved to disk and the returned ID is inserted into the database as a String.\nIn order to easily obtain the UIImage from the database, the function '.asUIImage()' called on an SDColumn object may be used:\n\n```swift\nlet (resultSet, err) = SD.executeQuery(\"SELECT * FROM SampleImageTable\")\nif err != nil {\n    //there was an error with the query, handle it here\n} else {\n    for row in resultSet {\n        if let image = row[\"Image\"]?.asUIImage() {\n            //'image' contains the UIImage with the ID stored in this column\n        } else {\n            //the ID is invalid, or the image could not be initialized from the data at the specified path\n        }\n    }\n}\n```\n\nThe '.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).\n\nIf you would like to delete the photo, you may call the function:\n\n```swift\nif SD.deleteUIImageWithID(imageID) {\n    //image successfully deleted\n} else {\n    //there was an error deleting the image with the specified ID\n}\n```\n\nThis function should be called to delete the image with the specified ID from disk *before* the row containing the image ID is removed.\nRemoving the row containing the image ID from the database does not delete the image stored on disk.\n\n\n=================\n###Thread Safety\n\nAll SwiftData operations are placed on a custom serial queue and executed in a FIFO order.\n\nThis means that you can access the SQLite database from multiple threads without the need to worry about causing errors.\n\n\n##API Documentation\n\nFull API Documentation can be found [here](http://ryanfowler.github.io/SwiftData)\n\n\n##Contact\n\nPlease open an issue for any comments, concerns, ideas, or potential pull requests - all welcome :)\n\n\n##License\n\nSwiftData is released under the MIT License.\n\nSee the LICENSE file for full details.\n","funding_links":[],"categories":["Databases","Libs","Database","Swift"],"sub_categories":["Data Management"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fryanfowler%2FSwiftData","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fryanfowler%2FSwiftData","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fryanfowler%2FSwiftData/lists"}