3

How to store and fetch images in SQLite and in what format the images get saved? It would be more helpful if explained with an example.

Deepika P
  • 81
  • 1
  • 2
  • 7

3 Answers3

13

Image itself cannot be stored into a database columns but you can first convert it into a string and then store it. The string is called base64 string. As far as I know, any image can be converted to that and reversely.

To encode to base 64:

let image : UIImage = UIImage(named:"imageNameHere")!
let imageData:NSData = UIImagePNGRepresentation(image)!
let strBase64 = imageData.base64EncodedString(options: .lineLength64Characters)

Now your UIImage object is converted to a String! Save strBase64 to SQLite DB. Remember to use text as column type because this string is very long.

To decode back to UIImage:

let dataDecoded:NSData = NSData(base64EncodedString: strBase64, options: NSDataBase64DecodingOptions(rawValue: 0))!
let decodedimage:UIImage = UIImage(data: dataDecoded)!
Fangming
  • 24,551
  • 6
  • 100
  • 90
  • 5
    Instead of using NSData use directly Swift 3 native Data `let decodedData = Data(base64Encoded: base64Str, options: .ignoreUnknownCharacters)` – Kaz Miller Apr 13 '18 at 21:39
  • A nice and better explanation (y) – iOS Developer Oct 17 '18 at 07:26
  • I am trying to save the strBase64 into the db with column type text just as you said but its not saving. – Nishad Arora Nov 02 '18 at 06:33
  • @NishadArora Lots of things could go wrong when you do things like this. Have you try saving some random string and see if that will save? Did you see any errors from sql? Maybe it's problem with sql, not the base64 itself, which is just a string. Are you trying to save the string into a db file located within the main bundle? If you, you will need to copy that to the document folder before making any modifications – Fangming Nov 02 '18 at 15:01
  • @Fangming I tried saving random string and it getting saved in the db , can't see any errors also while saving base string 64 , tried saving the data blob also , tried all now left with only option to save the image in document directory and then locally save the path in the db. – Nishad Arora Nov 05 '18 at 06:03
  • what do you mean by "Are you trying to save the string into a db file located within the main bundle?" @Fangming – Nishad Arora Nov 06 '18 at 05:30
  • @NishadArora Main bundle is where the source files of your app are located. If you want to ship a DB file with your application, you will have to copy that file into the document folder first before saving data into it. – Fangming Nov 06 '18 at 05:45
  • @Fangming Finally!! I got the image inserted in my db using BLOB type.After lots of research I found out that inserting and retrieving an image is possible only if we convert the image into a blob first. – Nishad Arora Nov 06 '18 at 05:57
1

Alternative

  1. save your image into document directory.
  2. save your image file path or name of image in sqlite
  3. Get the image path or name from sqlite and access that path from document directory.

Take Ref : Iphone : How to Display Document Directory images in Image View?

dahiya_boy
  • 9,298
  • 1
  • 30
  • 51
1

You can also store your image directly as a BLOB, however it depends on which framework you use for SQLite access. In case you use SQLite.swift, then there is an option:

Set up a file SQLiteHelper.swift like that:

class SQLiteHelper{
    var db: Connection!

    let personsTable = Table("person")
    let id = Expression<Int>("id")
    let firstName = Expression<String>("firstName")
    let lastName = Expression<String>("lastName")
    let profileImage = Expression<Data>("profileImage")
    let date = Expression<Date>("savedAt")

    init() {
        do{
            let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
            let dbTemp = try Connection("\(path)/myDb.sqlite3") //Create db if not existing
            self.db = dbTemp
        }
        catch {
            print("Error info: \(error)")
        }
    }

    public func insertData(firstNameVal: String,
                           lastNameVal: String,
                           profileImageVal: Data,
                           dateVal: Date){

        do{
            //Create a new table only if it does not exist yet
            try db.run(personsTable.create(ifNotExists: true) { t in      // CREATE TABLE "person" (
                t.column(id, primaryKey: true)          //     "id" INTEGER PRIMARY KEY NOT NULL,
                t.column(firstName)                     //     "firstName" TEXT,
                t.column(lastName)                      //     "lastName" TEXT,
                t.column(profileImage)                  //     "profileImage" BLOB,
                t.column(date)                          //     "savedAt" DATETIME)
            })
        }
        catch {
            print("The new SQLite3 Table could not be added: \(error)")
        }

        do{
            try db.run(personsTable.insert(firstName <- firstNameVal,
                                            lastName <- lastNameVal,
                                            profileImage <- profileImageVal,
                                            date <- dateVal
            ))
        }
        catch {
            print("Could not insert row: \(error)")
        }
    }

    public func getData() -> [Person]{
        var persons = [Person]()
        do{
            for row in try db.prepare(personsTable) {
                let person: Person = Person(firstName: row[firstName],
                                               lastName: row[lastName],
                                               profileImage: row[profileImage],
                                               savedAt: row[date])

                persons.append(person)
            }
        }
        catch {
            print("Could not get row: \(error)")
        }
        return persons
    }

Now create a file Person.swift and put the following struct inside of it:

import Foundation

struct Person: Identifiable {
    var id = UUID()
    var firstName: String
    var lastName: String
    var profileImage: Data
    var savedAt: Date
}

Store Data

In order to store data as a .png BLOB you would now basically do something like that:

var databaseHelper: SQLiteHelper = SQLiteHelper.init()
self.databaseHelper.insertData(firstNameVal: "yourFirstName",
                                   lastNameVal: "yourLastName",
                              profileImageVal: yourImageView.pngData(),
                              dateVal: Date())

Retreive Data

If you want to display the image later in another Imageview you would have to do this:

var persons = self.databaseHelper.getData()
let profileImage = UIImage(data: persons[0].profileImage)
let myImageView = UIImageView(image: profileImage)

Saving UIImage as BLOB

I have saved the image as a .png because I want to use my database outside of iOS and therefore want to ensure compatibility. If you want you can also store your UIImage directly. You would roughly need to change it like that:

let profileImage = Expression<UIImage>("profileImage")
...
profileImageVal: yourImageView,
...
let myImageView = persons[0].profileImage
...

import Foundation
import UIKit

struct Person: Identifiable {
    var id = UUID()
    var firstName: String
    var lastName: String
    var profileImage: UIImage
    var savedAt: Date
}

Note: SQLite.swift also supports lazy loading, which would probably make more sense in ascenario like that...

frankenapps
  • 5,800
  • 6
  • 28
  • 69