how to save image into sqlite3 using swift - ios

I want to store an image into sqlite3. I am new to iOS please help me out how to save this image and retrieve in image view.
Appdelegate.swift
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
createDB()
return true
}
func createDB()
{
// create db
let dir = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let dbpath = dir.appendingPathComponent("imgdatabase.sqlite")
print(dir)
// check if file exist
let m = FileManager()
if m.fileExists(atPath: dbpath.path)
{
print("file exist no need to create")
}
else
{
m.createFile(atPath: dbpath.path, contents: nil, attributes: nil)
}
// open db
var op : OpaquePointer? = nil
if sqlite3_open(dbpath.path, &op)==SQLITE_OK
{
print("db open successfuly")
let query = "create table img_save(img text)"
if sqlite3_exec(op, query.cString(using: .utf8), nil, nil, nil)==SQLITE_OK
{
print("table created ")
}
else
{
print("table not created")
}
}
else
{
print("db unable to open")
}
sqlite3_close(op)
}
Image_save_ViewController.swift
import UIKit
class Image_save_ViewController: UIViewController {
#IBAction func img_display_click(_ sender: AnyObject) {
}
#IBOutlet weak var imgview: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func img_save_click(_ sender: AnyObject) {
let image : UIImage = UIImage(named: "35-handgun-png-image.png")!
let imageData : NSData = UIImagePNGRepresentation(image)! as NSData
let strBase64 = imageData.base64EncodedString(options: .lineLength64Characters)
var decodeimg : NSData = NSData(base64Encoded: strBase64, options: NSData.Base64DecodingOptions(rawValue: 0))!
imgview.image = UIImage(data : decodeimg as Data)!
let dir = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let dbpath = dir.appendingPathComponent("imgdatabase.sqlite")
print(dir)
// check if file exist
let m = FileManager()
if m.fileExists(atPath: dbpath.path)
{
print("file exist no need to create")
}
else
{
m.createFile(atPath: dbpath.path, contents: nil, attributes: nil)
print("db file created")
}
// open db
var op : OpaquePointer? = nil
if sqlite3_open(dbpath.path, &op)==SQLITE_OK
{
print("db open successfuly")
let image_d = strBase64
let query = String.init(format : "insert into img_save values('%#')", image_d)
if sqlite3_exec(op, query.cString(using: .utf8), nil, nil, nil)==SQLITE_OK
{
print("image saved successfuly")
}
else
{
print("unable to save")
}
}
else
{
print("unable to open db")
}
sqlite3_close(op)
}
I AM ADDING HERE READDB() FUNC THAT I AM FETCHING IMAGE FROM DB
#IBOutlet weak var imgview: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
readDB()
//imgview.image = UIImage(named: "35-handgun-png-image.png")
}
func readDB()
{
let image : UIImage = UIImage(named: "35-handgun-png-image.png")!
let imageData = UIImagePNGRepresentation(image)!
let docdir = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
print(docdir)
let imgurl = docdir.appendingPathComponent("35-handgun-png-image.png")
try! imageData.write(to: imgurl)
let newImage = UIImage(contentsOfFile: imgurl.path)
let dir = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let dbpath = dir.appendingPathComponent("imgdatabase.sqlite")
//check file if exist
let m = FileManager()
if m.fileExists(atPath: dbpath.path)
{
print("file exist no need to create")
}
else
{
m.createFile(atPath: dbpath.path, contents: nil, attributes: nil)
print("file created")
}
//open db
var opq : OpaquePointer? = nil
if sqlite3_open(dbpath.path, &opq)==SQLITE_OK
{
let query = "select * from img_save"
var st : OpaquePointer? = nil
if sqlite3_prepare(opq, query.cString(using: .utf8), -1, &st, nil)==SQLITE_OK
{
while sqlite3_step(st)==SQLITE_ROW
{
let imgd = String.init(format : "%s",sqlite3_column_text(st, 0))
//let imgd = String.init(format : "%s",sqlite3_value_text(st!))
imgview.image = UIImage(named : imgd)
print("nil value")
}
}
}
sqlite3_close(opq)
}
EARLIER IT WAS SHOWING FATAL ERROR FOUND NIL WHILL UNWRAPPING VALUES BUT NOW I HAVE TAKEN IMAGE VIEW TO STORE COLUM VALUE AND DISPLAY IT USING IMAGE VIEW..SO PLEASE TELL ME HOW TO ACHIEVE THIS I AM GETTING NIL VALUE OUT THERE

If possible you write your image in application document directory by specific name.
And save that name in sqlite table. Retrieve name from sqlite and image from document directory.

Related

Why am not able to access my model class in Swift Project?

How to access my Model from ViewController and use the Model data to load in table view????
Source Code Link
My ViewController looks like this
import UIKit
class ViewController: UIViewController {
var cclm: CountryCodeListModel?
override func viewDidLoad() {
super.viewDidLoad()
Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(hello), userInfo: nil, repeats: true)
readLocalJSONFile(forName: "countryList")
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
}
#objc func hello()
{
print(cclm?.data?[0].flag)
}
}
and my model class look like this
struct CountryCodeList : Decodable {
var alpha2Code: String?
var alpha3Code: String?
var flag : String?
var name : String?
var code : String?
}
public struct CountryCodeListModel : Decodable {
var data : [CountryCodeList]?
}
var cclm: CountryCodeListModel?
//Method to load json
func readLocalJSONFile(forName name: String) {
do {
if let filePath = Bundle.main.path(forResource: name, ofType: "json") {
let fileUrl = URL(fileURLWithPath: filePath)
let data = try Data(contentsOf: fileUrl)
if let countryCodeObject = parse(jsonData: data) {
cclm = countryCodeObject
print(cclm?.data?[1].alpha2Code ?? "") //Printing Correct Value
}
}
} catch {
print("error: \(error)")
}
}
func parse(jsonData: Data) -> CountryCodeListModel?{
var dataArray : [Dictionary<String,Any>] = [[:]]
var country = Dictionary<String,Any>()
var modelData = Dictionary<String,Any>()
do {
// make sure this JSON is in the format we expect
if let json = try JSONSerialization.jsonObject(with: jsonData, options: []) as? Dictionary<String,Any> {
dataArray.removeAll()
for item in json["data"] as! [Dictionary<String, Any>] {
country = item
let url = URL(string: country["flag"] as? String ?? "")
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
let image = UIImage(data: data!)
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileName = url?.lastPathComponent // name of the image to be saved
let fileURL = documentsDirectory.appendingPathComponent(fileName ?? "")
if let data = image?.jpegData(compressionQuality: 1.0){
do {
try data.write(to: fileURL)
country["flag"] = fileURL.absoluteString
//print("file saved")
//urlAsString = fileURL.absoluteString
} catch {
print("error saving file:", error)
}
}
dataArray.append(country)
country.removeAll()
}
modelData["data"] = dataArray
//print(modelData)
let jsonData1 = try JSONSerialization.data(withJSONObject: modelData, options: [])
do {
let decodedData = try JSONDecoder().decode(CountryCodeListModel.self, from: jsonData1)
return decodedData
} catch {
print("error: \(error)")
}
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
return nil
}
Problem statement:
Iam reading local json and take the url value of flag key and download corresponding images to local. Once i download then am taking the localpath and update in the dictionary and then create JSON object and update my model class.
Now, am trying to access my model class from ViewController like below
print(CountryCodeListModel?.data?[0].name) //check screenshot for error
print(cclm?.data?[0].flag) // this prints nil always
Please check the error screenshots attached2
My JSON look like this
{
"meta":{
"success":true,
"message":"Successfully retrieved country details",
"code":"200"
},
"data":[
{
"alpha2Code":"AF",
"alpha3Code":"AFG",
"flag":"https://raw.githubusercontent.com/DevTides/countries/master/afg.png",
"name":"Afghanistan",
"code":"+93"
},
{
"alpha2Code":"AX",
"alpha3Code":"ALA",
"flag":"https://raw.githubusercontent.com/DevTides/countries/master/ala.png",
"name":"Aland Islands",
"code":"+358"
},
{
"alpha2Code":"AL",
"alpha3Code":"ALB",
"flag":"https://raw.githubusercontent.com/DevTides/countries/master/alb.png",
"name":"Albania",
"code":"+355"
},
{
"alpha2Code":"DZ",
"alpha3Code":"DZA",
"flag":"https://raw.githubusercontent.com/DevTides/countries/master/dza.png",
"name":"Algeria",
"code":"+213"
},
{
"alpha2Code":"AS",
"alpha3Code":"ASM",
"flag":"https://raw.githubusercontent.com/DevTides/countries/master/asm.png",
"name":"American Samoa",
"code":"+1684"
}
]
}
You are trying to decode something that doesn't exist.
print(CountryCodeListModel?.data?[0].name) //check screenshot for error
print(cclm?.data?[0].flag) // this prints nil always
The above code states that you want:
the name of
the variable data at position 0 of
the struct CountryCodeListModel.
What you want to do is:
the name of
the variable at position 0 of
an INSTANCE of the struct CountryCodeListModel.
For example...
func readLocalJSONFile(forName name: String) {
do {
if let filePath = Bundle.main.path(forResource: name, ofType: "json") {
let fileUrl = URL(fileURLWithPath: filePath)
let data = try Data(contentsOf: fileUrl)
if let countryCodeObject = parse(jsonData: data) {
cclm = countryCodeObject
print(cclm?.data?[1].alpha2Code ?? "") //Printing Correct Value
print(cclm?.data?[0].flag ?? "")
print(countryCodeObject?.data[0].flag ?? "") // Same as the line above
}
}
} catch {
print("error: \(error)")
}
}
Unless you are trying to use a static variable (at which you would use CountryCodeListModel.data), you need to make sure you are actually using an instance of the structure or an object of a class to reference your properties.
CAVEAT
CountryCodeListModel is a structure. CountryCodeListModel() is an instance of the structure CountryCodeListModel. Since you can have multiple instances of a structure, you need to reference a specific structure when accessing data. Thus, CountryCodeListModel.data will not work and it needs to be CountryCodeListModel().data. In this case, you have cclm.data.

Saved data on JSON file but can not retrieve previous data, only new entries

I am saving datas on on the json file on the first VC , load the data as well and display it when switching tab. When I kill the app or re run the app again, add new datas to the JSON file, only those new datas are on the JSON file, previous datas are gone(deleted without deleting them manually) and can not be load. How do I save the file so that next time I run the program it will just append to the previous data ?
class ViewController: UIViewController {
var game : Game?
var weekLeague : [[Game]]? = []
override func viewDidLoad() {
super.viewDidLoad()
creation()
}
#IBAction func endWLButton(_ sender: UIButton) {
if games != nil {
weekLeague?.append(games!)
}
save()
}
func save(){
guard let documentDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileUrl = documentDirectoryUrl.appendingPathComponent("ArrayOfArray.json")
print(fileUrl)
let json = try? JSONEncoder().encode(weekLeague)
do {
try json?.write(to: fileUrl)
print(json!)
print(weekLeague)
print("JSON data was written to teh file successfully!")
}catch{
print(error)
}
}
func ShouldSendGame(game: Game) {
self.game = game
games?.append(game)
}
func creation(){
let documentsDirectoryPathString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let documentsDirectoryPath = NSURL(string: documentsDirectoryPathString)!
let jsonFilePath = documentsDirectoryPath.appendingPathComponent("ArrayOfArray.json")
let fileManager = FileManager.default
var isDirectory: ObjCBool = false
// creating a .json file in the Documents folder
if !fileManager.fileExists(atPath: jsonFilePath!.path, isDirectory: &isDirectory) {
let created = fileManager.createFile(atPath: jsonFilePath!.path, contents: nil, attributes: nil)
if created {
print("File created ")
} else {
print("Couldn't create file for some reason")
}
} else {
print("File already exists")
}
}
}
class AllLeagueController : UITableViewController {
var arrayOfArrayGamesCopy : [[Game]] = []
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.global().async {
self.loadData()
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
func loadData() {
guard let documentsDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileUrl = documentsDirectoryUrl.appendingPathComponent("ArrayOfArray.json")
do{
let data = try Data(contentsOf:fileUrl)
let decoder = JSONDecoder()
let jsonData = try decoder.decode([[Game]].self, from: data)
arrayOfArrayGamesCopy = jsonData
print("Succes")
print(jsonData.count)
} catch{
print(error)
}
}
}
You need to load data here before save ... Also you need to have separate class for saving and loading data .. dot do that in controller .. its against Single Responsibility Principle ...Your load function should return array of [Game] and save data should return success or failure
#IBAction func endWLButton(_ sender: UIButton) {
//load and set data in games from file ...then append current data and save to file
if games != nil {
weekLeague?.append(games!)
}
save()
}

Open DB with FMDB in Swift

i would like to open an existing Database with FMDB. The DB should be a sqlite Database with the ending .db.
My Code is:
static let shared: DBManager = DBManager()
let databaseFileName = "/mydb.db"
var pathToDatabase: String!
var pathNSURL: NSURL!
var database: FMDatabase!
override init() {
super.init()
let documentsDirectory = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString) as String
let zwerg = documentsDirectory + databaseFileName
let pathNSURL = NSURL(fileURLWithPath: zwerg)
let pathString = documentsDirectory + databaseFileName
pathToDatabase = pathNSURL.path
print(pathToDatabase)
}
func firstQuestion() -> Bool {
if openDatabase(){
let query = "SELECT * FROM movie"
do {
let result = try database.executeQuery(query, values: nil)
print(result)
}
catch {
print(error.localizedDescription)
}
database.close()
return true
}
return false
}
func openDatabase() -> Bool {
if database == nil {
print(FileManager.default.fileExists(atPath: pathToDatabase))
if FileManager.default.fileExists(atPath: pathToDatabase) {
print("Database set new path -> File exists")
database = FMDatabase(path: pathToDatabase)
}
}
if database != nil {
print("Database != nil")
if database.open() {
print("Database is open in != nil")
return true
}
}
return false
}
Im calling the Method firstQuestion() from a VC:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
print(DBManager.shared.firstQuestion())
}
My Problem is that the fileExists-Method always returns false. The Filename is correct. This is my first Project with a database in swift, so perhaps i made a stupid mistake... Any suggestions?
Thanks for your help!
let fileManager = FileManager.default
let docURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
let databaseURL = docURL.appendingPathComponent("databaseNaeme.db")
and initialize database as:
database = FMDatabase(path: databaseURL.absoluteString)

How to upload images with cloud kit using swift?

How do I upload and load back images from cloud kit with swift?
What attribute type do I use?
What code do I use? This is the code I use currently...
func SaveImageInCloud(ImageToSave: UIImage) {
let newRecord:CKRecord = CKRecord(recordType: "ImageRecord")
newRecord.setValue(ImageToSave, forKey: "Image")
if let database = self.privateDatabase {
database.saveRecord(newRecord, completionHandler: { (record:CKRecord!, error:NSError! ) in
if error != nil {
NSLog(error.localizedDescription)
}
else {
dispatch_async(dispatch_get_main_queue()) {
println("finished")
}
}
})
}
You need to create a CKAsset and add that to your record. You can do that with code like this:
func SaveImageInCloud(ImageToSave: UIImage) {
let newRecord:CKRecord = CKRecord(recordType: "ImageRecord")
let nsDocumentDirectory = NSSearchPathDirectory.DocumentDirectory
let nsUserDomainMask = NSSearchPathDomainMask.UserDomainMask
if let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true) {
if paths.count > 0 {
if let dirPath = paths[0] as? String {
let writePath = dirPath.stringByAppendingPathComponent("Image2.png")
UIImagePNGRepresentation(ImageToSave).writeToFile(writePath, atomically: true)
var File : CKAsset? = CKAsset(fileURL: NSURL(fileURLWithPath: writePath))
newRecord.setValue(File, forKey: "Image")
}
}
}
if let database = self.privateDatabase {
database.saveRecord(newRecord, completionHandler: { (record:CKRecord!, error:NSError! ) in
if error != nil {
NSLog(error.localizedDescription)
} else {
dispatch_async(dispatch_get_main_queue()) {
println("finished")
}
}
})
}
Here's something similar to Edwin's answer but a little more compact. I've tested this and it works well.
This example is saving "myImage" UIImageView into "mySaveRecord" CKRecord, just replace those names with your respective ones.
let documentDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! String
let imageFilePath = documentDirectory.stringByAppendingPathComponent("lastimage")
UIImagePNGRepresentation(myImage).writeToFile(imageFilePath, atomically: true)
let asset = CKAsset(fileURL: NSURL(fileURLWithPath: imageFilePath))
mySaveRecord.setObject(asset, forKey: "ProfilePicture")
CKContainer.defaultContainer().publicCloudDatabase.saveRecord(mySaveRecord, completionHandler: {
record, error in
if error != nil {
println("\(error)")
} else {
//record saved successfully!
}
})
I created this little extension in Swift 5 to convert from UIImage to CKAsset:
extension UIImage {
func toCKAsset(name: String? = nil) -> CKAsset? {
guard let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first else {
return nil
}
guard let imageFilePath = NSURL(fileURLWithPath: documentDirectory)
.appendingPathComponent(name ?? "asset#\(UUID.init().uuidString)")
else {
return nil
}
do {
try self.pngData()?.write(to: imageFilePath)
return CKAsset(fileURL: imageFilePath)
} catch {
print("Error converting UIImage to CKAsset!")
}
return nil
}
}
You can then use it as you have it in your question:
if let asset = ImageToSave.toCKAsset() {
newRecord.setObject(asset, forKey: "Image")
CKContainer.defaultContainer().publicCloudDatabase.saveRecord(newRecord, completionHandler: {
record, error in
if error != nil {
println("\(error)")
} else {
// saved successfully!
}
})
}
This answer works with Swift 2.2 & iOS 9, and separates the file creation from the upload so that you can properly test against both, since they are distinct actions with their own potential issues.
For the uploadPhoto function, the recordType variable is the value you use in your CloudKit dashboard. The "photo" key in the photo["photo"] = asset line is the field name for your record type.
func uploadPhoto(image: UIImage, recordName: String) {
let privateDB = CKContainer.defaultContainer().privateCloudDatabase
let photoID = CKRecordID(recordName: recordName)
let photo = CKRecord(recordType: recordType, recordID: photoID)
let asset = CKAsset(fileURL: writeImage(image))
photo["photo"] = asset
privateDB.saveRecord(photo) { (record, error) in
guard error == nil else {
print(error?.localizedDescription)
return
}
print("Successful")
}
}
func writeImage(image: UIImage) -> NSURL {
let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
let fileURL = documentsURL.URLByAppendingPathComponent(NSUUID().UUIDString + ".png")
if let imageData = UIImagePNGRepresentation(image) {
imageData.writeToURL(fileURL, atomically: false)
}
return fileURL
}
You can call this with the following:
uploadPhoto(UIImage(named: "foo.png")!, recordName: "bar")
You'll want to pick the Asset value type in the dashboard for this value.
newRecord.setValue(ImageToSave, forKey: "Image")
UIImage is not an allowed type on CKRecord. Your best option is to write this image out to a file, then create a CKAsset and set that on the record.

Playing Audio File from the directory in swift

I am trying to save a audio file from a server to the users phone so they dont have to download it again its always there. I have almost everything but i am trying to test and see if the audio file actually plays after it is saved. How do i do this?
Code i have:
var urlWebView = NSURL(string: "http://domain.com//////audios////Nightmares.wav")
var requestWebView = NSURLRequest(URL: urlWebView)
NSURLConnection.sendAsynchronousRequest(requestWebView, queue: NSOperationQueue.mainQueue(), completionHandler: {
response, data, error in
if error != nil {
println("There was an error")
} else {
let musicFile = (data: data)
var documentsDirectory:String?
var paths:[AnyObject] = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
if paths.count > 0 {
documentsDirectory = paths[0] as? String
var savePath = documentsDirectory! + "/audio.wav"
NSFileManager.defaultManager().createFileAtPath(savePath, contents: data, attributes: nil)
self.audioPlayer = AVAudioPlayer(contentsOfURL: savePath, error: nil)
//tried to play it here but i cant since savePath is a string and not actually audio file
}
}
})
import UIKit
import Foundation
import AVFoundation
class ViewController: UIViewController {
#IBOutlet weak var strFiles: UITextView!
var myPlayer = AVAudioPlayer()
var yourSound:NSURL?
func prepareYourSound(myData:NSData) {
myPlayer = AVAudioPlayer(data: myData, error: nil)
myPlayer.prepareToPlay()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var urlWebView = NSURL(string: "http://freewavesamples.com/files/Korg-DS-8-Rotary-Organ-C6.wav")!
var requestWebView = NSURLRequest(URL: urlWebView)
NSURLConnection.sendAsynchronousRequest(requestWebView, queue: NSOperationQueue.mainQueue(), completionHandler: {
response, data, error in
if error != nil {
println("There was an error")
} else {
let musicFile = (data: data)
var documentsDirectory:String?
var paths:[AnyObject] = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
if paths.count > 0 {
documentsDirectory = paths[0] as? String
var savePath = documentsDirectory! + "/audio.wav"
NSFileManager.defaultManager().createFileAtPath(savePath, contents: data, attributes: nil)
self.prepareYourSound(musicFile)
self.myPlayer.play()
//tried to play it here but i cant since savePath is a string and not actually audio file
// list your files from disk (documents)
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
let files = NSFileManager().enumeratorAtPath(documentsPath)
var myFiles:[String] = []
while let file: AnyObject = files?.nextObject() {
myFiles.append(file as String)
self.strFiles.text = "\(self.strFiles.text)\n\(file as String)"
}
}
}
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Resources