Простое хранение/извлечение больших двоичных объектов или данных (возможно, NSKeyArchive класса)

Опытный разработчик здесь... но несколько новичок в Swift и определенный новичок в SQLite.swift...

Я пытаюсь разобраться с использованием SQLite в приложении для iOS, поэтому я создал простое приложение, чтобы убедиться, что у меня есть основы... Все шло отлично, пока я не дошел до точки хранения и извлечения объектов данных (т.е. объекты класса)...

Вот мой код:

    //**********************************************************************
    //**
    //** Create/Open database connection
    //**
    //**********************************************************************
    let dbPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
    let db: Connection = try! Connection("\(dbPath)/stickis.sqlite3")

    let dbTable = Table("testtbl")
    let id = Expression<Int64>("id")
    let boolfld = Expression<Bool>("boolfld")
    let int64fld = Expression<Int64>("int64fld")
    let stringfld = Expression<String>("stringfld")
    let blobfld = Expression<SQLite.Blob?>("blobfld") // optional due to ?

    //**********************************************************************
    //*
    //* Drop table if it exists
    //*
    //**********************************************************************
    try! db.run(dbTable.drop(ifExists: true))

    //**********************************************************************
    //**
    //** Create/Open Table
    //**
    //**********************************************************************
    do
    {
        try db.run((dbTable.create(ifNotExists: true)
        {
            t in
            t.column(id, primaryKey: true)
            t.column(boolfld)
            t.column(int64fld)
            t.column(stringfld)
            t.column(blobfld)
            })
        )
    }
    catch
    {
        print("Failed to create sticki table")
    }

    //**********************************************************************
    //**
    //** Add Record 1
    //**
    //**********************************************************************
    do
    {
        let testblob: Blob = windowcontent()
        let rowid = try db.run(dbTable.insert(boolfld <- true, int64fld <- 1963, stringfld <- "unknown", blobfld <- testblob))
        print("inserted id: \(rowid)")
    }
    catch
    {
        print("insertion failed: \(error)")
    }

    //**********************************************************************
    //**
    //** Add Record 2
    //**
    //**********************************************************************
    do
    {
        let rowid = try db.run(dbTable.insert(boolfld <- true, int64fld <- 1972, stringfld <- "David"))
        print("inserted id: \(rowid)")
    }
    catch
    {
        print("insertion failed: \(error)")
    }

    //**********************************************************************
    //**
    //** Update Record 1
    //**
    //**********************************************************************
    let rec2updt = dbTable.filter(id == 1)
    do
    {
        if try db.run(rec2updt.update(stringfld <- "TJ")) > 0
        {
            print("updated to TJ")
        }
        else
        {
            print("record not found")
        }
    }
    catch
    {
        print("update failed")
    }

    //**********************************************************************
    //**
    //** Query Particular Record using filter
    //**
    //**********************************************************************
    let tjFilter = dbTable.filter(int64fld == 1964)
    for dataRec in try! db.prepare(tjFilter)
    {
        print("id: \(dataRec[id]), stringfld: \(dataRec[stringfld])")
    }

    //**********************************************************************
    //**
    //** Query All Records
    //**
    //**********************************************************************
    for dataRec in try! db.prepare(dbTable)
    {
        print("id: \(dataRec[id]), stringfld: \(dataRec[stringfld])")
    }

    //**********************************************************************
    //**
    //** Delete Records
    //**
    //**********************************************************************
    try! db.run(dbTable.delete())

У меня все работало нормально.... пока я не добавил следующую строку в блок комментариев "Добавить запись 1"...

    let testblob: Blob = windowcontent()

windowcontent() - это класс (или может быть структура), который я хочу "архивировать" и сохранить... Причина, по которой я использую Blob для его хранения, заключается в том, что сохраненный тип объекта может относиться к нескольким различным классам.

Я также пробовал это:

        let testdata: Data = Data()
        let testblob: Blob = testdata as Blob

и получите ожидаемую ошибку, связанную с невозможностью преобразования данных в BLOB-объекты.

Есть ли у кого-нибудь простой пример назначения и извлечения данных в/из SQLite.Blob, которым они могли бы поделиться со мной?

Кроме того, побочная тема, есть ли хорошее место, где я могу поделиться своим «базовым» примером кода iOS, когда все это будет запущено? Предполагая, что может появиться какой-нибудь другой новичок, и это может облегчить жизнь.


person tj4shee    schedule 07.09.2017    source источник
comment
К вашему сведению... я не ожидал, что let testblob: Blob = windowcontent() действительно сработает... для меня это был лучший способ просто показать, что я хотел закодировать... поэтому, пожалуйста, не увлекайтесь пытаясь отладить синтаксис этой строки...   -  person tj4shee    schedule 07.09.2017
comment
Выяснил часть решения.... используйте Blob(bytes: [UInt8](xxx)) для преобразования данных в большой двоичный объект... где xxx - объект данных.... Теперь, чтобы выяснить, как преобразовать обратно к данным...   -  person tj4shee    schedule 08.09.2017
comment
Вторая часть решения - преобразование из Blob обратно в данные выполняется с использованием Blob.bytes... см. код, который я ввожу в качестве ответа.   -  person tj4shee    schedule 08.09.2017


Ответы (1)


Вот рабочий код, который отвечает на мой вопрос. См. раздел «Запросить конкретную запись с использованием фильтра», чтобы узнать, где я конвертирую из Blob обратно в String.

override func viewDidLoad()
{
    super.viewDidLoad()
    // Do any additional setup after loading the view.


    //**********************************************************************
    //**
    //** Create/Open database connection
    //**
    //**********************************************************************

    // dbpath is where we want the sqlite file to reside... in this case we are using the documents directory
    let dbPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!

    // the filename we are giving our database is "testdb.sqlite3" - it can be what ever you like.  If the file exists, it opens it as a database, if it does not exist, it will create it and then open it.
    let db: Connection = try! Connection("\(dbPath)/testdb.sqlite3")

    // defining the name of the SQLite table we want to use as well as the field names we want to create in the table.  In this case, we are creating one of each major type of fields - Bool, Long Int, String, Blob
    let dbTable = Table("testtbl")
    let id = Expression<Int64>("id")
    let boolfld = Expression<Bool>("boolfld")
    let int64fld = Expression<Int64>("int64fld")
    let stringfld = Expression<String>("stringfld")
    let blobfld = Expression<SQLite.Blob?>("blobfld") // Blob field is optional due to SQLite.Blob? - remove the ? to make it require "Not Null"

    //**********************************************************************
    //*
    //* Drop table if it exists
    //*
    //**********************************************************************
    // Deleting the table if it exists - this is simply to allow the program to be rerun and start by  creating a new table
    try! db.run(dbTable.drop(ifExists: true))

    //**********************************************************************
    //**
    //** Create/Open Table
    //**
    //**********************************************************************
    do
    {
        // create the table with the following fields...
        try db.run((dbTable.create(ifNotExists: true)
        {
            t in
            // the fields in our table...
            t.column(id, primaryKey: true)
            t.column(boolfld)
            t.column(int64fld)
            t.column(stringfld)
            t.column(blobfld)
            })
        )
    }
    catch
    {
        // should not get to this, but if it does, you can expect the remainder of the app to fail also.
        print("Failed to create sticki table")
    }

    //**********************************************************************
    //**
    //** Add Record 1
    //**
    //**********************************************************************
    do
    {
        // setup a Data var and then save it as an SQLite.Blob
        let testdata: Data = "foo".data(using: .utf8)!  //Data()
        let testblob: Blob = Blob(bytes: [UInt8](testdata))

        // insert a new record into the database... the function will return the rowID of the newly inserted record.
        let rowid = try db.run(dbTable.insert(boolfld <- true, int64fld <- 1963, stringfld <- "unknown", blobfld <- testblob))

        print("inserted id: \(rowid)")
    }
    catch
    {
        print("insertion failed: \(error)")
    }

    //**********************************************************************
    //**
    //** Add Record 2
    //**
    //**********************************************************************
    do
    {
        // Adding a 2nd record to the database - no Blob field this time
        let rowid = try db.run(dbTable.insert(boolfld <- true, int64fld <- 1972, stringfld <- "David"))
        print("inserted id: \(rowid)")
    }
    catch
    {
        print("insertion failed: \(error)")
    }

    //**********************************************************************
    //**
    //** Update Record 1
    //**
    //**********************************************************************

    // Setup filter to get record "WHERE id == 1"
    let rec2updt = dbTable.filter(id == 1)
    do
    {
        // db.run will return the # of records updated - must be > 0 (actually, == 1)
        if try db.run(rec2updt.update(stringfld <- "TJ")) > 0
        {
            print("updated to TJ")
        }
        else
        {
            print("record not found")
        }
    }
    catch
    {
        print("update failed")
    }

    //**********************************************************************
    //**
    //** Query Particular Record using filter
    //**
    //**********************************************************************

    // Setup filter to get record "WHERE int64fld == 1963"
    let tjFilter = dbTable.filter(int64fld == 1963)

    // Run using filter and print out results (should only be 1 rec in our case)
    for dataRec in try! db.prepare(tjFilter)
    {
        // Convert Blob back to String
        let tmpStr: String = String(bytes: dataRec[blobfld]!.bytes, encoding: .utf8)!
        print("id: \(dataRec[id]), stringfld: \(dataRec[stringfld]), blobfld: \(tmpStr)")
    }

    //**********************************************************************
    //**
    //** Query All Records
    //**
    //**********************************************************************

    // Prints all records in database
    for dataRec in try! db.prepare(dbTable)
    {
        print("id: \(dataRec[id]), stringfld: \(dataRec[stringfld])")
    }

    //**********************************************************************
    //**
    //** Delete Records
    //**
    //**********************************************************************

    // Deletes ALL records in the table (use a filter to delete individual records)
    try! db.run(dbTable.delete())

}

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