如何在通用 iOS 设备而不是模拟器上使用 FMDB?

Posted

技术标签:

【中文标题】如何在通用 iOS 设备而不是模拟器上使用 FMDB?【英文标题】:How to use FMDB on the generic iOS device instead of simulator? 【发布时间】:2017-03-07 16:25:00 【问题描述】:

我使用 Swift3 和 Xcode8 创建了一个应用程序,并使用 FMDB 作为我的数据库,当它在模拟器上运行时,它可以从data.db 获取数据,但是当它在通用设备(这是我的手机)上运行时),tableView中没有数据,也无法插入记录。我将data.db添加到我的项目中,但是当我在模拟器上更改记录时,data.db中的记录没有改变,但是我打印了路径,它指向模拟器文件夹,该文件夹中的数据库将随着模拟器中的修改而改变并且该文件夹的路径几乎每次都会更改。我很困惑,是不是因为我实际上没有连接到我的数据库?

这是Utility.Swift,它拥有常见且经常重用的功能

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()
      

StudentDataBase.swift 包含查询语言

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


也在AppDelegate.swift,我写过:

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

我是 Swift 和 FMDB 的新手,请解释一下,我会尽力而为。非常感谢!!!!

编辑: 在我将内容下载到手机中之后,数据库是空白的。在我丢弃错误后,它显示

Inserted failed:Optional("no such table: Student info")

【问题讨论】:

【参考方案1】:

SQLite 提供了良好的错误报告,您应该利用它。因此,例如,如果这些executeUpdate 调用中的任何一个失败,您应该检查lastErrorMessage(在关闭数据库之前),以便知道它失败的原因。

就我个人而言,我建议使用executeQuery(_:values:)executeUpdate(_:values:) 而不是executeQuery(_:withArgumentsIn:)executeUpdate(_:withArgumentsIn:),因为前一种方法会引发错误(它会从更被动的错误处理范式转变为更主动的错误处理范式)必须手动记住检查错误)。但不管你如何处理错误,都去做吧,否则我们都在猜测。

就个人而言,我建议使用 Xcode 的“设备”窗口并下载您的应用程序包(请参阅https://***.com/a/38064225/1271826)并查看数据库。我敢打赌,这是一个完全没有定义表的空白数据库。至于原因,可能是因为您进行了一些较早的测试,并且 Documents 文件夹中有旧版本的数据库。尝试从设备中完全删除应用程序并重新运行,看看是否可以解决问题。

其他更模糊的问题来源包括文件名大写(例如 Data.dbdata.db)。 macOS 文件系统通常会优雅地处理它,因为它(通常)不区分大小写。但 ios 设备区分大小写。

但是,就像我说的那样,我们一直在瞎忙,直到 (a) 您在代码中添加了一些错误处理,这样您就可以了解它失败的原因; (b) 查看设备上的数据库以了解其外观。

【讨论】:

我将data.db添加到我的项目中,但即使我删除了它,它仍然可以工作,所以我想知道它是否与全局实例sharedInstance有关?我很困惑。我编辑了我的问题,您能否更具体地说明如何抛出错误?我真的很新? 我试图将withArgumentsIn 更改为values 但它出错了...... 是的,使用values 排列来获取错误的目的,因此您可以准确地看到它为什么不起作用。您告诉我们它“出错”,但不要告诉我们错误是什么。如果我们不知道错误是什么,我们该如何提供帮助。不幸的是,您与我们分享的错误日志并不是很能说明问题。首先可能需要关注的是(a)copyItem 产生的错误; (b) FMDB 报告的 SQLite 错误。 好的,谢谢!我已经通过 lastErrorMessage 抛出了错误,它显示的就像我编辑的图片一样,找不到表格。下一步我能做什么? 你还没有足够的声望来做这件事。我认为您需要 40 个代表点... 所以您的错误是告诉您该表不存在。因此,假设您的捆绑包中的数据库确实具有该表,这意味着它没有成功复制到您的捆绑包,或者您以编程方式尝试复制它失败(或没有发生)。因此,首先,如果复制失败,请立即停止您的应用程序。其次,查看应用程序包并确保真正的数据库在那里并且具有您期望的表。第三,单步完成应用的复制过程,找出复制不起作用的原因。

以上是关于如何在通用 iOS 设备而不是模拟器上使用 FMDB?的主要内容,如果未能解决你的问题,请参考以下文章

如何在真实的 iOS 设备上打开 .app 文件?

测试 iAd 在模拟器、iPhone 设备而不是 iPad 设备上显示

iOS 通用应用程序链接在模拟器上工作,而不是在真实设备上

如何使用设备而不是模拟器

XCode 在模拟器选择中使用 GUID 而不是 iOS 版本号

ios通用应用程序只显示3.5英寸屏幕而不是4英寸屏幕