2

I've created an app using Swift3 and Xcode8, and using FMDB as my database, when it runs on the simulator it can get the data from data.db,but when it runs on the generic device (which is my phone), there's no data in the tableView, also could't insert records. I added data.db into my project, but when I changed records on simulator, records in data.db didn't change, but I printed the path, it pointed to simulator folder, database in that folder will changed along the modify in simulator and that folder's path changes almost every time. I'm so confused, is that because I didn't connected to my database in fact?

Here's the Utility.Swift which holds common and often reused function

import UIKit

class Utility: NSObject {

class func getPath(_ fileName: String) -> String {
    let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    let fileURL = documentsURL.appendingPathComponent(fileName)
    print(fileURL.path)
    return fileURL.path
    }

class func copyFile(_ fileName: NSString){

    let dbPath: String = getPath(fileName as String)
    let fileManager = FileManager.default

    if !fileManager.fileExists(atPath: dbPath) {
        let documentsURL = Bundle.main.resourceURL
        let fromPath = documentsURL!.appendingPathComponent(fileName as String)

        var error : NSError?
        do {
            try fileManager.copyItem(atPath: fromPath.path, toPath: dbPath)
        }
        catch let error1 as NSError {
            error = error1
        }
        let alert: UIAlertView = UIAlertView()
        if (error != nil) {
            alert.title = "Error Occured"
            alert.message = error?.localizedDescription
        }
        else {
            alert.title = "Successfully Copy"
            alert.message = "Your database copy successfully"
        }
        alert.delegate = nil
        alert.addButton(withTitle: "Ok")
        alert.show()
        }
    }

class func invokeAlertMethod(_ strTitle: NSString, strBody: NSString, delegate: AnyObject?) {
    let alert: UIAlertView = UIAlertView()
    alert.message = strBody as String
    alert.title = strTitle as String
    alert.delegate = delegate
    alert.addButton(withTitle: "Ok")
    alert.show()
    }  
}

and StudentDataBase.swift contains Query languages

import UIKit
let sharedInstance = StudentDataBase()

class StudentDataBase : NSObject {

var database: FMDatabase? = nil

class func getInstance() -> StudentDataBase{

    if((sharedInstance.database) == nil)
    {
        sharedInstance.database = FMDatabase(path: Utility.getPath("data.db"))
    }
    return sharedInstance
}


func addStuData(_ student: Student) -> Bool{

    sharedInstance.database!.open()

    let isInserted = sharedInstance.database!.executeUpdate("INSERT INTO [Student info] (StudentID, FirstName, LastName, PhoneNumber) VALUES (?, ?, ?, ?)", withArgumentsIn: [student.studentID, student.fstName, student.lstName, student.phoneNum])

    sharedInstance.database!.close()
    return isInserted
}


func updateStuData(_ student: Student) -> Bool {

    sharedInstance.database!.open()

    let isUpdated = sharedInstance.database!.executeUpdate("UPDATE [Student info] SET FirstName=?, LastName=?, PhoneNumber=? WHERE StudentID=?", withArgumentsIn: [student.fstName, student.lstName, student.phoneNum, student.studentID])
    print(student)
    print(isUpdated)
    sharedInstance.database!.close()
    return isUpdated
}


func deleteStuData(_ student: Student) -> Bool {

    sharedInstance.database!.open()

    let isDeleted = sharedInstance.database!.executeUpdate("DELETE FROM [Student info] WHERE StudentID=?", withArgumentsIn: [student.studentID])

    sharedInstance.database!.close()
    return isDeleted
}

func getAllStuData() -> [Student] {

    sharedInstance.database!.open()

    let resultSet: FMResultSet! = sharedInstance.database!.executeQuery("SELECT * FROM [Student info]", withArgumentsIn: nil)
    var marrStudentInfo : [Student] = []
    if (resultSet != nil) {
        while resultSet.next() {
            let student : Student = Student()
            student.studentID = resultSet.string(forColumn: "StudentID")
            student.fstName = resultSet.string(forColumn: "FirstName")
            student.lstName = resultSet.string(forColumn: "LastName")
            student.phoneNum = resultSet.string(forColumn: "PhoneNumber")
            marrStudentInfo.append(student)
        }
    }
    sharedInstance.database!.close()
    return marrStudentInfo
}
}

also in AppDelegate.swift, I've written:

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    Utility.copyFile("data.db")
    return true
}

I'm new to Swift and FMDB, please explain and I'll try my best.Thank you very much!!!!

Edit: After I downloaded the contents in my phone and the database is blank. And after I throwaway the error, it shows

enter image description here

Inserted failed:Optional("no such table: Student info")
Rob
  • 415,655
  • 72
  • 787
  • 1,044
Grace Yang
  • 37
  • 8

1 Answers1

0

SQLite offers good error reporting and you should avail yourself of it. So, for example, if any of these executeUpdate calls fail, you should examine the lastErrorMessage (before you close the database) so you know why it failed.

Personally, I'd suggest using executeQuery(_:values:) and executeUpdate(_:values:) rather than executeQuery(_:withArgumentsIn:) and executeUpdate(_:withArgumentsIn:) because the former methods throw errors (which shifts to a more active error handling paradigm from the more passive one in which you have to manually remember to check for errors). But regardless of how you do your error handling, do it, or else we're all guessing.

Personally, I'd suggest using Xcode's "Devices" window and download your app's bundle (see https://stackoverflow.com/a/38064225/1271826) and look at the database. I bet it's a blank database with no tables defined at all. In terms of why, it could be because you did some earlier testing and there's an old version of the database in the Documents folder. Try completely deleting the app from the device and re-running and see if that fixes it.

Other, more obscure sources of problem include filename capitalization (e.g. Data.db vs data.db). The macOS file system will often handle that gracefully because it's not (generally) case sensitive. But iOS devices are case sensitive.

But, like I said, we're flying blind until (a) you add some error handling to your code so you can see why its failing; and (b) look at the database on the device to see what it looks like.

Community
  • 1
  • 1
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • I added my `data.db` into my project, but even if I deleted it, it can still work, so I was wondering if it's about the global instance sharedInstance? I was so confused. I edited my question and could you please be more specific how to throw the error? I'm really new I tried to changed `withArgumentsIn` into `values` but it went error.... – Grace Yang Mar 07 '17 at 20:07
  • Yes, the purpose in using the `values` permutation to get an error, so you can see precisely why it wasn't working. You tell us it "went error", but don't tell us what the error was. How can we help if we don't know what the error was. Unfortunately, the error log you shared with us is not terribly illuminating. What might need to focus on first is (a) the errors that `copyItem` generated; and (b) what SQLite errors FMDB reported. – Rob Mar 07 '17 at 22:07
  • Yes, thank you! and I've throw the error through lastErrorMessage, and it shows like the picture I edit, couldn't find the table. What can I do next? – Grace Yang Mar 08 '17 at 22:08
  • You don't yet have sufficient reputation to do that. I think you need something like 40 rep points... – Rob Mar 08 '17 at 22:38
  • So your error is telling you that the table doesn't exist. So, assuming that the database in your bundle does have that table, that means that either it wasn't successfully copied to your bundle, or your programmatic attempt to copy it failed (or didn't take place). So, first, stop your app immediately if the copy failed. Second, take a look at the app bundle and make sure the real database is there and has the tables you'd expect. Third, single step through the app's copy process and find out why the copy isn't working. – Rob Mar 08 '17 at 22:39
  • Bottom line, are you 100% sure the database was included in the bundle? – Rob Mar 08 '17 at 22:41
  • And remember, now that it's failed and created the blank database, you have to delete the app entirely from the device before trying again (because it's going to find that blank database and skip the copy steps). – Rob Mar 08 '17 at 22:42
  • First, I've deleted app in my phone. Second, the database is there. I think maybe I should use code to add my database in instead of just add them using `Add files to Test1.3`? yes, I did put `Data.db` in Test1.3 folder – Grace Yang Mar 08 '17 at 22:50
  • and I've changed `data.db` into `Data.db` – Grace Yang Mar 08 '17 at 22:51
  • I've went through the copyFile steps, and printed all the path out but seems like they are all match.. `documentsURL` is `file:///var/mobile/Containers/Data/Application/A4632105-B2F6-4FB4-859A-5573C0039867/Documents/` and the `fileURL.path` is `/var/mobile/Containers/Data/Application/A4632105-B2F6-4FB4-859A-5573C0039867/Documents/Data.db`. But I don't know where to find the var folder – Grace Yang Mar 08 '17 at 23:28
  • That folder is the one you download as part of the Devices window in Xcode. – Rob Mar 08 '17 at 23:47
  • So did this means I copied file successfully? I really don't think so but don't know how to solve – Grace Yang Mar 09 '17 at 00:07
  • If you stepped over `copyItem` and it didn't throw an error, that means that it copied successfully. So, if you download the container via Devices window in Xcode, you can confirm. – Rob Mar 09 '17 at 00:33
  • it shows `XPC connection interrupted`, and `data.db` in my device content is empty, without table – Grace Yang Mar 09 '17 at 00:48