0

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

JonGrimes20
  • 115
  • 9
  • `Bundle.main.path(forResource: "Assignment2", ofType: "sqlite3")`, anything in the app bundle is read only so for starters you need to move your db file – Joakim Danielson Feb 21 '22 at 21:08
  • So how would I copy the file to the application support directory? – JonGrimes20 Feb 21 '22 at 21:12
  • @JonGrimes20 in their documentation they talk about how to establish read and write connection, did you run it on your code? [Document read write link](https://github.com/stephencelis/SQLite.swift/blob/master/Documentation/Index.md#read-write-databases) – Reza Khonsari Feb 21 '22 at 21:26
  • @RezaKhonsari In what context would I use that func to copy? Would I just use it in my first viewDidLoad() and not assign it to a var or what? Sorry I'm very new to using databases. – JonGrimes20 Feb 21 '22 at 21:40
  • @RezaKhonsari When I put it into my code it shows a warning that the result is never used, is that something to worry about? – JonGrimes20 Feb 21 '22 at 21:42
  • @JonGrimes20 func copyDatabaseIfNeeded(sourcePath: String) -> Bool this function just check your db is in app sandbox document directory which is stable for your db, you just make db connection to that directory and when it's in there you can create, read, edit, delete from that db. – Reza Khonsari Feb 21 '22 at 21:50
  • @JonGrimes20 you should just call for example db.run(customers.delete), db.run(customers.insert) or db.run(customers.update), and behind scene it will update your document file.sqlite3 – Reza Khonsari Feb 21 '22 at 21:53
  • if it's help just tell me to make my answer – Reza Khonsari Feb 21 '22 at 21:54
  • Could possibly provide an example using the code from above as a full answer rather than a comment please? Thank you very much! – JonGrimes20 Feb 21 '22 at 21:57
  • @RezaKhonsari sorry I forgot to tag you in my previous comment – JonGrimes20 Feb 21 '22 at 22:14
  • @JonGrimes20 ok I'll try to make answer for you – Reza Khonsari Feb 21 '22 at 22:15

1 Answers1

0

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

Reza Khonsari
  • 479
  • 3
  • 13
  • The connection is working but when I try to replicate the insert function I start to have some problems. I have another View Controller 'NewCustomerPage' that pops up with UITextFields to enter the customers name, contact name, address, city, postalCode, and country. The save button at the bottom of the page when tapped takes the text in the above textFields and uses that to run an insert command, but when I test it no new customer appears, I'll edit my question for you to look at the code. – JonGrimes20 Feb 21 '22 at 23:18
  • @JonGrimes20 I think this is another problem rather than this question, if my answer helps you to understand your question, please upvote my answer, and also please upload your codes through public git and share it in your question, so people can help and contribute on your code – Reza Khonsari Feb 21 '22 at 23:24
  • I've edited the question for you to now see the 'NewCustomerPageVC' code, because I don't know if it's actually a writeable database or not. Also I apparently don't have enough reputation to upvote – JonGrimes20 Feb 21 '22 at 23:24
  • @JonGrimes20 as I tell you, you should upload your project over git, ask people to help you. there are more edge cases in your code for example you don't have makeDBConnection in NewCustomerPage but you call it. – Reza Khonsari Feb 21 '22 at 23:32
  • @JonGrimes20 My response is related to your question about Establishing a Read-Write connection, not fixing your project codes. – Reza Khonsari Feb 21 '22 at 23:34
  • Okay thank you for the advice and being so helpful, I'm still working on learning the ropes of the programming world. – JonGrimes20 Feb 21 '22 at 23:56