Hello to every awesome developer in the entire world!
What I'am trying to do is copying a database file from main application bundle to the IOS document folder, the code seems fine but it always fail to copy the file, the destination file always has zero byte !
so, what went wrong is the following code!
Swift 5
private func moveDbFile() {
let fileManager = FileManager.default
let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
print(documentsDirectory)
guard let sourcePath = Bundle.main.path(forResource: "database", ofType: "db") else {
return
}
if fileManager.fileExists(atPath: sourcePath) {
let sourceUrl = URL(fileURLWithPath: sourcePath)
let destination = documentsDirectory.appendingPathComponent("database.db", isDirectory: false)
try? fileManager.copyItem(at: sourceUrl, to: destination)
if fileManager.fileExists(atPath: destination.path) {
print("file copied")
} else {
print("file copy failed")
}
}
}
Fixed by add database file to project target membership
Related
I'm working on an iOS application. In the app, I am using Alamofire to create a POST request that returns a raw PDF file in response. Right now, I am able to save the file and open it with UIDocumentInteractionController. But, I want the file to stay in User's documents folder.
Here's how I create the destination path:
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent("Dividend Summary Report.pdf")
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
Someone please tell me what's wrong with my logic and what I can do to correct it.
Well you need to check the file status if it exists then read from documentDirectory else download the file.
Create function like this:
func checkIfFileExists(urlString: String) {
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = NSURL(fileURLWithPath: path)
let fileName = urlString
let fileManager = FileManager.default
let filePath = url.appendingPathComponent("\(fileName).pdf")?.path
print("filePath : \(String(describing: filePath))")
if fileManager.fileExists(atPath: filePath!) {
print("File exists")
} else {
print("File doesn't exists")
// set your download function here
}
}
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.
There are all sorts of sample code & questions on SO dealing with how to programmatically copy files in Obj-C from the app bundle to the application's sandboxed Documents folder (e.g. here, here, and here) when the application runs for the first time.
How do you do this in Swift?
You could use FileManager API:
Here's example with a function that copies all files with specified extension:
func copyFilesFromBundleToDocumentsFolderWith(fileExtension: String) {
if let resPath = Bundle.main.resourcePath {
do {
let dirContents = try FileManager.default.contentsOfDirectory(atPath: resPath)
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
let filteredFiles = dirContents.filter{ $0.contains(fileExtension)}
for fileName in filteredFiles {
if let documentsURL = documentsURL {
let sourceURL = Bundle.main.bundleURL.appendingPathComponent(fileName)
let destURL = documentsURL.appendingPathComponent(fileName)
do { try FileManager.default.copyItem(at: sourceURL, to: destURL) } catch { }
}
}
} catch { }
}
}
Usage:
copyFilesFromBundleToDocumentsFolderWith(fileExtension: ".txt")
For Swift 4.2:
Assuming the file in your App Bundle is called Some File.txt
In ViewDidLoad, add:
let docName = "Some File"
let docExt = "txt"
copyFileToDocumentsFolder(nameForFile: docName, extForFile: docExt)
and then create a function as follows:
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")
}
}
I'm trying to upload my sqlite database file into an application. I've been learning about the iOS file system, and I'm not completely sure how it works and got lost. This is what I would like to achieve, but not sure how:
I would like to have database on this location my-xcode-project-path/data/foo.sqlite. Now I'm running simulator for first time, and I would like to copy this database into simulator's Document directory.
In case if I had the application already installed, I would skip step 1. and copy database from bundle.
If I tried to run simulator, but don't have file available in bundle, I would like to keep that database.
Thank you in advance!!!
My code is looking like this:
func prepareDatabaseFile() -> String {
let fileName: String = "foo.sqlite"
let filemanager:FileManager = FileManager.default
let directory = filemanager.urls(for: .documentDirectory, in: .userDomainMask).first!
let newUrl = directory.appendingPathComponent(fileName)
let bundleUrl = Bundle.main.resourceURL?.appendingPathComponent(fileName)
// check bundle
if filemanager.fileExists(atPath: (bundleUrl?.path)!) {
print("bundle file exists!")
return (bundleUrl?.path)! //probably I need to copy from bundle to new app and return new url
// here check if file already exists on simulator, but not in bundle
} else if filemanager.fileExists(atPath: (newUrl.path)) {
print("prebuild file exists!")
return newUrl.path //no copy is needed
// finally, if nothing I need to copy local file
} else {
//todo copy local file from the path my-xcode-project-path/data/foo.sqlite
print("todo")
}
return fileName
}
With #Rob's help, I came to following solution which satisfied my requirement. Previously it was necessary to add sqlite file into xcode project, with proper target.
func prepareDatabaseFile() -> String {
let fileName: String = "foo.sqlite"
let fileManager:FileManager = FileManager.default
let directory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
let documentUrl= directory.appendingPathComponent(fileName)
let bundleUrl = Bundle.main.resourceURL?.appendingPathComponent(fileName)
// here check if file already exists on simulator
if fileManager.fileExists(atPath: (documentUrl.path)) {
print("document file exists!")
return documentUrl.path
else if fileManager.fileExists(atPath: (bundleUrl?.path)!) {
print("document file does not exist, copy from bundle!")
fileManager.copyItem(at:bundleUrl, to:documentUrl)
}
return documentUrl.path
}
self.copyfile(filename: "mydata.sqlite" )
return true
}
func copyfile(filename : String)
{
let dbpath : String = getpath(filename: filename as String)
let filemanager = FileManager.default
if filemanager.fileExists(atPath: dbpath)
{
let documentsURl = Bundle.main.resourceURL
let frompath = documentsURl?.appendingPathComponent(filename)
print(frompath)
}
}
func getpath(filename: String) -> String
{
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
print(documentsURL)
let fileURL = documentsURL.appendingPathComponent(filename as String)
print(fileURL.path)
return fileURL.path
}
I downloaded some PDF files in my app and want to delete these on closing the application.
For some reason it does not work:
Creating the file:
let reference = "test.pdf"
let RequestURL = "http://xx/_PROJEKTE/xx\(self.reference)"
let ChartURL = NSURL(string: RequestURL)
//download file
let documentsUrl = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first! as NSURL
let destinationUrl = documentsUrl.URLByAppendingPathComponent(ChartURL!.lastPathComponent!)
if NSFileManager().fileExistsAtPath(destinationUrl.path!) {
print("The file already exists at path")
} else {
// if the file doesn't exist
// just download the data from your url
if let ChartDataFromUrl = NSData(contentsOfURL: ChartURL!){
// after downloading your data you need to save it to your destination url
if ChartDataFromUrl.writeToURL(destinationUrl, atomically: true) {
print("file saved")
print(destinationUrl)
} else {
print("error saving file")
}
}
}
Then I want to call the test() function to remove the items, like this:
func test(){
let fileManager = NSFileManager.defaultManager()
let documentsUrl = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first! as NSURL
do {
let filePaths = try fileManager.contentsOfDirectoryAtPath("\(documentsUrl)")
for filePath in filePaths {
try fileManager.removeItemAtPath(NSTemporaryDirectory() + filePath)
}
} catch {
print("Could not clear temp folder: \(error)")
}
}
This code works for me. I removed all the images that were cached.
private func test(){
let fileManager = NSFileManager.defaultManager()
let documentsUrl = NSFileManager.defaultManager().URLsForDirectory(.CachesDirectory, inDomains: .UserDomainMask).first! as NSURL
let documentsPath = documentsUrl.path
do {
if let documentPath = documentsPath
{
let fileNames = try fileManager.contentsOfDirectoryAtPath("\(documentPath)")
print("all files in cache: \(fileNames)")
for fileName in fileNames {
if (fileName.hasSuffix(".png"))
{
let filePathName = "\(documentPath)/\(fileName)"
try fileManager.removeItemAtPath(filePathName)
}
}
let files = try fileManager.contentsOfDirectoryAtPath("\(documentPath)")
print("all files in cache after deleting images: \(files)")
}
} catch {
print("Could not clear temp folder: \(error)")
}
}
**** Update swift 3 ****
let fileManager = FileManager.default
let documentsUrl = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! as NSURL
let documentsPath = documentsUrl.path
do {
if let documentPath = documentsPath
{
let fileNames = try fileManager.contentsOfDirectory(atPath: "\(documentPath)")
print("all files in cache: \(fileNames)")
for fileName in fileNames {
if (fileName.hasSuffix(".png"))
{
let filePathName = "\(documentPath)/\(fileName)"
try fileManager.removeItem(atPath: filePathName)
}
}
let files = try fileManager.contentsOfDirectory(atPath: "\(documentPath)")
print("all files in cache after deleting images: \(files)")
}
} catch {
print("Could not clear temp folder: \(error)")
}
I believe your problem is on this line:
let filePaths = try fileManager.contentsOfDirectoryAtPath("\(documentsUrl)")
You're using contentsOfDirectoryAtPath() with something that is an NSURL. You choose either path strings or URLs, not try to mix them both. To pre-empty your possible next question, URLs are preferred. Try using contentsOfDirectoryAtURL() and removeItemAtURL().
Another curious thing you should look at once you resolve the above: why are you using NSTemporaryDirectory() for the file path when you try to delete? You're reading the document directory and should use that.
Swift 5:
Check out the FileManager.removeItem() method
// start with a file path, for example:
let fileUrl = FileManager.default.urls(
for: .documentDirectory,
in: .userDomainMask
).deletingPathExtension()
.appendingPathComponent(
"someDir/customFile.txt",
isDirectory: false
)
// check if file exists
// fileUrl.path converts file path object to String by stripping out `file://`
if FileManager.default.fileExists(atPath: fileUrl.path) {
// delete file
do {
try FileManager.default.removeItem(atPath: fileUrl.path)
} catch {
print("Could not delete file, probably read-only filesystem")
}
}