Last active
July 29, 2024 03:01
-
-
Save victorferreira/98254255aa7728a81ab69588c78685a7 to your computer and use it in GitHub Desktop.
Updating SQLite Tutorial code to Swift 3. See the tutorial at: https://www.raywenderlich.com/123579/sqlite-tutorial-swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//: Back to [The C API](@previous) | |
import SQLite | |
import Foundation | |
import PlaygroundSupport | |
destroyPart2Database() | |
//: # Making it Swift | |
//: ## Errors | |
enum SQLiteError: Error { | |
case OpenDatabase(message: String) | |
case Prepare(message: String) | |
case Step(message: String) | |
case Bind(message: String) | |
} | |
//: ## The Database Connection | |
class SQLiteDatabase { | |
private let dbPointer: OpaquePointer? | |
private init(dbPointer: OpaquePointer) { | |
self.dbPointer = dbPointer | |
} | |
static func open(path: String) throws -> SQLiteDatabase { | |
var db: OpaquePointer? = nil | |
if sqlite3_open(path, &db) == SQLITE_OK { | |
return SQLiteDatabase(dbPointer: db!) | |
} else { | |
defer { | |
if db != nil { | |
sqlite3_close(db) | |
} | |
} | |
if let message: String? = String(cString: sqlite3_errmsg(db)!) { | |
throw SQLiteError.OpenDatabase(message: message!) | |
} else { | |
throw SQLiteError.OpenDatabase(message: "No error message provided from sqlite.") | |
} | |
} | |
} | |
var errorMessage: String { | |
if let errorMessage: String? = String(cString: sqlite3_errmsg(dbPointer)!) { | |
return errorMessage! | |
} else { | |
return "No error message provided from sqlite." | |
} | |
} | |
func prepareStatement(sql: String) throws -> OpaquePointer { | |
var statement: OpaquePointer? = nil | |
guard sqlite3_prepare_v2(dbPointer, sql, -1, &statement, nil) == SQLITE_OK else { | |
throw SQLiteError.Prepare(message: errorMessage) | |
} | |
return statement! | |
} | |
func createTable(table: SQLTable.Type) throws { | |
// 1 | |
let createTableStatement = try prepareStatement(sql: table.createStatement) | |
// 2 | |
defer { | |
sqlite3_finalize(createTableStatement) | |
} | |
// 3 | |
guard sqlite3_step(createTableStatement) == SQLITE_DONE else { | |
throw SQLiteError.Step(message: errorMessage) | |
} | |
print("\(table) table created.") | |
} | |
func insertContact(contact: Contact) throws { | |
let insertSql = "INSERT INTO Contact (Id, Name) VALUES (?, ?);" | |
let insertStatement = try prepareStatement(sql: insertSql) | |
defer { | |
sqlite3_finalize(insertStatement) | |
} | |
let name: NSString = contact.name as NSString | |
guard sqlite3_bind_int(insertStatement, 1, contact.id) == SQLITE_OK && | |
sqlite3_bind_text(insertStatement, 2, name.utf8String, -1, nil) == SQLITE_OK else { | |
throw SQLiteError.Bind(message: errorMessage) | |
} | |
guard sqlite3_step(insertStatement) == SQLITE_DONE else { | |
throw SQLiteError.Step(message: errorMessage) | |
} | |
print("Successfully inserted row.") | |
} | |
deinit { | |
sqlite3_close(dbPointer) | |
} | |
} | |
let db: SQLiteDatabase | |
do { | |
db = try SQLiteDatabase.open(path: part2DbPath) | |
print("Successfully opened connection to database.") | |
} catch SQLiteError.OpenDatabase(let message) { | |
print("Unable to open database. Verify that you created the directory described in the Getting Started section.") | |
PlaygroundPage.current.finishExecution() | |
} | |
//: ## Preparing Statements | |
// extension dind't work so I moved it inside the class. | |
//: ## Create Table | |
protocol SQLTable { | |
static var createStatement: String { get } | |
} | |
extension Contact: SQLTable { | |
static var createStatement: String { | |
return "CREATE TABLE Contact(" + | |
"Id INT PRIMARY KEY NOT NULL," + | |
"Name CHAR(255)" + | |
");" | |
} | |
} | |
do { | |
try db.createTable(table: Contact.self) | |
try db.insertContact(contact: Contact(id: 1, name: "Ray")) | |
} catch { | |
print(db.errorMessage) | |
} | |
//: ## Insert Row | |
//: ## Read | |
extension SQLiteDatabase { | |
func contact(id: Int32) -> Contact? { | |
let querySql = "SELECT * FROM Contact WHERE Id = ?;" | |
guard let queryStatement = try? prepareStatement(sql: querySql) else { | |
return nil | |
} | |
defer { | |
sqlite3_finalize(queryStatement) | |
} | |
guard sqlite3_bind_int(queryStatement, 1, id) == SQLITE_OK else { | |
return nil | |
} | |
guard sqlite3_step(queryStatement) == SQLITE_ROW else { | |
return nil | |
} | |
let id = sqlite3_column_int(queryStatement, 0) | |
let queryResultCol1 = sqlite3_column_text(queryStatement, 1) | |
let name = String(cString: queryResultCol1!) | |
return Contact(id: id, name: name) | |
} | |
} | |
let first = db.contact(id: 1) | |
print("\(first)") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SQLite | |
import Foundation | |
import PlaygroundSupport | |
destroyPart1Database() | |
/*: | |
# Getting Started | |
The first thing to do is set your playground to run manually rather than automatically. This will help ensure that your SQL commands run when you intend them to. At the bottom of the playground click and hold the Play button until the dropdown menu appears. Choose "Manually Run". | |
You will also notice a `destroyPart1Database()` call at the top of this page. You can safely ignore this, the database file used is destroyed each time the playground is run to ensure all statements execute successfully as you iterate through the tutorial. | |
Secondly, this Playground will need to write SQLite database files to your file system. Create the directory `~/Documents/Shared Playground Data/SQLiteTutorial` by running the following command in Terminal. | |
`mkdir -p ~/Documents/Shared\ Playground\ Data/SQLiteTutorial` | |
*/ | |
func openDatabase() -> OpaquePointer { | |
var db: OpaquePointer? = nil | |
if sqlite3_open(part1DbPath, &db) == SQLITE_OK { | |
print("Successfully opened connection to database at \(part1DbPath)") | |
return db! | |
} else { | |
print("Unable to open database. Verify that you created the directory described " + | |
"in the Getting Started section.") | |
PlaygroundPage.current.finishExecution() | |
} | |
} | |
//: ## Open a Connection | |
let db = openDatabase() | |
//: ## Create a Table | |
let createTableString = "CREATE TABLE Contact(" + | |
"Id INT PRIMARY KEY NOT NULL," + | |
"Name CHAR(255));" | |
func createTable() { | |
// 1 | |
var createTableStatement: OpaquePointer? = nil | |
// 2 | |
if sqlite3_prepare_v2(db, createTableString, -1, &createTableStatement, nil) == SQLITE_OK { | |
// 3 | |
if sqlite3_step(createTableStatement) == SQLITE_DONE { | |
print("Contact table created.") | |
} else { | |
print("Contact table could not be created.") | |
} | |
} else { | |
print("CREATE TABLE statement could not be prepared.") | |
} | |
// 4 | |
sqlite3_finalize(createTableStatement) | |
} | |
createTable() | |
//: ## Insert a Contact | |
let insertStatementString = "INSERT INTO Contact (Id, Name) VALUES (?, ?);" | |
// func insert() { | |
// var insertStatement: OpaquePointer? = nil | |
// // 1 | |
// if sqlite3_prepare_v2(db, insertStatementString, -1, &insertStatement, nil) == SQLITE_OK { | |
// let id: Int32 = 1 | |
// let name: NSString = "Ray" | |
// // 2 | |
// sqlite3_bind_int(insertStatement, 1, id) | |
// // 3 | |
// sqlite3_bind_text(insertStatement, 2, name.utf8String, -1, nil) | |
// // 4 | |
// if sqlite3_step(insertStatement) == SQLITE_DONE { | |
// print("Successfully inserted row.") | |
// } else { | |
// print("Could not insert row.") | |
// } | |
// } else { | |
// print("INSERT statement could not be prepared.") | |
// } | |
// // 5 | |
// sqlite3_finalize(insertStatement) | |
// } | |
func insert() { | |
var insertStatement: OpaquePointer? = nil | |
let names: [NSString] = ["Ray", "Chris", "Martha", "Danielle"] | |
if sqlite3_prepare_v2(db, insertStatementString, -1, &insertStatement, nil) == SQLITE_OK { | |
for (index, name) in names.enumerated(){ | |
let id = Int32(index + 1) | |
sqlite3_bind_int(insertStatement, 1, id) | |
sqlite3_bind_text(insertStatement, 2, name.utf8String, -1, nil) | |
if sqlite3_step(insertStatement) == SQLITE_DONE { | |
print("Successfully inserted row") | |
} else { | |
print("Could not insert row") | |
} | |
sqlite3_reset(insertStatement) | |
} | |
sqlite3_finalize(insertStatement) | |
} else { | |
print("INSERT statement could not be prepared.") | |
} | |
} | |
insert() | |
//: ## Querying | |
let queryStatementString = "SELECT * FROM Contact;" | |
// func query() { | |
// var queryStatement: OpaquePointer? = nil | |
// // 1 | |
// if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK { | |
// // 2 | |
// if sqlite3_step(queryStatement) == SQLITE_ROW { | |
// // 3 | |
// let id = sqlite3_column_int(queryStatement, 0) | |
// // 4 | |
// let queryResultCol1 = sqlite3_column_text(queryStatement, 1) | |
// let name = String(cString: queryResultCol1!) | |
// // 5 | |
// print("Query Result:") | |
// print("\(id) | \(name)") | |
// } else { | |
// print("Query returned no results") | |
// } | |
// } else { | |
// print("SELECT statement could not be prepared") | |
// } | |
// // 6 | |
// sqlite3_finalize(queryStatement) | |
// } | |
func query() { | |
var queryStatement: OpaquePointer? = nil | |
// 1 | |
if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK { | |
// 2 | |
while (sqlite3_step(queryStatement) == SQLITE_ROW) { | |
let id = sqlite3_column_int(queryStatement, 0) | |
let queryResultCol1 = sqlite3_column_text(queryStatement, 1) | |
let name = String(cString: queryResultCol1!) | |
print("Query Result:") | |
print("\(id) | \(name)") | |
} | |
} else { | |
print("SELECT statement could not be prepared") | |
} | |
// 6 | |
sqlite3_finalize(queryStatement) | |
} | |
query() | |
//: ## Update | |
let updateStatementString = "UPDATE Contact SET Name = 'Chris' WHERE Id = 1;" | |
func update() { | |
var updateStatement: OpaquePointer? = nil | |
if sqlite3_prepare_v2(db, updateStatementString, -1, &updateStatement, nil) == SQLITE_OK { | |
if sqlite3_step(updateStatement) == SQLITE_DONE { | |
print("Successfully updated row.") | |
} else { | |
print("Could not update row.") | |
} | |
} else { | |
print("UPDATE statement could not be prepared") | |
} | |
sqlite3_finalize(updateStatement) | |
} | |
update() | |
query() | |
//: ## Delete | |
let deleteStatementStirng = "DELETE FROM Contact WHERE Id = 1;" | |
func delete() { | |
var deleteStatement: OpaquePointer? = nil | |
if sqlite3_prepare_v2(db, deleteStatementStirng, -1, &deleteStatement, nil) == SQLITE_OK { | |
if sqlite3_step(deleteStatement) == SQLITE_DONE { | |
print("Successfully deleted row.") | |
} else { | |
print("Could not delete row.") | |
} | |
} else { | |
print("DELETE statement could not be prepared") | |
} | |
sqlite3_finalize(deleteStatement) | |
} | |
delete() | |
query() | |
//: ## Errors | |
let malformedQueryString = "SELECT Stuff from Things WHERE Whatever;" | |
func prepareMalformedQuery() { | |
var malformedStatement: OpaquePointer? = nil | |
// 1 | |
if sqlite3_prepare_v2(db, malformedQueryString, -1, &malformedStatement, nil) == SQLITE_OK { | |
print("This should not have happened.") | |
} else { | |
// 2 | |
let errorMessage = String(cString: sqlite3_errmsg(db)!) | |
print("Query could not be prepared! \(errorMessage)") | |
} | |
// 3 | |
sqlite3_finalize(malformedStatement) | |
} | |
prepareMalformedQuery() | |
//: ## Close the database connection | |
sqlite3_close(db) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you very much