Swift - 设备上不包含预填充的 SQLite 数据库

Posted

技术标签:

【中文标题】Swift - 设备上不包含预填充的 SQLite 数据库【英文标题】:Swift - Pre-populated SQLite database not included on device 【发布时间】:2015-09-17 05:00:31 【问题描述】:

我正在尝试在我的物理 iPhone 的构建中包含一个预填充的 SQLite 数据库(带有 FMDB 包装器)。但是,预填充的数据库并未包含在物理设备的构建中。

请注意,它在 ios 模拟器中运行良好,但仅适用于一个模拟器设备。

我已将数据库包含在 Build Phases > Copy Bundled Resources 中,并在 Link Binary with Libraries 下链接了 libsqlite3.dylib。

我需要添加哪些 Swift 代码才能将构建中包含的数据库添加到物理设备?

代码:

import UIKit

class ViewController: UIViewController 


    @IBOutlet weak var checkButton: UIButton!
    @IBOutlet weak var tests_scroller: UITextView!

    var databasePath = NSString()

    override func viewDidLoad() 
        super.viewDidLoad()

        checkButton.setTitle("\u2610", forState: .Normal)
        checkButton.setTitle("\u2611", forState: .Selected)


        let filemgr = NSFileManager.defaultManager()

        let dirPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)

        let docsDir = dirPaths[0] as! String

        databasePath = docsDir.stringByAppendingPathComponent("vmd_db.db")

        let myDatabase = FMDatabase(path: databasePath as String)


        if myDatabase.open()

            var arrayData:[String] = []

            let query_lab_test = "SELECT lab_test FROM lab_test ORDER BY lab_test ASC"

            let results_lab_test:FMResultSet? = myDatabase.executeQuery(query_lab_test, withArgumentsInArray: nil)

            while results_lab_test?.next() == true 

                if let resultString = results_lab_test?.stringForColumn("lab_test")

                arrayData.append(resultString)

            
        
            var multiLineString = join("\n", arrayData)
            tests_scroller.text = multiLineString
            myDatabase.close()
    
    




    override func didReceiveMemoryWarning() 
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    

更新 - 在 XCode 7 中工作 Swift 2 代码 - 使用 FMDB 包装器预填充的 SQLite 数据库复制到物理设备 OK:

import UIKit

class ViewController: UIViewController 

    @IBOutlet weak var tests_scroller: UITextView!

    var databasePath = NSString()

    override func viewDidLoad() 
        super.viewDidLoad()


        let sourcePath = NSBundle.mainBundle().pathForResource("vmd_db", ofType: "db")

        let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first as String!

        let destinationPath = (documentDirectoryPath as NSString).stringByAppendingPathComponent("vmd_db.db")

        do                                 
            try NSFileManager().copyItemAtPath(sourcePath!, toPath: destinationPath)

         catch _                 
        

        //read it
        let myDatabase = FMDatabase(path: destinationPath as String)

        if myDatabase.open()

            var arrayData:[String] = []

            let query_lab_test = "SELECT lab_test FROM lab_test ORDER BY lab_test ASC"

            let results_lab_test:FMResultSet? = myDatabase.executeQuery(query_lab_test, withArgumentsInArray: nil)

            while results_lab_test?.next() == true 

                if let resultString = results_lab_test?.stringForColumn("lab_test")

                    arrayData.append(resultString)

                
            

            let multiLineString = arrayData.joinWithSeparator("\n")

            tests_scroller.text = multiLineString
            myDatabase.close()
        
    


    override func didReceiveMemoryWarning() 
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    

【问题讨论】:

为什么我的问题被否决了??? 投反对票的人没有发表评论,所以我投反对票。 【参考方案1】:

据我所知,它不应该在任何模拟器/设备上工作,因为您访问了文档文件夹中的文件。这不在您的捆绑包中。

您需要做的是将文件从捆绑包复制到文档目录,然后再尝试打开它:

//path in documents
let dirPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let docsDir = dirPaths[0]
databasePath = docsDir.stringByAppendingPathComponent("vmd_db.db")

//if not there, copy from bundle
if !filemgr.fileExistsAtPath(databasePath) 
    let bundleDatabasePath = NSBundle.mainBundle().pathForResource("vm_db", ofType: "db")
    filemgr.copyItemAtPath(bundleDatabasePath, toPath: databasePath)


//read it
let myDatabase = FMDatabase(path: databasePath as String)

【讨论】:

@Daij-Djan...感谢您的支持! XCode希望对以下内容进行更改以包含错误位:filemgr.copyItemAtPath(bundleDatabasePath, toPath: databasePath, error:nil) 但现在我得到“NSString 不能隐式转换为'String';你的意思是'as'显式转换?” 你在 swift 1.2 和 2 之间切换——你上面的代码是 1.2 但错误是 2.0——databasePath 永远不应该是 NSString :) 也许 XCode 是问题所在...我使用的是 v6.4。 yip - 没有了,xcode 7 有 swift 2 所以是的 - 你需要在这里和那里投射 我决定将 XCode 更新到 v7 并使用 Swift 2。现在 stringByAppendingPathComponent 已从 Swift 2 中删除,并替换为产生错误“'String' 类型的值没有成员 'URLByAppendingPathComponent '" 有什么想法吗?【参考方案2】:

我遇到了类似的问题;具体来说,在将数据添加到我正在 iPhone 模拟器中开发的应用程序之后,我不想将所有这些数据再次添加到我的物理 iPhone 设备中。在查看了 IlludimPu36 和 Daij-Djan 提供的解决方案后,我想出了以下方法,我在 App Delegate 的 (AppDelegate.swift) 应用程序 (didFinishLaunchingWithOptions) 方法的第一行中调用了该方法:

func copyBundledSQLiteDB() 
    let sourcePath = NSBundle.mainBundle().pathForResource("filename", ofType: "sqlite")

    let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first as String!

    let destinationPath = (documentDirectoryPath as NSString).stringByAppendingPathComponent("filename.sqlite")

    // If file does not exist in the Documents directory already,
    // then copy it from the bundle.
    if !NSFileManager().fileExistsAtPath(destinationPath) 
        do 
            try NSFileManager().copyItemAtPath(sourcePath!, toPath: destinationPath)

         catch _ 
        
    

它似乎可以工作,并且在调试器中单步执行方法的每一行看起来都没有错误,无论是当数据库不存在时(即,在删除应用程序或重置模拟器后重新安装后) ),并且当数据库确实存在时。我在物理设备上也得到了相同的结果。

感谢 @IlludiumPu36 和 @Daij-Djan 提供提示/答案,我希望这可以帮助其他人。

【讨论】:

以上是关于Swift - 设备上不包含预填充的 SQLite 数据库的主要内容,如果未能解决你的问题,请参考以下文章

编辑在 IOS 应用程序中管理的预填充 sqlite 数据库核心数据

包含静态预填充和动态用户数据的核心数据

如何用应用商店中的新版本替换 iOS 应用中预先填充的核心数据?

离子预填充数据库 Sqlite

如何在 UWP 中使用预填充的 sqlite 数据库?

什么意思位置:'default' SQLite 和 Ionic 预填充数据库