DatabaseConnecton issue in GRDB swift - ios

I would like to use SQLite database in my iOS app and there are different frameworks available. So my current focus is on GRDB and I have successfully created, inserted values, and received back with SQL commands as shown here.
However, once the app is closed, how am I going to access the same DataBaseQueue and what should be the constant path to my database?
import GRDB
// Open a simple database connection
if let dbQueue = try DatabaseQueue(path: "what should be the correct path ")!= nil{
setupDatabase()
}
else
{
dbQueue = DatabaseQueue()
}

The GRDB FAQ answers your question:
How do I create a database in my application?
The database has to be stored in a valid place where it can be created and modified. For example, in the Application Support directory:
let databaseURL = try FileManager.default
.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("db.sqlite")
let dbQueue = try DatabaseQueue(path: databaseURL.path)

Related

When exporting core data to CSV, how do I also save an image?

I have an entity named Item. I have an export function that exports all Items to CSV. So name, weight, quantity and etc is all exported correctly. The purpose of this is to save the data so that it may be imported later if all the data was deleted. One of the attributes of Items is a picture that the user chooses from its own library. How do I export that picture, so that it can be reimported later?
This is on iOS using the latest swift and Xcode.
I know I have not included any code, I am mainly asking for a direction to look. I'm not sure if I can get the location of the image on the device and then save that to the CSV or if there's a similar way. Thank you!
So I solved this problem using the code below but I may have created a new one. I'll be posting a new question to better clarify
The code below allowed me to save to Documents which allowed me to export and import the images.
func saveImage(image: UIImage, string: String) -> Bool {
guard let data = image.jpegData(compressionQuality: 1) ?? image.pngData() else {
return false
}
guard let directory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) as NSURL else {
return false
}
do {
print(string)
try data.write(to: directory.appendingPathComponent(string)!)
print("Success - \(string)")
return true
} catch {
print(error.localizedDescription)
return false
}
}
func getSavedImage(named: String) -> UIImage? {
if let dir = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) {
return UIImage(contentsOfFile: URL(fileURLWithPath: dir.absoluteString).appendingPathComponent(named).path)
}
return nil
}
CSV is a text based file format but Images are binary data. So the two does not mix well.
One thing you can do is convert the image to Base64 String and insert that string to the CSV. But the string would be too large and there may be consequences.
If you are using the same device (probably not) you can get the path of the image and append it to the CSV.
If you are using a DB simply upload your images to it and add the path to the CSV. (you can even upload the images to your drive and add the path)
There may be other ways also.

User's document directory returning nil with FileManager

I'm trying to write a file locally but no success. When I try to get the user's document directory it returns nil and I believe this is why my file is not been stored.
Also, I have many doubts of what the "user's document directory" is supposed to mean. Is it the "Documents/" inside "iCloud Drive" or "on my phone". Should I be looking in another place instead of "Files" app? I'm using the iPhone simulator.
My code is designed as follow. documentFolderURL, fileURL and url are all nil when debugging.
let documentFolderURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last
let ext: String = type ?? "pdf"
let name = "extrato." + ext
let fileURL = documentFolderURL?.appendingPathComponent(name)
do {
if let url = fileURL {
try file.write(to: url, options: .atomic)
}
} catch {...}
Use the throwing API to get an error (there should be none)
do {
let documentFolderURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let ext: String = type ?? "pdf"
let fileURL = documentFolderURL.appendingPathComponent(name).appendingPathExtension(ext)
try file.write(to: fileURL, options: .atomic)
} catch { print(error) }
It seems that you are creating the file successfully, but you aren't looking for it in the right place.
You can navigate to the simulator's User Defaults folder by:
Print the file path of the simulator's documents directory. print(documentFolderURL) should print something like file:///Users/yourname/Library/Developer/CoreSimulator/Devices/8DAF542C-4B37-41D1-BA43-1D7C2A32E585/data/Containers/Data/Application/63545C94-56F5-3B11-B601-543801BE717A/Documents/
Copy the entire url EXCEPT the leading file:// (in other words, start with /User/yourname...
Open your macbook's Finder app, and press command + shift + g. This will allow you to...(drum roll please)...
Paste in the url to navigate to your simulator's documents directory.
Your file should be there :)

Where is a file stored in .documentDirectory?

I wrote a file in .documentDirectory in .userDomainMask:
do {
let fileManager = FileManager.default
let docs = try fileManager.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil, create: false)
let path = docs.appendingPathComponent("myName.txt")
let data = "Hai...!".data(using: .utf8)!
fileManager.createFile(atPath: path.absoluteString, contents: data, attributes: nil)
} catch {
// handle error
}
I have not gotten any errors or exceptions. It runs perfectly. But I can't see that file. Where can I find that file?
just add in target -> info -> custom iOS Target properties
Application supports iTunes file sharing - YES
and you will be able to see the folder you saved a file in Files app on your simulator.
Otherwise use print(FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)) to get a path to folder using Finder

Saving database changes into .db file with SQLite (Swift)

In my Project I am opening an already existing database from a .db with sqlite3_open. This file was created with the command-line tool sqlite3. I am updating some rows in a table, which works fine, but those updates are only made in the temporary instance of the database. How can I also update my .db file for future runs of my project to work with updated data?
Somebody asked here how to save a SQLite database in Swift. But the answer seems to me somehow unsatisfying, because the file should be written in the same format as created by sqlite3, so that it can be opened again via sqlite3_open.
Is there maybe even a function presented by SQLite? I couldn't find anything for that...
You cannot modify the contents of the bundle. So you will want to copy the file to some a directory where it can be modified. Historically we might have advised using “documents” folder for this. But nowadays we use the “application support directory”. See iOS Storage Best Practices.
Anyway, the basic idea is:
include the original database in the bundle (by adding it to your app’s target, if you haven’t already);
try to open the database (with SQLITE_READWRITE option but not the SQLITE_CREATE option ... we don’t want it creating a blank database if it doesn’t find it) in the application support directory; and
if that fails (because the file is not found), copy the database from the bundle to the application support directory and try again
Thus, perhaps something like:
var db: OpaquePointer?
enum DatabaseError: Error {
case bundleDatabaseNotFound
case sqliteError(Int32, String?)
}
func openDatabase() throws {
let fileURL = try FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("database.db")
// try opening database and just return if it succeeded
if sqlite3_open_v2(fileURL.path, &db, SQLITE_OPEN_READWRITE, nil) == SQLITE_OK {
return
}
// if it failed, clean up before trying again ...
sqlite3_close(db)
db = nil
// then copy database from bundle ...
guard let bundlePath = Bundle.main.url(forResource: "database", withExtension: "db") else {
throw DatabaseError.bundleDatabaseNotFound
}
try FileManager.default.copyItem(at: bundlePath, to: fileURL)
// and try again
let rc = sqlite3_open_v2(fileURL.path, &db, SQLITE_OPEN_READWRITE, nil)
if rc == SQLITE_OK { return }
// and if it still failed, again, clean up, and then throw error
let errorMessage = sqlite3_errmsg(db)
.flatMap { String(cString: $0) }
sqlite3_close(db)
db = nil
throw DatabaseError.sqliteError(rc, errorMessage)
}

How to correctly reference/retrieve a temp file created in AppData for file upload to a server?

So the app I'm making creates a file called "logfile" and I'm trying to send that file via Alamofire upload to a server. The file path printed in the console log is
/var/mobile/Containers/Data/Application/3BE13D78-3BF0-4880-A79A-27B488ED9EFE/Documents/logfile.txt
and the file path I can use to manually access the log created in the .xcappdata is
/AppData/Documents/logfile.txt
To access it, I'm using
let fileURL = Bundle.main.url(forResource: "", withExtension: "txt")
where inbetween the double quotes for "forResource", I've tried both file paths I listed in the previous paragraph as well as just the file name but I'm getting a nil value for file found for either. The file isn't recognized to be there, presumably because the file path I'm using is wrong as Alamofire is returning nil when trying to locate send the file. Anyone know the direct file path I'm supposed to use to be able to grab my file since the other two don't supposedly work? Thank you!
Use below code to get string data from text file to upload to server:
let fileName = "logfile"
let documentDirURL = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let fileURL = documentDirURL.appendingPathComponent(fileName).appendingPathExtension("txt")
print("FilePath: \(fileURL.path)")
var readString = "" // Used to store the file contents
do {
// Read the file contents
readString = try String(contentsOf: fileURL)
} catch let error as NSError {
print("Failed reading from URL: \(fileURL), Error: " + error.localizedDescription)
}
print("File Text: \(readString)") // Send 'readString' to server
If you're dynamically creating the file at runtime, it won't be in your app bundle so the Bundle class won't be able to find it. The directories you see are also dynamically-generated and not only platform-specific, but also device-specific, so you can't use the file paths directly. Instead, you'll have to ask for the proper directory at runtime from the FileManager class, like this:
guard let documents = FileManager.default.urls(for: .documentsDirectory, in: .userDomainMask).first else{
// This case will likely never happen, but forcing anything in Swift is bad
return
}
let logURL = URL(string: "logfile.txt", relativeTo: documents)
do{
let fileContents = String(contentsOf: logURL)
// Send your file to your sever here
catch{
// Handle any errors you might've encountered
}
Note that I'm guessing based on the paths you pasted in your answer you put it in your application's documents directory. That's a perfectly fine place to put this type of thing, but if I'm wrong and you put it in a different place, you'll have to modify this code to point to the right place

Resources