SQLite3和Swift

时间:2018-05-18 11:38:19

标签: swift database sqlite

我有一项任务是在Swift中制作一个移动应用程序来存储有关学生的数据。进入应用程序时,应创建数据库。应该有一个文本字段,用户可以从中将其名称插入数据库,还有一个按钮,用于重新加载页面并查看数据库中的所有名称。此外,创建日期应存储在数据库中。 我有以下代码:

import UIKit
import SQLite3

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    var db: OpaquePointer?
    var Students = [Student]()

    @IBOutlet weak var textFieldName: UITextField!
    @IBAction func buttonSave(_ sender: UIButton) {
        //getting values from textfields
        let name = textFieldName.text?.trimmingCharacters(in: .whitespacesAndNewlines)
        let d = Date()
        let formatter = DateFormatter()
        formatter.dateFormat = "dd.MM.yyyy"
        let date = formatter.string(from: d)

        //validating that values are not empty
        if(name?.isEmpty)!{
            textFieldName.layer.borderColor = UIColor.red.cgColor
            return
        }

        //creating a statement
        var stmt: OpaquePointer?

        //the insert query
        let queryString = "INSERT INTO Students (name, date) VALUES (?,?)"

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

        //binding the parameters
        if sqlite3_bind_text(stmt, 1, name, -1, nil) != SQLITE_OK{
            let errmsg = String(cString: sqlite3_errmsg(db)!)
            print("failure binding name: \(errmsg)")
            return
        }

        if sqlite3_bind_text(stmt, 2, date, -1, nil) != SQLITE_OK{
            let errmsg = String(cString: sqlite3_errmsg(db)!)
            print("failure binding date: \(errmsg)")
            return
        }

        //executing the query to insert values
        if sqlite3_step(stmt) != SQLITE_DONE {
            let errmsg = String(cString: sqlite3_errmsg(db)!)
            print("failure inserting student: \(errmsg)")
            return
        }

        //emptying the textfields
        textFieldName.text=""
        readValues()

        //displaying a success message
        print("Student saved successfully")
    }

    @IBOutlet weak var tableViewStudents: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        let fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
            .appendingPathComponent("StudentsDatabase.sqlite")

        if sqlite3_open(fileURL.path, &db) != SQLITE_OK {
            print("error opening database")
        }

        if sqlite3_exec(db, "DROP TABLE Students", nil, nil, nil) != SQLITE_OK {
            let errmsg = String(cString: sqlite3_errmsg(db)!)
            print("error deleting table: \(errmsg)")
        }

        if sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS Students (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, date TEXT)", nil, nil, nil) != SQLITE_OK {
            let errmsg = String(cString: sqlite3_errmsg(db)!)
            print("error creating table: \(errmsg)")
        }

    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return Students.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
        let student: Student
        student = Students[indexPath.row]
        print(student.name)
        cell.textLabel?.text = student.name
        return cell
    }

    func readValues(){
        //first empty the list of heroes
        Students.removeAll()

        //this is our select query
        let queryString = "SELECT * FROM Students"

        //statement pointer
        var stmt:OpaquePointer?

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

        //traversing through all the records
        while(sqlite3_step(stmt) == SQLITE_ROW){
            let id = sqlite3_column_int(stmt, 0)
            let name = String(cString: sqlite3_column_text(stmt, 1))
            let date = String(cString: sqlite3_column_text(stmt, 2))

            //adding values to list
            Students.append(Student(id: Int(id), name: String(name), date: String(date)))
        }
        tableViewStudents.reloadData()
    }
}

class Student {
    var id: Int
    var name: String
    var date: String

    init(id: Int, name: String, date: String){
        self.id = id
        self.name = name
        self.date = date
    }
}

事情是,由于某种原因,学生的名字没有出现在表格中。相反,只显示一片日期,按学生姓名中的字母数进行切片。因此,如果我尝试在5月18日插入“John”,它将返回“18.0” - 从日期中选择4个符号。 如何解决这个问题的任何帮助表示赞赏。

1 个答案:

答案 0 :(得分:0)

主要问题是您使用String(cString: sqlite3_column_text(stmt, 1)来读取存储的字符串,但是您使用对String变量的简单引用来编写字符串,而不是使用name.utf8CString之类的内容。更新每个sqlite3_bind_text来电。

您的代码中还有一些其他问题需要修复:

  1. 永远不要使用SELECT * FROM。无法保证按特定顺序返回字段。列出字段,以便传递给sqlite3_column_xxx的列索引与之匹配。
  2. 您永远不会在sqlite3_finalizebuttonSave中的预准备语句上致电readValues。并且您需要重构代码以确保即使其他代码出错也能最终确定准备好的语句。
  3. 您的Student课程应该是struct。那么您甚至不需要明确提供init
  4. 变量名称应以小写字母开头。将Students更改为students
  5. buttonSave方法中,请尽早查看name的有效性。如果名称无效,则无法创建和格式化日期。