Search code examples
iosswiftsqlitesqlite.swift

Establishing a Read-Write connection to a database in SQLite.swift


I'm working a project where I need to use a SQLite database to read, create , and edit different objects. I thought I had established the connection properly but, it turns out I had only established a read only connection. How do I modify this code to be a read-write connection using SQLite.swift

import Foundation
import SQLite
import UIKit


let path = Bundle.main.path(forResource: "Assignment2", ofType: "sqlite3")
//Array of customer structs to populate the table
var customerArray: [Customer] = []


class CustomerPageVC: UIViewController, UITableViewDelegate, UITableViewDataSource {

//IBOutlets
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var addCustButton: UIButton!



override func viewDidLoad() {
    super.viewDidLoad()
    //Additional Setup
    do {
        //Search for DB in documents directory
        let db = try Connection(path!)
        let customers = Table("Customers")
        //Define the columns of the table as expressions
        let id = Expression<Int64>("CustomerID")
        let name = Expression<String>("CustomerName")
        let contactName = Expression<String>("ContactName")
        let address = Expression<String>("Address")
        let city = Expression<String>("City")
        let postalCode = Expression<String>("PostalCode")
        let country = Expression<String>("Country")
        
        //Load the data from db file into customerArray
        for customer in try db.prepare(customers) {
            let cust = Customer(Int(customer[id]), customer[name], customer[contactName], customer[address], customer[city], customer[postalCode], customer[country])
            customerArray.append(cust)
        }
    }
    catch {
        print(error)
    }
    tableView.delegate = self
    tableView.dataSource = self
} 
}

Edit there's a func copyDatabaseIfNeeded in the documentation so maybe my true question is in what context do I use this func to copy the database to the application support directory?

func copyDatabaseIfNeeded(sourcePath: String) -> Bool {
let documents = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let destinationPath = documents + "/db.sqlite3"
let exists = FileManager.default.fileExists(atPath: destinationPath)
guard !exists else { return false }
do {
    try FileManager.default.copyItem(atPath: sourcePath, toPath: destinationPath)
    return true
} catch {
  print("error during file copy: \(error)")
    return false
}
}

You can find the documentation for SQLite.swift here https://github.com/stephencelis/SQLite.swift/blob/master/Documentation/Index.md#connecting-to-a-database


Solution

  • When database created in your document directory it will be there permanently till user delete the app or you delete that directory.
    so read and write connection will occur when you save your sql file in this directory.
    as you wanted I make a new example for you that I created recently.

    import UIKit
    import SQLite
    
    class ViewController: UIViewController {
        
        let customer = Table("Customer")
        let id = Expression<Int64>("CustomerID")
        let name = Expression<String>("CustomerName")
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            let db = makeDBConnection()
            createTable(db: db)
            insertNewCustomer(db: db)
            fetchDatabase(db: db)
        }
        
        private func makeDBConnection() -> Connection {
            let path = NSSearchPathForDirectoriesInDomains(
                .documentDirectory, .userDomainMask, true
            ).first!
            
            let sourcePath = "\(path)/db.sqlite3"
            
            _ = copyDatabaseIfNeeded(sourcePath: sourcePath)
    
            return try! Connection(sourcePath)
        }
        
        private func createTable(db: Connection) {
            //Define the columns of the table as expressions
            
            do {
                try db.run(customer.create(block: { table in
                    table.column(id, primaryKey: true)
                    table.column(name)
                }))
            } catch {
                // This tells you table already created for second time you running this code
            }
        }
        
        private func insertNewCustomer(db: Connection) {
            // This will insert a new customer into your table each time app runs
            let insert = customer.insert(name <- "Reza")
            try! db.run(insert)
        }
        
        private func fetchDatabase(db: Connection) {
            for customer in try! db.prepare(customer) {
                print("id: \(customer[id]), name: \(customer[name])")
            }
        }
        
        func copyDatabaseIfNeeded(sourcePath: String) -> Bool {
            let documents = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
            let destinationPath = documents + "/db.sqlite3"
            let exists = FileManager.default.fileExists(atPath: destinationPath)
            guard !exists else { return false }
            do {
                try FileManager.default.copyItem(atPath: sourcePath, toPath: destinationPath)
                return true
            } catch {
              print("error during file copy: \(error)")
                return false
            }
        }
    }
    

    the result is when I run the app each time:

    enter image description here