Неверный вывод SELECT с SQLite3 и Swift - PullRequest
0 голосов
/ 18 мая 2018

У меня есть задача сделать мобильное приложение в 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
    }
}

Дело в том, что по какой-то причине имя ученика не появляется в таблице.Вместо этого показан только фрагмент даты, разрезанный по количеству букв в имени ученика.Поэтому, если я попытаюсь вставить «Джон» 18 мая, он вернет «18.0» - 4 символа, выбранные из даты.Любая помощь в том, как это исправить, приветствуется.

1 Ответ

0 голосов
/ 18 мая 2018

Основная проблема заключается в том, что вы используете String(cString: sqlite3_column_text(stmt, 1) для чтения сохраненной строки, но вы пишете строку, используя простую ссылку на переменную String вместо использования чего-то вроде name.utf8CString.Обновите каждый из sqlite3_bind_text вызовов.

В вашем коде есть несколько других проблем, которые необходимо исправить:

  1. Никогда не используйте SELECT * FROM.Нет гарантии, что поля будут возвращены в определенном порядке.Перечислите поля так, чтобы индекс столбца, который вы передаете sqlite3_column_xxx, как известно, соответствовал.
  2. Вы никогда не вызовете sqlite3_finalize для подготовленного оператора в buttonSave и readValues.И вам нужно будет реорганизовать ваш код, чтобы завершить подготовленный оператор, даже если другой код выдает ошибку.
  3. Ваш класс Student, вероятно, должен быть struct.Тогда вам даже не нужно явно указывать init.
  4. Имена переменных должны начинаться со строчных букв.Измените Students на students.
  5. В вашем методе buttonSave проверьте правильность name раньше.Нет смысла создавать и форматировать дату, если имя недействительно.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...