iOS: load file inside folder/directory from AppBundle - ios

I want to load a file from AppBundle. I am able to do this at top level ("/file.txt") with following code:
guard let path = Bundle.main.path(forResource: "file", ofType: "txt") else {
print("file not found")
return
}
But when I put the "file.txt" in a folder ("anyFolder") I do not know how to read the file from "/anyFolder/file.txt".
The shown code does not work with a nested structure.

You should use "url(forResource:withExtension:subdirectory:)"
Check Apple Document
guard let url = Bundle.main.url(forResource: "ios", withExtension: ".ovpn")
else{
print("error")
return
}
print("bundle",url.path)
Create urls of ios.file

Related

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)")
}

Downloaded PDF file but cannot locate it [duplicate]

I have an XML file in my Xcode Project, and I'm trying to first save it to disk, and secondly how can I tell if I've successfully saved it? Is this the proper approach? Using the simulator I navigated to the new "Files" folder in iOS 11 and I don't see it but I'm not sure if it should be there or not?
guard let path = Bundle.main.url(forResource: "sample", withExtension: "xml") else {print("NO URL"); return}
let sample = try? Data(contentsOf: path)
print("sample XML = \(String(describing: sample?.debugDescription))")
//put xml file on the device
let filename = getDocumentsDirectory().appendingPathComponent("sample.xml")
do {
try sample?.write(to: filename)
} catch {
print("ERROR")
}
updated to include my check if file exists:
//check if file exists
let checkPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = URL(fileURLWithPath: checkPath)
let filePath = url.appendingPathComponent("sample.xml").path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath) {
print("FILE AVAILABLE")
} else {
print("FILE NOT AVAILABLE")
}
You can use UIDocumentInteractionController and let the user select where he wants to save your file when you share your url. The user just needs to select save to files and choose which directory to save the file you are exporting.
You can use UIDocumentInteractionController to share any file type located inside your App bundle, at your Documents directory or another folder accessible from your App.
class ViewController: UIViewController {
let documentInteractionController = UIDocumentInteractionController()
func share(url: URL) {
documentInteractionController.url = url
documentInteractionController.uti = url.typeIdentifier ?? "public.data, public.content"
documentInteractionController.name = url.localizedName ?? url.lastPathComponent
documentInteractionController.presentOptionsMenu(from: view.frame, in: view, animated: true)
}
#IBAction func shareAction(_ sender: UIButton) {
guard let url = URL(string: "https://www.ibm.com/support/knowledgecenter/SVU13_7.2.1/com.ibm.ismsaas.doc/reference/AssetsImportCompleteSample.csv?view=kc") else { return }
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
let tmpURL = FileManager.default.temporaryDirectory
.appendingPathComponent(response?.suggestedFilename ?? "fileName.csv")
do {
try data.write(to: tmpURL)
DispatchQueue.main.async {
self.share(url: tmpURL)
}
} catch {
print(error)
}
}.resume()
}
}
extension URL {
var typeIdentifier: String? {
return (try? resourceValues(forKeys: [.typeIdentifierKey]))?.typeIdentifier
}
var localizedName: String? {
return (try? resourceValues(forKeys: [.localizedNameKey]))?.localizedName
}
}
edit/update:
If you would like to expose the files located on your App Bundle's document directory you can check this post How can I display my App documents in the Files app for iPhone
Add these two keys to 'info.plist' -
...
<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
...
After this, files saved in '.documentDirectory' will appear in 'Files App' inside a folder with your app name.
If editing 'info.plist' in Xcode then add below keys -
Application supports iTunes file sharing = YES
Supports opening documents in place = YES
info.plist

Xcode File not found error during parse XLSX

I am parsing XLXS file. when I execute code getting error
"Fatal error: XLSX file corrupted or does not exist".
I double checked test.xlsx file available inside project.what is wrong with the code. Any help will be appreicated
guard let file = XLSXFile(filepath: "./test.xlsx") else {
fatalError("XLSX file corrupted or does not exist")
}
do{
for path in try file.parseWorksheetPaths() {
let ws = try file.parseWorksheet(at: path)
for row in ws.data?.rows ?? [] {
for c in row.cells {
print(c)
}
}
}
}catch{
print("error", error)
}
filepath can't accessing from bundle directory, need to pass correct path:
guard let filePath = Bundle.main.path(forResource: "test", ofType: "xlsx", inDirectory: nil) else {
fatalError("XLSX file not exist")
}
guard let file = XLSXFile(filepath: filePath) else {
fatalError("XLSX file corrupted")
}
One more thing to need to check, while adding XLSX file to project it must added to target & copy to bundle directory:
You should get the filePath using the following :
if let path = Bundle.main.path(forResource: "test", ofType: "xlsx") {
//Use your file here
}
Try replacing
guard let file = XLSXFile(filepath: "./test.xlsx") else {
with
guard var file = XLSXFile(filepath: "./test.xlsx") else {
I do not have experience dealing with files but I hope this helps you

How to write a file to a folder located at Apple's Files App in Swift

I have an XML file in my Xcode Project, and I'm trying to first save it to disk, and secondly how can I tell if I've successfully saved it? Is this the proper approach? Using the simulator I navigated to the new "Files" folder in iOS 11 and I don't see it but I'm not sure if it should be there or not?
guard let path = Bundle.main.url(forResource: "sample", withExtension: "xml") else {print("NO URL"); return}
let sample = try? Data(contentsOf: path)
print("sample XML = \(String(describing: sample?.debugDescription))")
//put xml file on the device
let filename = getDocumentsDirectory().appendingPathComponent("sample.xml")
do {
try sample?.write(to: filename)
} catch {
print("ERROR")
}
updated to include my check if file exists:
//check if file exists
let checkPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = URL(fileURLWithPath: checkPath)
let filePath = url.appendingPathComponent("sample.xml").path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath) {
print("FILE AVAILABLE")
} else {
print("FILE NOT AVAILABLE")
}
You can use UIDocumentInteractionController and let the user select where he wants to save your file when you share your url. The user just needs to select save to files and choose which directory to save the file you are exporting.
You can use UIDocumentInteractionController to share any file type located inside your App bundle, at your Documents directory or another folder accessible from your App.
class ViewController: UIViewController {
let documentInteractionController = UIDocumentInteractionController()
func share(url: URL) {
documentInteractionController.url = url
documentInteractionController.uti = url.typeIdentifier ?? "public.data, public.content"
documentInteractionController.name = url.localizedName ?? url.lastPathComponent
documentInteractionController.presentOptionsMenu(from: view.frame, in: view, animated: true)
}
#IBAction func shareAction(_ sender: UIButton) {
guard let url = URL(string: "https://www.ibm.com/support/knowledgecenter/SVU13_7.2.1/com.ibm.ismsaas.doc/reference/AssetsImportCompleteSample.csv?view=kc") else { return }
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
let tmpURL = FileManager.default.temporaryDirectory
.appendingPathComponent(response?.suggestedFilename ?? "fileName.csv")
do {
try data.write(to: tmpURL)
DispatchQueue.main.async {
self.share(url: tmpURL)
}
} catch {
print(error)
}
}.resume()
}
}
extension URL {
var typeIdentifier: String? {
return (try? resourceValues(forKeys: [.typeIdentifierKey]))?.typeIdentifier
}
var localizedName: String? {
return (try? resourceValues(forKeys: [.localizedNameKey]))?.localizedName
}
}
edit/update:
If you would like to expose the files located on your App Bundle's document directory you can check this post How can I display my App documents in the Files app for iPhone
Add these two keys to 'info.plist' -
...
<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
...
After this, files saved in '.documentDirectory' will appear in 'Files App' inside a folder with your app name.
If editing 'info.plist' in Xcode then add below keys -
Application supports iTunes file sharing = YES
Supports opening documents in place = YES
info.plist

Realm iOS issue

I want to load prefilled Realm DB in my app. So what I do:
let bundlePath = Bundle.main.path(forResource: "default", ofType: "realm")
let destPath = Realm.Configuration.defaultConfiguration.fileURL?.path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: destPath!) {
//File exist, do nothing
print("File exists")
} else {
do {
//Copy file from bundle to Realm default path
try fileManager.copyItem(atPath: bundlePath!, toPath: destPath!)
print("Copied")
} catch {
print("\n",error)
}
}
This part always returns true, I get file exists even I reset simulator content and settings.
Second: when I hardly copy my realm db file without checking if file exists, this code
let realm = try! Realm()
var posts: Results<Post> {
get {
return realm.objects(Post.self)
}
}
returns empty posts on first run. When I run app second time I get prefilled db. What do I wrong?

Resources