Skip to content

Instantly share code, notes, and snippets.

@victorferreira
Last active October 27, 2020 06:52
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save victorferreira/98254255aa7728a81ab69588c78685a7 to your computer and use it in GitHub Desktop.
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
//: 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)")
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)
@deepaMacno
Copy link

Thank you very much

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment