简体   繁体   English

iPhone 作为 Xcode 模拟器不从 sqlite 数据库读取

[英]iPhone as Xcode simulator doesn't read from sqlite database

I have coded an app that utilizes the sqlite database that ships with Xcode.我编写了一个使用 Xcode 附带的 sqlite 数据库的应用程序。 It works well on the mac but when I select iPhone as simulator the app (on iPhone) doesn't read database data.它在 mac 上运行良好,但是当我选择 iPhone 作为模拟器时,应用程序(在 iPhone 上)不会读取数据库数据。 Do I need to code access to the sqlite database differently from how it accesses it on the mac?我是否需要对 sqlite 数据库的访问与在 mac 上访问它的方式不同?

Here is my function to get data from a database that is used in a picker view.这是我从选择器视图中使用的数据库中获取数据的函数。 This picker view does not populate when I use an iPhone (linked to the computer via usb).当我使用 iPhone(通过 USB 链接到计算机)时,此选择器视图不会填充。 However it populates when I run any of the other listed simulators但是,当我运行任何其他列出的模拟器时它会填充

struct Gender {
    let gender:String
}

var genderCode = [Gender]()

func readGenderCode(){
    //database setup
    let fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) .appendingPathComponent("OTC.sqlite")
    //opening the OTC database
    if sqlite3_open(fileURL.path, &db) != SQLITE_OK {
        print("error opening database")
    }

    //get data by query
    let queryString = "SELECT gender from gender"

    //statement pointer
    var stmt2:OpaquePointer?

    //preparing the query
    if sqlite3_prepare(db, queryString, -1, &stmt2, nil) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error preparing read: \(errmsg)")
        return
    }

    //go through gender table records
    while(sqlite3_step(stmt2) == SQLITE_ROW)  {
        let gc = String(cString: sqlite3_column_text(stmt2,0))

        //populate the array
        genderCode.append(Gender(gender: String(describing: gc)))
    }

}

This behavior, where you're not seeing the data in the database, generally just means that it is empty.这种行为,您没有看到数据库中的数据,通常只是意味着它是空的。

So, first, confirm this thesis.所以,首先,确认这个论点。 Download the container from the physical device (see https://stackoverflow.com/a/38064225/1271826 ) and open up the database found in this container from macOS and examine what it contains.从物理设备下载容器(参见https://stackoverflow.com/a/38064225/1271826 )并从 macOS 打开此容器中的数据库并检查其中包含的内容。 I'm wagering that the database or the table is empty.我打赌数据库或表是空的。

Assuming this is indeed the problem, it begs the question as to how you ended up with a blank database on that device rather than a copy of the database within your app bundle.假设这确实是问题所在,它引出了一个问题,即您如何在该设备上得到一个空白数据库,而不是应用程序包中的数据库副本。 Most likely, at some point during development and testing on the physical device, the app accidentally opened a database in the documents folder without first successfully copying the bundle version.最有可能的是,在物理设备上的开发和测试过程中,应用程序在没有首先成功复制捆绑版本的情况下意外打开了文档文件夹中的数据库。 Unfortunately, the default behavior of sqlite3_open is to create a blank database if one is not found.不幸的是, sqlite3_open的默认行为是在找不到数据库时创建一个空白数据库。 So, you want to (a) remove that blank database from your device;因此,您想 (a) 从您的设备中删除该空白数据库; and (b) write code that prevents this from being able to happen in the future. (b) 编写代码以防止将来发生这种情况。

I would therefore suggest:因此,我建议:

  1. Remove your app from the device in question.从相关设备中删除您的应用。 This will remove any blank databases created during this development/testing process.这将删除在此开发/测试过程中创建的任何空白数据库。

  2. If you are distributing app with prepopulated database, remove all references to sqlite3_open and replace them with sqlite3_open_v2 , using only the SQLITE_OPEN_READWRITE option (making sure that the app cannot possibly create a blank database for you).如果您使用预填充的数据库分发应用程序,请删除对sqlite3_open所有引用并将它们替换为sqlite3_open_v2 ,仅使用SQLITE_OPEN_READWRITE选项(确保应用程序不可能为您创建空白数据库)。 Specifically, do not use the SQLITE_OPEN_CREATE option.特别是,不要使用SQLITE_OPEN_CREATE选项。

  3. Revisit your open routine.重新审视你的open例程。 Eg you might do something like:例如,您可能会执行以下操作:

     func open() -> Bool { let fileUrl = try! FileManager.default .url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true) .appendingPathComponent(databaseName) if sqlite3_open_v2(fileUrl.path, &db, SQLITE_OPEN_READWRITE, nil) == SQLITE_OK { return true } close() // even though open failed, you still need to close it guard let bundleURL = Bundle.main.url(forResource: databaseName, withExtension: nil) else { print("not found in bundle") return false } try? FileManager.default.copyItem(at: bundleURL, to: fileUrl) guard sqlite3_open_v2(fileUrl.path, &db, SQLITE_OPEN_READWRITE, nil) == SQLITE_OK else { let error = sqlite3_errmsg(db).flatMap { String(cString: $0, encoding: .utf8) } print(error ?? "unknown error") close() return false } return true } func close() { sqlite3_close(db) db = nil }

    There are a few things that I'd like to draw your attention to in the above:我想提请您注意以上几点:

    • I would suggest you use the application support directory rather than the documents folder.我建议您使用应用程序支持目录而不是文档文件夹。 See iOS Standard Directories: Where Files Reside or watch iOS Storage Best Practices video.请参阅iOS 标准目录:文件所在的位置或观看iOS 存储最佳实践视频。

    • Just try to open database in the application support folder.只需尝试在应用程序支持文件夹中打开数据库。 If it fails, try copying from bundle to application support directory and try again.如果失败,请尝试从包复制到应用程序支持目录,然后重试。

    • By the way, if sqlite3_open_v2 (or sqlite3_open ) fails, remember that you still have to call sqlite3_close .顺便说一句,如果sqlite3_open_v2 (或sqlite3_open )失败,请记住您仍然必须调用sqlite3_close As the SQLite documentation says, “Whether or not an error occurs when it is opened, resources associated with the database connection handle should be released by passing it to sqlite3_close() when it is no longer required.”正如SQLite 文档所说,“无论打开时是否发生错误,与数据库连接句柄关联的资源都应该在不再需要时通过将其传递给sqlite3_close()来释放。”

  4. Now your readGenderCode can use this open routine:现在你的readGenderCode可以使用这个open例程:

     func readGenderCode() -> [Gender]? { guard open() else { return nil } defer { close() } let sql = "SELECT gender from gender" var statement: OpaquePointer? guard sqlite3_prepare(db, sql, -1, &statement, nil) == SQLITE_OK else { let errmsg = sqlite3_errmsg(db).flatMap { String(cString: $0) } print("error preparing read:", errmsg ?? "Unknown error") return nil } defer { sqlite3_finalize(statement) } var genders = [Gender]() //go through gender table records while sqlite3_step(statement) == SQLITE_ROW { if let cString = sqlite3_column_text(statement, 0) { let string = String(cString: cString) genders.append(Gender(gender: string)) } } return genders }

    Note:笔记:

    • If you're going to open database when running SQL, you'll want to close it when you're done.如果您要在运行 SQL 时打开数据库,那么您将希望在完成后关闭它。 Personally, I open database once and leave it open (rather than littering open calls throughout my database controller), but if you're going to constantly re-open the database for every SQL call, remember to close it, too.就个人而言,我打开数据库一次并保持打开状态(而不是在我的数据库控制器中乱扔open调用),但是如果您要为每个 SQL 调用不断地重新打开数据库,也请记住关闭它。

    • If your “prepare” succeeded, make sure to “finalize” it, too, or else you will leak.如果你的“准备”成功了,一定要“完成”它,否则你会泄漏。

    • I'd suggest avoiding force unwrapping operator, !我建议避免强制展开运算符, ! , if you can. , 如果你可以的话。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM