iPhone, поскольку симулятор Xcode не читает из базы данных sqlite - PullRequest
1 голос
/ 15 марта 2020

Я кодировал приложение, которое использует базу данных sqlite, которая поставляется с Xcode. Это хорошо работает на ма c, но когда я выбираю iPhone в качестве симулятора, приложение (на iPhone) не читает данные базы данных. Нужно ли кодировать доступ к базе данных sqlite иначе, чем к ней на ма c?

Вот моя функция для получения данных из базы данных, которая используется в представлении выбора. Это окно выбора не заполняется, когда я использую iPhone (связанный с компьютером через USB). Однако он заполняется, когда я запускаю любой из перечисленных симуляторов

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)))
    }

}

1 Ответ

0 голосов
/ 15 марта 2020

Такое поведение, когда вы не видите данные в базе данных, обычно означает, что они пустые.

Итак, сначала подтвердите этот тезис. Загрузите контейнер с физического устройства (см. { ссылка }) и откройте базу данных, найденную в этом контейнере, из macOS и проверьте, что в ней содержится. Я держу пари, что база данных или таблица пуста.

Если предположить, что это действительно проблема, возникает вопрос о том, как вы получили пустую базу данных на этом устройстве, а не копию базы данных. в вашем приложении. Скорее всего, в какой-то момент во время разработки и тестирования на физическом устройстве приложение случайно открыло базу данных в папке документов без предварительного успешного копирования версии пакета. К сожалению, по умолчанию sqlite3_open создает пустую базу данных, если она не найдена. Итак, вы хотите (а) удалить эту пустую базу данных с вашего устройства; и (б) написать код, который предотвращает это в будущем.

Поэтому я бы предложил:

  1. Удалите ваше приложение с соответствующего устройства. Это удалит все пустые базы данных, созданные во время этого процесса разработки / тестирования.

  2. Если вы распространяете приложение с предварительно заполненной базой данных, удалите все ссылки на sqlite3_open и замените их на sqlite3_open_v2, используя только параметр SQLITE_OPEN_READWRITE (убедитесь, что приложение не может создать пустую базу данных для вас). В частности, не используйте параметр SQLITE_OPEN_CREATE.

  3. Пересмотрите вашу open процедуру. Например, вы можете сделать что-то вроде:

    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
    }
    

    Есть несколько вещей, на которые я хотел бы обратить ваше внимание в приведенном выше:

    • Я бы предложил вы используете каталог поддержки приложений, а не папку документов. См. iOS Стандартные каталоги: где находятся файлы или посмотрите iOS Рекомендации по хранению видео.

    • Просто попробуйте открыть базу данных в папке поддержки приложения. Если это не удается, попробуйте скопировать из пакета в каталог поддержки приложений и попробуйте снова.

    • Кстати, если sqlite3_open_v2 (или sqlite3_open) не удается, помните, что вам все равно придется Звоните sqlite3_close. Как говорится в документации SQLite : «Независимо от того, возникает ли ошибка при ее открытии, ресурсы, связанные с дескриптором соединения с базой данных, должны быть освобождены путем передачи ее в sqlite3_close(), когда она больше не требуется».

  4. Теперь ваш 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
    }
    

    Примечание:

    • Если вы собираетесь открыть базу данных при запуске SQL, вы захотите закрыть ее, когда закончите. Лично я открываю базу данных один раз и оставляю ее открытой (вместо того, чтобы засорять вызовы open по всему контроллеру базы данных), но если вы собираетесь постоянно повторно открывать базу данных для каждого вызова SQL, не забудьте также закрыть ее .

    • Если ваша «подготовка» прошла успешно, обязательно также «завершите» ее, иначе произойдет утечка.

    • I ' d, если возможно, предложите избегать принудительного развертывания, !.

...