Copy Database after starting swift - ios

this is my first DB Project, so i am facing some issues. Hopefully you can help me!
I am using FMDB to have access to an existing DB. When i try to execute a simpel Query like "Select * From films" it returns stuff like "no such table". I looked into the folder of the IPhone Simulator and found the DB, but it was empty. My next Step was, to include this Method:
func copyDatabaseIfNeeded() {
// Move database file from bundle to documents folder
let fileManager = FileManager.default
let documentsUrl = fileManager.urls(for: .documentDirectory,
in: .userDomainMask)
guard documentsUrl.count != 0 else {
return // Could not find documents URL
}
let finalDatabaseURL = documentsUrl.first!.appendingPathComponent("foo.db")
if !( (try? finalDatabaseURL.checkResourceIsReachable()) ?? false) {
print("DB does not exist in documents folder")
let documentsURL = Bundle.main.resourceURL?.appendingPathComponent("foo.db")
do {
try fileManager.copyItem(atPath: (documentsURL?.path)!, toPath: finalDatabaseURL.path)
} catch let error as NSError {
print("Couldn't copy file to final location! Error:\(error.description)")
}
} else {
print("Database file found at path: \(finalDatabaseURL.path)")
}
}
But this Method is not working. Im calling it from DidFinishLaunching.
This is the error message:
OverBurned/Library/Developer/CoreSimulator/Devices/B5EAE004-A036-4BD5-A692-C25EF3875D25/data/Containers/Bundle/Application/5ABA8D38-7625-4F98-83E9-4266A3E5B6B0/GameOne.app/foo.db, NSUnderlyingError=0x600000053230 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
()
Am i using the wrong Method or is implemented wrong?

The error is clear. There is no foo.db in your app's resource bundle.
You do have lots of issues with the code you posted.
Your code to get the path to foo.db is far from ideal.
You do not properly deal with optionals.
Your variable names need to be improved. Example - the 2nd documentsURL implies it is a URL referencing the "Documents" folder. It is actually a URL to a file in the resource bundle.
There is no need for NSError.
Here is how I would write this code:
func copyDatabaseIfNeeded() {
// Move database file from bundle to documents folder
let fileManager = FileManager.default
guard let documentsUrl = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let finalDatabaseURL = documentsUrl.appendingPathComponent("foo.db")
do {
if !fileManager.fileExists(atPath: finalDatabaseURL.path) {
print("DB does not exist in documents folder")
if let dbFilePath = Bundle.main.path(forResource: "foo", ofType: "db") {
try fileManager.copyItem(atPath: dbFilePath, toPath: finalDatabaseURL.path)
} else {
print("Uh oh - foo.db is not in the app bundle")
}
} else {
print("Database file found at path: \(finalDatabaseURL.path)")
}
} catch {
print("Unable to copy foo.db: \(error)")
}
}

Related

SwiftUI | File found on simulator but not on iOS device

I'm trying to create a connection between my program and a database located in /Documents of the app. When the code is built using on the simulator, it successfully opens the database; however, when I run the code on an iOS device, it can't find the file.
This is the code that I use:
let path = NSSearchPathForDirectoriesInDomains(
.documentDirectory, .userDomainMask, true
).first!
let db = try! Connection("\(path)/Database.db")
These are the contents of the variable path when I run the code on the simulator:
/Users/xxxx/Library/Developer/CoreSimulator/Devices/EFD14A1B-7207-4840-9ACE-8E44A269CC70/data/Containers/Data/Application/58D150B9-E242-4857-B06C-DA28C88A26D0/Documents
And these are the contents of the variable path when I run the code on an iOS device:
/var/mobile/Containers/Data/Application/4EC93D76-4E99-4552-855A-48C1D9346449/Documents
Xcode version: 12.0
iOS device: iPhone X
iOS version: 14.1
Edit
I tried to use this code to copy the database from the app bundle to the document directory, but it gives the error Unable to copy file:
copyFileToDocumentsFolder(nameForFile: "Database", extForFile: "db")
func copyFileToDocumentsFolder(nameForFile: String, extForFile: String) {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let destURL = documentsURL.appendingPathComponent(nameForFile).appendingPathExtension(extForFile)
guard let sourceURL = Bundle.main.url(forResource: nameForFile, withExtension: extForFile)
else {
print("Source File not found.")
return
}
let fileManager = FileManager.default
do {
try fileManager.copyItem(at: sourceURL, to: destURL)
} catch {
print("Unable to copy file")
}
}
You can use url or path: with this codes you get the address of your data base, but you have to have your database file already there when your app use this address to find your databace! if you use this address, without having real file there, your app will crash. So if you have problem about copying your databace file let me know!
Version: 1.0.0
let appBaseURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].standardizedFileURL
let databaseURL = appBaseURL.appendingPathComponent("Database").appendingPathExtension("db").standardizedFileURL
let databasePath = databaseURL.path
Update Version: 2.0.0
This codes down are the most simplest and cleanest code about copy and paste on planet Earth for Swift and SwiftUI! You can not find better than this:
// If you want see your file in device or give user access to the file do this 2 steps:
// 1 - add this one ("Application supports iTunes file sharing" -> Yes) from (info.plist)
// 2 -add this one ("Supports opening documents in place" -> Yes) from (info.plist)
let fileName = "omid"
let fileExtension = "jpeg"
let appBaseURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].standardizedFileURL
let pasteFileURL = appBaseURL.appendingPathComponent(fileName).appendingPathExtension(fileExtension).standardizedFileURL
let copyFileURL = Bundle.main.bundleURL.appendingPathComponent(fileName).appendingPathExtension(fileExtension).standardizedFileURL
if FileManager.default.fileExists(atPath: appBaseURL.path)
{
print("appBaseFolder already exists!")
}
else
{
do{ try FileManager.default.createDirectory(at: appBaseURL, withIntermediateDirectories: true, attributes: nil); print("appBaseFolder successfully created!") }
catch{ print("Error in creating appBaseFolder!") }
}
if FileManager.default.fileExists(atPath: pasteFileURL.path)
{
print("The selected file already exists!")
}
else
{
do{ try FileManager.default.copyItem(at: copyFileURL, to: pasteFileURL); print("The selected file successfully copied!") }
catch { print("Error with copying selected file!") }
}
Version: 3.0.0 (The GM Version)
//▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
// If you want see your file in device or give user access to the file do this 2 steps:
// 1 - add this one ("Application supports iTunes file sharing" -> Yes) from (info.plist)
// 2 - add this one ("Supports opening documents in place" -> Yes) from (info.plist)
//...........................................................
let fileName = "omid"
let fileExtension = "jpeg"
//...........................................................
let appBaseURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].standardizedFileURL
let pasteFileURL = appBaseURL.appendingPathComponent(fileName).appendingPathExtension(fileExtension).standardizedFileURL
let copyFileURL = Bundle.main.bundleURL.appendingPathComponent(fileName).appendingPathExtension(fileExtension).standardizedFileURL
//▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼
//...........................................................
if FileManager.default.fileExists(atPath: appBaseURL.path)
{
print("appBaseFolder already exists!")
}
else
{
do{ try FileManager.default.createDirectory(at: appBaseURL, withIntermediateDirectories: true, attributes: nil); print("appBaseFolder successfully created!") }
catch{ print("Error in creating appBaseFolder!") }
}
//...........................................................
//...........................................................
if FileManager.default.fileExists(atPath: copyFileURL.path)
{
//...........................................................
if FileManager.default.fileExists(atPath: pasteFileURL.path)
{
print("The selected file already exists!")
}
else
{
do{ try FileManager.default.copyItem(at: copyFileURL, to: pasteFileURL); print("The selected file successfully copied!") }
catch { print("Error with copying selected file!") }
}
//...........................................................
}
else
{
print("The selected file not exists for copy Action!")
}
//...........................................................
As I already mentioned in your last question the Bundle is read-only. you need to move/copy your database to another directory that you can read/write to it. If you don't want the user to have access to the file you should copy it to that application support directory:
extension URL {
static let database: URL = {
let applicationSupport = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
let bundleID = Bundle.main.bundleIdentifier ?? "company name"
let subDirectory = applicationSupport.appendingPathComponent(bundleID, isDirectory: true)
let destination = subDirectory.appendingPathComponent("Database.db")
if !FileManager.default.fileExists(atPath: destination.path) {
let source = Bundle.main.url(forResource: "Database", withExtension: "db")!
do {
try FileManager.default.createDirectory(at: subDirectory, withIntermediateDirectories: true, attributes: nil)
print("directory created")
try FileManager.default.copyItem(at: source, to: destination)
print("file copied successfully")
} catch {
print("Unable to copy file. return the bundle read-only version")
return source
}
}
print("database found return app suport database read-write url")
return destination
}()
}
let dbURL = URL.database
The Mac (and by extension the Simulator) has a case-insensitive file system. iOS has a case-sensitive file system. If it's working on the simulator, but not the device, I expect the capitalization of your database filename is incorrect. The most likely mistake would be that it's database.db rather than Database.db.

iOS file path is changing at every launch/rerun the application

I have an issues in changing the file path at every launch of the app.
I have a file("AppConstant.json") in application bundle, and this file I need to copy into application document directory. I am successfully saving "AppConstant.json" file inside the created user folder "MyFolder" on Document directory.
But the problem is when I relaunch the application second time, it's not showing the same path. Also I am using relativepath, but still it not getting.
here is the code
// calling the directory
let stringAppConstant = copyFileFromBundleToDocumentDirectory(resourceFile: "AppConstant", resourceExtension: "json")
// saving or get exit file path
func copyFileFromBundleToDocumentDirectory(resourceFile: String, resourceExtension: String) -> String
{
var stringURLPath = "Error_URLPath"
let fileManager = FileManager.default
let docURL = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let destFolderPath = URL(string:docURL)?.appendingPathComponent("MyFolder")
let fileName = "\(resourceFile).\(resourceExtension)"
guard let newDestPath = destFolderPath, let sourcePath = Bundle.main.path(forResource: resourceFile, ofType: ".\(resourceExtension)"), let fullDestPath = NSURL(fileURLWithPath: newDestPath.absoluteString).appendingPathComponent(fileName) else {
return stringURLPath
}
if !fileManager.fileExists(atPath: newDestPath.path) {
do {
try fileManager.createDirectory(atPath: newDestPath.path,withIntermediateDirectories: true, attributes: nil)
print("Created folder successfully in :::", newDestPath.path)
} catch {
print("Error in creating folder :::",error.localizedDescription);
}
}
else {
print("Folder is already exist!")
}
if fileManager.fileExists(atPath: fullDestPath.path) {
print("File is exist in ::: \(fullDestPath.path)")
stringURLPath = fullDestPath.path
}
else {
do {
try fileManager.copyItem(atPath: sourcePath, toPath: fullDestPath.path)
print("Saved file successfully in :::", fullDestPath.path)
stringURLPath = fullDestPath.path
} catch {
print("Error in creating file ::: \(error.localizedDescription)")
}
}
return stringURLPath
}
Please help me, where I need to save the path in Sandbox. Is this right way what I implemented.
I am running in device and simulator, both path are different while relaunch
this is the path for first time launch:
/var/mobile/Containers/Data/Application/81B568A7-0932-4C3E-91EB-9DD62416DFE8/Documents/MyFolder/AppConstant.json
relaunch the application I am getting new path:
/var/mobile/Containers/Data/Application/3DAABAC3-0DF5-415B-82A5-72B204311904/Documents/MyFolder/AppConstant.json
NOTE: I create a sample project and I use this same code and it's working. But in existing project it's not working. I am using the same bundle id and profile only for both sample and project. Checked the file added reference, settings, version all are same.
Any idea?
The behavior that the container path changes periodically is normal.
These lines
let destFolderPath = URL(string:docURL)?.appendingPathComponent("MyFolder")
let fileName = "\(resourceFile).\(resourceExtension)"
guard let newDestPath = destFolderPath, let sourcePath = Bundle.main.path(forResource: resourceFile, ofType: ".\(resourceExtension)"), let fullDestPath = NSURL(fileURLWithPath: newDestPath.absoluteString).appendingPathComponent(fileName) else {
return stringURLPath
}
contain a lot of mistakes
URL(string is the wrong API for file paths, it's URL(fileURLWithPath).
The second parameter of path(forResource:ofType:) must not have a leading dot.
The API absoluteString is wrong as parameter of URL(fileURLWithPath
Not a real mistake but don't use NSURL in Swift.
It's highly recommended to use always the URL related API to concatenate paths and get the documents folder from FileManager. Further it's good practice to make the method throw the real error rather than returning a meaningless literal string. And NSSearchPathForDirectoriesInDomains is outdated and should not be used in Swift.
func copyFileFromBundleToDocumentDirectory(resourceFile: String, resourceExtension: String) throws -> URL
{
let sourceURL = Bundle.main.url(forResource: resourceFile, withExtension: resourceExtension)!
let fileManager = FileManager.default
let destFolderURL = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("MyFolder")
let fullDestURL = destFolderURL.appendingPathComponent(resourceFile).appendingPathExtension(resourceExtension)
if !fileManager.fileExists(atPath: destFolderURL.path) {
try fileManager.createDirectory(at: destFolderURL, withIntermediateDirectories: true, attributes: nil)
print("Created folder successfully in :::", destFolderURL.path)
try fileManager.copyItem(at: sourceURL, to: fullDestURL)
print("Saved file successfully in :::", fullDestURL.path)
} else {
print("Folder already exists!")
if fileManager.fileExists(atPath: fullDestURL.path) {
print("File exists in ::: \(fullDestURL.path)")
} else {
try fileManager.copyItem(at: sourceURL, to: fullDestURL)
print("Saved file successfully in :::", fullDestURL.path)
}
}
return fullDestURL
}
Edit 1:
Hi I created the new project and use the same code I posted in main, and it's working. But in the real project it not working.
Not sure what exactly going on in your project, try to debug it. It's part of development as well. :)
If you are in hurry to fix this issue in this weekend try to use the following code snippet.
// collect data from bundle
let constFileURL = Bundle.main.url(forResource: "AppConst", withExtension: "json")!
let data = try! Data(contentsOf: constFileURL)
// try to write data in document directory
do {
let constFileURL = try saveFileInDocumentDirectory(filePath: "MyFolder/AppConst.json", data: data)
// use your `constFileURL`
} catch (let error as FileOperationError) {
switch error {
case .fileAlreadyExists(let url):
let data = try! Data(contentsOf: url)
print(String(data: data, encoding: .utf8))
case .IOError(let error):
print("IO Error \(error)")
}
} catch {
print("Unknown Error \(error)")
}
// Helpers
enum FileOperationError: Error {
case fileAlreadyExists(url: URL)
case IOError(Error)
}
func saveFileInDocumentDirectory(filePath: String, data: Data) throws -> URL {
// final destination path
let destURLPath = fullURLPathOf(filePath, relativeTo: .documentDirectory)
// check for file's existance and throw error if found
guard FileManager.default.fileExists(atPath: destURLPath.path) == false else {
throw FileOperationError.fileAlreadyExists(url: destURLPath)
}
// Create Intermidiate Folders
let intermidiateDicPath = destURLPath.deletingLastPathComponent()
if FileManager.default.fileExists(atPath: intermidiateDicPath.path) == false {
do {
try FileManager.default.createDirectory(at: intermidiateDicPath, withIntermediateDirectories: true, attributes: nil)
} catch {
throw FileOperationError.IOError(error)
}
}
// File Writing
do {
try data.write(to: destURLPath, options: .atomic)
} catch {
throw FileOperationError.IOError(error)
}
return destURLPath
}
func fullURLPathOf(_ relativePath: String, relativeTo dic:FileManager.SearchPathDirectory ) -> URL {
return FileManager.default.urls(for: dic, in: .userDomainMask).first!.appendingPathComponent(relativePath)
}
Original Answer
Why don't you just return "MyFolder/\(fileName)" on successful file operation? If you need to access the path later you can always do that using FileManager APIs.
let docDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let constFilePath = docDir.appendingPathComponent("MyFolder/\(fileName)")
// Access const file data
do {
let fileData = try Data(contentsOf: constFilePath)
// Use you data for any further checking
} catch {
// Error in reading file data
print("Error in file data access : \(error)")
}

How can i copy Folder from Project to Document Directory ios swift

Here is my Folder Structure
I want to copy this Stickers folder to Document Directory for this purpose i have used following code
let fileManager = FileManager.default
let documentsUrl = fileManager.urls(for: .documentDirectory,
in: .userDomainMask)
guard documentsUrl.count != 0 else {
return // Could not find documents URL
}
let finalDatabaseURL = documentsUrl.first!.appendingPathComponent("Stickers")
if !( (try? finalDatabaseURL.checkResourceIsReachable()) ?? false) {
print("DB does not exist in documents folder")
let documentsURL = Bundle.main.resourceURL?.appendingPathComponent("Stickers")
do {
try fileManager.copyItem(atPath: (documentsURL?.path)!, toPath: finalDatabaseURL.path)
} catch let error as NSError {
print("Couldn't copy file to final location! Error:\(error.description)")
}
} else {
print("Database file found at path: \(finalDatabaseURL.path)")
}
but it won't work. I am getting an Error as
NOTE: If Copying folder is not possible then i want to copy Assets.xcassets to Document Directory. How can i achieve it?
Please below code..
I update your code in two functions to copy all files from folder to document directory.
Hope it will work.
func copyFolders() {
let fileManager = FileManager.default
let documentsUrl = fileManager.urls(for: .documentDirectory,
in: .userDomainMask)
guard documentsUrl.count != 0 else {
return // Could not find documents URL
}
let finalDatabaseURL = documentsUrl.first!.appendingPathComponent("Stickers")
if !( (try? finalDatabaseURL.checkResourceIsReachable()) ?? false) {
print("DB does not exist in documents folder")
let documentsURL = Bundle.main.resourceURL?.appendingPathComponent("Stickers")
do {
if !FileManager.default.fileExists(atPath:(finalDatabaseURL?.path)!)
{
try FileManager.default.createDirectory(atPath: (finalDatabaseURL.path), withIntermediateDirectories: false, attributes: nil)
}
copyFiles(pathFromBundle: (documentsURL?.path)!, pathDestDocs: finalDatabaseURL.path)
} catch let error as NSError {
print("Couldn't copy file to final location! Error:\(error.description)")
}
} else {
print("Database file found at path: \(finalDatabaseURL.path)")
}
}
func copyFiles(pathFromBundle : String, pathDestDocs: String) {
let fileManagerIs = FileManager.default
do {
let filelist = try fileManagerIs.contentsOfDirectory(atPath: pathFromBundle)
try? fileManagerIs.copyItem(atPath: pathFromBundle, toPath: pathDestDocs)
for filename in filelist {
try? fileManagerIs.copyItem(atPath: "\(pathFromBundle)/\(filename)", toPath: "\(pathDestDocs)/\(filename)")
}
} catch {
print("\nError\n")
}
}

Having trouble saving a file in a folder inside Document directory in Swift 2

I am trying to save a file inside a subfolder in Documents directory but it won't save. I can't seem to find what's wrong. Here is what I've tried:
if let audioUrl = NSURL(string: "http://pillar.foundationu.com/wp-content/plugins/pillar-data-sync/php/htmlBreakdownResult.json") {
let documentsUrl = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
do {
let directoryContents = try NSFileManager.defaultManager().contentsOfDirectoryAtURL(documentsUrl, includingPropertiesForKeys: nil, options: NSDirectoryEnumerationOptions())
print(directoryContents[0].path!)
if NSFileManager().fileExistsAtPath(String(audioUrl.path!)) {
print("The file already exists at path")
} else {
// if the file doesn't exist
// just download the data from your url
if let myAudioDataFromUrl = NSData(contentsOfURL: audioUrl){
// after downloading your data you need to save it to your destination url
if myAudioDataFromUrl.writeToURL(NSURL(fileURLWithPath: directoryContents[0].path!), atomically: true) {
print("file saved")
} else {
print("error saving file")
}
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
Here is where I want to save the file:
/Users/rendell/Library/Developer/CoreSimulator/Devices/5A052CC5-FD34-44FD-B060-24D6F1970860/data/Containers/Data/Application/37753B0B-FAB0-478D-A7F8-98E3039D07DD/Documents/MyFolder2
But it keeps on giving me "error saving file".
First of all, use always writeToURL:options:error to get a (more) descriptive error message.
The issue is quite simple: You forgot to provide a file name.
Technically you're going to overwrite an existing folder with data. That's not possible.

Can't read from a file even though it is there - Swift

I am downloading a file to a destination, and I can confirm that the file is there, in fact I can check whether that file is read-only or writable, but I am getting an error when I try to show the context of this file : "Error Domain=NSCocoaErrorDomain Code=257 "The file “Documents” couldn’t be opened because you don’t have permission to view it. Anyone had this issue before? It's driving me nuts because I can't even track down the location on my computer because I am using my iPad to run the app. Would be grateful if someone could help me out.
Code snippet where I check for a file :
let documentsURL = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
let checkValidation = NSFileManager.defaultManager()
if (checkValidation.fileExistsAtPath(documentsURL))
{
print("FILE AVAILABLE");
}
else
{
print("FILE NOT AVAILABLE");
}
print(documentsDirectoryPath)
if checkValidation.isWritableFileAtPath(documentsURL) {
print("File is writable")
} else {
print("File is read-only")
}
var newarray = [Int]()
do{
let data = try String(contentsOfFile: documentsURL as String,
encoding: NSASCIIStringEncoding)
print(data)
print("i am at the do statement")
newarray = data.characters.split(){$0 == ","}.map{
Int(String.init($0).stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()))!}
print(newarray)
}
catch let error {
print(error)
}

Resources