Search code examples
xcodeswiftsqlitexcode6fmdb

Append FMDB SQLite results to Swift array


I'm trying to append results from a FMDB SQLite query to a Swift array. The error I'm getting in XCode is 'value of option type 'String?' not unwrapped.'

Swapping out the line below in the while loop, the FMDB results can be printed to console OK, so no problems there.

println(results_lab_test?.stringForColumn("lab_test"))

New to XCode, so be kind...

  import UIKit

class ViewController: UIViewController {


    @IBOutlet weak var tests_label: UILabel!

    var databasePath = NSString()

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from a nib.

        var arrayData:[String] = []



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

            let query_lab_test = "SELECT lab_test FROM lab_test"

            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_label.text = multiLineString
                    tests_label.numberOfLines = 0
                    tests_label.lineBreakMode = NSLineBreakMode.ByWordWrapping
                    tests_label.sizeToFit()

            }

        }
    }
    }


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

Working Code thanks to @skypecakes:

import UIKit

class ViewController: UIViewController {


    @IBOutlet weak var tests_label: UILabel!

    var databasePath = NSString()

    override func viewDidLoad() {
        super.viewDidLoad()

        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"

            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)

            }
        }
            println(arrayData)
            myDatabase.close()
    }
    }


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

Solution

  • It seems like results_lab_test?.stringForColumn("lab_test") returns a String?, which is an optional. Your array is defined as an array of "String" items, so you can't put a "String?" inside it. Try this:

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

    Note that in general it is good practice to unwrap all of your optionals with "if let" instead of assuming that they are populated. So everywhere you have a question mark (e.g. results_lab_test?.stringForColumn), you can use "if let".

    If you're using XCode 7 with Swift 2.0, this would be a good case for the "guard let" statement, which provides a convenient syntax to abort your code if an optional returns null:

    guard let queryResults = results_lab_test else return
    while queryResults.next() == true {
    if let resultString = queryResults.stringForColumn("lab_test")
            arrayData.append(resultString)
    }
    

    This test worked for me (table has 2 rows, and printing the array prints 2 rows), in case it helps you:

    import UIKit
    import FMDB
    
    class ViewController: UIViewController
    {
        override func viewDidLoad()
        {
            super.viewDidLoad()
    
            if let myDatabase = makeSqlDB()
            {
                var arrayData:[String] = []
                let query_lab_test = "SELECT lab_test FROM lab_test"
    
                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)
                    }
                }
    
                println(arrayData)
                myDatabase.close()
            }
        }
    
    
        private func makeSqlDB()->FMDatabase?
        {
            let database = FMDatabase(path: String())
    
            if !database.open() {
                println("Unable to open database")
                return nil
            }
    
            if !database.executeUpdate("create table lab_test(lab_test text)", withArgumentsInArray: nil) {
                println("create table failed: \(database.lastErrorMessage())")
            }
    
            if !database.executeUpdate("insert into lab_test (lab_test) values (?)", withArgumentsInArray: ["test1"]) {
                println("insert 1 table failed: \(database.lastErrorMessage())")
            }
    
            if !database.executeUpdate("insert into lab_test (lab_test) values (?)", withArgumentsInArray: ["test2"]) {
                println("insert 2 table failed: \(database.lastErrorMessage())")
            }
    
            return database
        }
    
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    }