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
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)")
}
This question already has answers here:
How to access file included in app bundle in Swift?
(8 answers)
File write with [NSBundle mainBundle] fails
(5 answers)
Closed 3 years ago.
I have a local json file, to read what I do as follows:
enter image description here
func readJson() {
if let path = Bundle.main.path(forResource: "text", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
let decoder = JSONDecoder()
let model = try decoder.decode(Textos.self, from: data)
print("model", model)
} catch {
print("error file")
}
} else {
print("error")
}
}
the read function works fine but when I want to update the file it doesn't update.
func saveJson(response: Textos) {
do {
let jsonEncoder = JSONEncoder()
let jsonData = try jsonEncoder.encode(response) //all fine with jsonData here
let json = String(data: jsonData, encoding: String.Encoding.utf8)
let data = Data(json!.utf8)
if let path = Bundle.main.path(forResource: "text", ofType: "json") {
do {
try? data.write(to: URL(fileURLWithPath: path)) **//not Working**
readJson()
}
}
} catch {
print(error)
}
}
the saveJson function works fine in a simulator but when I test it with a device it doesn't work.
Short answer:
You will never be able to save.
let path = Bundle.main.path(forResource: "text", ofType: "json")
The file is within the application bundle. Access is read only.
Saving would be possible to either the document or cache folder.
let documentPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
(Example how to request the path to the user document folder. This folder is per application sandbox.
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
I'm trying to share file originally generated in watch App Extension but it's not working. I am using iOS 11.3 and Watch 4.3. I am also using Simulator.
I have done like this
Enable App domain in Main app and Watch App Extension.
App domain in selected in both with identifier.
REF : https://www.techotopia.com/index.php/Sharing_Data_Between_a_WatchKit_App_and_the_Containing_iOS_App
Code added:
In Watch App Extension:
do {
let fileManager = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: AppDomain)
guard let url = fileManager?.appendingPathComponent("TabCareLogs.txt") else { return }
try SOME_STRING.appendLineToURL(fileURL: url)
print("WatchKit: FILE PATH \(url.path)")
let result = try String(contentsOf: url, encoding: .utf8)
debugPrint("############# FILE: \(result) #############")
} catch let error {
print("Could not write to file : \(error.localizedDescription)")
}
extension String {
func appendLineToURL(fileURL: URL) throws {
try (self + "\n").appendToURL(fileURL: fileURL)
}
func appendToURL(fileURL: URL) throws {
let data = self.data(using: .utf8)!
try data.append(fileURL: fileURL)
}
}
extension Data {
func append(fileURL: URL) throws {
if let fileHandle = FileHandle(forWritingAtPath: fileURL.path) {
defer {
fileHandle.closeFile()
}
fileHandle.seekToEndOfFile()
fileHandle.write(self)
}
else {
try write(to: fileURL, options: .atomic)
}
}
}
WatchKit: FILE PATH
/Users/abhishek/Library/Developer/CoreSimulator/Devices/D515C513-3975-4C8F-B07D-6A0CCCD1A8EC/data/Containers/Shared/AppGroup/90DC2A1A-61D6-440B-A499-9EA608CE0C2E/TabCareLogs.txt
I'm getting file as well as its content.
In Main App:
let fileManager = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: AppDomain)
guard let url = fileManager?.appendingPathComponent("TabCareLogs.txt") else { return nil }
print("FILE PATH \(url.path)")
print("FILE PATH ABSOLUTE \(url.absoluteString)")
let exists = FileManager.default.fileExists(atPath: url.path)
print("FILE EXIST : \(exists)")
FILE PATH
/Users/abhishek/Library/Developer/CoreSimulator/Devices/D5CED7C4-B674-4BF7-A72C-C063F733F4E7/data/Containers/Shared/AppGroup/77C1EAC2-2A86-4763-973F-33929C909619/TabCareLogs.txt
FILE PATH ABSOLUTE
file:///Users/abhishek/Library/Developer/CoreSimulator/Devices/D5CED7C4-B674-4BF7-A72C-C063F733F4E7/data/Containers/Shared/AppGroup/77C1EAC2-2A86-4763-973F-33929C909619/TabCareLogs.txt
FILE EXIST : false
I have tried to print the path I'm getting path bit if I'm checking file exist then its is showing false.
Same issue : NSFileManager.defaultManager().fileExistsAtPath returns false instead of true
Also the paths are different I don't know why it is going to different path after using shared container?