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

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

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

Copy Database after starting swift

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

unable to overwrite the text file

I'm trying to overwrite the existing file everytime the application is started. I have tried two things both are not working.
1) I'm trying to create the file getting downloaded from online server with the same name but the data in the file is not getting updated, my code is..
let documentsUrl:URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL!
let destinationFileUrl = documentsUrl.appendingPathComponent("Splash.text")
let fileURL = URL(string:(defaults.object(forKey: "MainAddress") as! String).appending(filedownloadLink)
print("proper url = \(String(describing: fileURL))")
let sessionConfig = URLSessionConfiguration.default
let session1 = URLSession(configuration: sessionConfig)
print("splash file url \(destinationFileUrl)")
let request = URLRequest(url:fileURL!)
let task1 = session1.downloadTask(with: request) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
// Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Successfully downloaded. Status code: \(statusCode)")
}
do {
try FileManager.default.copyItem(at: tempLocalUrl, to: destinationFileUrl)
let fileManager = FileManager.default
// Check if file exists
} catch (let writeError) {
print("Error creating a file \(destinationFileUrl) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription);
}
}
task1.resume()
2) then i tried checking for the file before creating it, and if it exists then i'm deleting it. but once the file is deleted it is not getting opened with an error saying file "splash.text" doesnot exist. below is the code
do{
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = URL(fileURLWithPath: path)
let filePath = url.appendingPathComponent("Splash.text").path
let fileManager1 = FileManager.default
if fileManager1.fileExists(atPath: filePath) {
print("FILE AVAILABLE")
try fileManager1.removeItem(atPath: filePath)
} else {
print("FILE NOT AVAILABLE")
}
}
catch let error as NSError {
print("An error took place: \(error)")
}
and after this code I'm calling the code of method 1. I'm not sure why it is not getting created again for 2nd method, or why it is not overwritting it in 1st.

Keep Downloaded File (Cache) For The Next Time Application Open

I download external pdf file using this Alamofire way.
The problem is, I would like to keep it for the next time user open the app. So the user does not need to download the pdf again.
I use the following method to download
let destination =
Alamofire.Request.suggestedDownloadDestination(directory: .CachesDirectory,
domain: .UserDomainMask);
Alamofire.download(.GET, urlString, destination: destination)
.progress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
}
.response { request, response, _, error in
print("Downloaded to \(destination(NSURL(string: "")!, response!))");
}
The downloadedFilePath is something like this.
file:///var/mobile/Containers/Data/Application/4957AD15-947A-47D6-A126-EA06A5BCB099/Library/Caches/RewardMe-Presentation-at-NVIDIA-Auditorium.pdf
How do I keep the file for the next time my app launches?
I saved that path into NSUserDefaults the next time the app launches but the file is already gone.
There is a couple of functions add them into you code and call the copyFile where you are showing the path or after that.
func getPath(fileName: String) -> String {
let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
let fileURL = documentsURL.URLByAppendingPathComponent(fileName)
print("File Path Is : \(fileURL)")
return fileURL.path!
}
func copyFile(fileName: NSString , filePath : NSString) {
let dPath: String = getPath(fileName as String)
let fileManager = NSFileManager.defaultManager()
if !fileManager.fileExistsAtPath(dPath) {
let fromPath : NSURL = NSURL.fileURLWithPath(filePath);
var error : NSError?
do {
try fileManager.copyItemAtPath(fromPath.path!, toPath: dPath)
} catch let error1 as NSError {
error = error1
}
let alert: UIAlertView = UIAlertView()
if (error != nil) {
alert.title = "Error Occured"
alert.message = error?.localizedDescription
} else {
alert.title = "Successfully Copy"
alert.message = "Your File copy successfully"
}
alert.delegate = nil
alert.addButtonWithTitle("Ok")
alert.show()
}
}
copyFile two parameter one is file name for the file you want to save and second is where the file located.
And for checking what to do next time when application open. First get file path and check the file exist or not. If exist the d'not download the file otherwise download file.
let filePath: String = getPath("testFile.pdf")
let fileManager = NSFileManager.defaultManager()
if fileManager.fileExistsAtPath(dPath) {
//Code for using the file data
}
else {
//Download code for the file
}
Hope it helps :)
HTTP already has a caching mechanism, you could consider using it. Don't re-invent the wheel. Here's a way to do it.

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.

Resources