unable to overwrite the text file - ios

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.

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

How to download and save an audio file and then play it in swift?

I am trying to download an audio file from the internet and save it onto the phone. This is the download function:
func download() {
if let audioUrl = downloadUrl {
// then lets create your document folder url
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
// lets create your destination file url
let destinationUrl = documentsDirectoryURL.appendingPathComponent(audioUrl.lastPathComponent)
print(destinationUrl)
// to check if it exists before downloading it
if FileManager.default.fileExists(atPath: destinationUrl.path) {
print("The file already exists at path")
// if the file doesn't exist
} else {
// you can use NSURLSession.sharedSession to download the data asynchronously
URLSession.shared.downloadTask(with: audioUrl, completionHandler: { (location, response, error) -> Void in
guard let location = location, error == nil else { return }
do {
// after downloading your file you need to move it to your destination url
try FileManager.default.moveItem(at: location, to: destinationUrl)
print("File moved to documents folder")
} catch let error as NSError {
print(error.localizedDescription)
}
}).resume()
}
}
}
Then, after I close and open the app, I use the following function to retrieve the url and play it using an AVPlayer:
func getUrl2() {
if let audioUrl = downloadUrl {
// then lets create your document folder url
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
destinationUrl = documentsDirectoryURL.appendingPathComponent(audioUrl.lastPathComponent)
if let u = self.destinationUrl {
let player = AVPlayer(url: u)
print(u)
print("Bouta play")
print(CMTimeGetSeconds(player.currentItem!.duration))
player.play()
}
}
}
The duration that keeps getting printed out is "nan". Is there a way to check if the audio file is actually downloading? Or could it be a problem with retrieving the file after the download? Thanks in advance.
First of all you have to check for the URL is not empty with the below logic:
if !link.isEmpty{
checkBookFileExists(withLink: link){ [weak self] downloadedURL in
guard let self = self else{
return
}
play(url: downloadedURL)
}
}
Then checkBookFileExists function will check if the file already saved or not before download it again:
func checkBookFileExists(withLink link: String, completion: #escaping ((_ filePath: URL)->Void)){
let urlString = link.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
if let url = URL.init(string: urlString ?? ""){
let fileManager = FileManager.default
if let documentDirectory = try? fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create: false){
let filePath = documentDirectory.appendingPathComponent(url.lastPathComponent, isDirectory: false)
do {
if try filePath.checkResourceIsReachable() {
print("file exist")
completion(filePath)
} else {
print("file doesnt exist")
downloadFile(withUrl: url, andFilePath: filePath, completion: completion)
}
} catch {
print("file doesnt exist")
downloadFile(withUrl: url, andFilePath: filePath, completion: completion)
}
}else{
print("file doesnt exist")
}
}else{
print("file doesnt exist")
}
}
Then if the file doesn't exists you will download it with the below function:
func downloadFile(withUrl url: URL, andFilePath filePath: URL, completion: #escaping ((_ filePath: URL)->Void)){
DispatchQueue.global(qos: .background).async {
do {
let data = try Data.init(contentsOf: url)
try data.write(to: filePath, options: .atomic)
print("saved at \(filePath.absoluteString)")
DispatchQueue.main.async {
completion(filePath)
}
} catch {
print("an error happened while downloading or saving the file")
}
}
}
That function will save it and you can play it with:
func play(url: URL) {
print("playing \(url)")
do {
audioPlayer = try AVAudioPlayer(contentsOf: url)
audioPlayer?.prepareToPlay()
audioPlayer?.delegate = self
audioPlayer?.play()
let percentage = (audioPlayer?.currentTime ?? 0)/(audioPlayer?.duration ?? 0)
DispatchQueue.main.async {
// do what ever you want with that "percentage"
}
} catch let error {
audioPlayer = nil
}
}

Try throwing an error, but why?

I'm trying to retrieve images from the documents directory in order to populate a collection view but my try block returns an error but I don't know what the error is or why it is occurring.
func refreshCollectionView() {
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = NSURL(fileURLWithPath: path)
let filePath = url.appendingPathComponent(imagesDirectoryPath)?.path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath!) {
print("FILE AVAILABLE")
do {
titles = try FileManager.default.contentsOfDirectory(atPath: imagesDirectoryPath)
print(titles.count)
for image in titles {
let data = FileManager.default.contents(atPath: imagesDirectoryPath + "/\(image)")
let image = UIImage(data: data!)
myImages.append(image!)
}
self.collectionView?.reloadData()
}
catch {
print("Error")
}
}
else {
print("FILE NOT AVAILABLE")
}
}
The line print(titles.count) is never executed and the error is caught but what is the error?
if fileManager.fileExists(atPath: filePath!) {
print("FILE AVAILABLE")
do {
// ...
} catch {
print("Error:", error)
}
}
This will show you actual error

Swift downloadTask with request file download not working

I'm trying to download a file from a server, by sending the id of the file. I tried several things but the file is getting downloaded as CFNetworkDownload.tmp file.
I want it to save as the file that exists. The file type can be PNG,JPEG,PDF,DOCX,PPTX,XLSX. Tried many things but in vain. I'm sure it must be something simple i'm missing to understand here
Tried the below. Difference being in most of the examples, the file name is in the URL. But I send id and get file in response.
How to download file in swift?
How To Download Multiple Files Sequentially using NSURLSession downloadTask in Swift
Below is my code.
func downloadFile(id : String, fileName : String) -> Void {
let session = URLSession.shared
let url = URL(string: qaDownloadURL+id)!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
// Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Success: \(statusCode)")
}
do {
// let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
//
// self.savePath = documentsUrl!.absoluteString + "/" + fileName
//
// let fileURL = URL(fileURLWithPath: self.savePath)
//
// let dataFromURL = NSData(contentsOf: tempLocalUrl)
// dataFromURL?.write(to: fileURL, atomically: true)
var documentsDirectory: String?
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
if paths.count > 0
{
documentsDirectory = paths.first!
}
self.savePath = documentsDirectory!// + "/" + fileName
let fileURL = URL(fileURLWithPath: self.savePath)
let dataFromURL = NSData(contentsOf: tempLocalUrl)
dataFromURL?.write(to: fileURL, atomically: true)
// try FileManager.default.copyItem(at: tempLocalUrl, to: fileURL)
DispatchQueue.main.async {
let documentController = UIDocumentInteractionController.init(url: fileURL)
documentController.delegate = self
documentController.presentPreview(animated: true)
}
} catch (let writeError) {
print("error writing file \(self.savePath) : \(writeError)")
}
} else {
print("Failure: %#", error?.localizedDescription);
}
}
task.resume()
}
You cannot write data into a location which represents a directory, you need to specify the full path including the file name.
Using modern URL related API you can replace the entire do block with
do {
let documentFolderURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let fileURL = documentFolderURL.appendingPathComponent(fileName)
try FileManager.default.copyItem(at: tempLocalUrl, to: fileURL)
DispatchQueue.main.async {
let documentController = UIDocumentInteractionController.init(url: fileURL)
documentController.delegate = self
documentController.presentPreview(animated: true)
}
}
or use URLSessionDataTask which returns the raw data rather than downloading the file to a temporary location and save the Data directly for example
let task = session.dataTask(with: request) { (data, response, error) in
guard error == nil else {
print(error!)
return
}
// Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Success: \(statusCode)")
}
do {
let documentFolderURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let fileURL = documentFolderURL.appendingPathComponent(fileName)
try data!.write(to: fileURL)
DispatchQueue.main.async {
let documentController = UIDocumentInteractionController.init(url: fileURL)
documentController.delegate = self
documentController.presentPreview(animated: true)
}
} catch {
print("error writing file \(fileName) : \(error)")
}
}
task.resume()
If this does not work the error is related to somewhere else.

How to copy remote server file in document directory in swift

I know how to get a remote URL in Swift
let remoteURL = NSURL(string: "https://myserver/file.txt")!
I know how to get a local URL in Swift
let localURL = NSURL(fileURLWithPath: documentsFolder + "/my_local_file.txt")
and unfortunately this does not work
NSFileManager.defaultManager().copyItemAtURL(remoteURL, toURL: localURL)
with the following error
The file “file.txt” couldn’t be opened because URL type https isn’t supported.
Is there a way how to perform this?
You can use NSURLSessionDownloadTask to download the file:
func downloadFile(url: URL) {
let downloadRequest = URLRequest(url: url)
URLSession.shared.downloadTask(with: downloadRequest) { location, response, error in
// To do check resoponse before saving
guard let tempLocation = location where error == nil else { return }
let documentDirectory = FileManager.default.urlsForDirectory(.documentDirectory, inDomains: .userDomainMask).last
do {
let fullURL = try documentDirectory?.appendingPathComponent((response?.suggestedFilename!)!)
try FileManager.default.moveItem(at: tempLocation, to: fullURL!)
print("saved at \(fullURL) ")
} catch NSCocoaError.fileReadNoSuchFileError {
print("No such file")
} catch {
// other errors
print("Error downloading file : \(error)")
}
}.resume()
}
let stringURL = "https://wordpress.org/plugins/about/readme.txt"
downloadImage(url: URL(string: stringURL)!)
Update: SWIFT 2.2
func downloadFile(url: NSURL) {
let downloadRequest = NSURLRequest(URL: url)
NSURLSession.sharedSession().downloadTaskWithRequest(downloadRequest){ (location, response, error) in
guard let tempLocation = location where error == nil else { return }
let documentDirectory = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first
let fullURL = documentDirectory?.URLByAppendingPathComponent((response?.suggestedFilename)!)
do {
try NSFileManager.defaultManager().moveItemAtURL(tempLocation, toURL: fullURL!)
} catch NSCocoaError.FileReadNoSuchFileError {
print("No such file")
} catch {
print("Error downloading file : \(error)")
}
}.resume()
}
let stringURL = "https://wordpress.org/plugins/about/readme.txt"
let url = NSURL.init(string: stringURL)
downloadFile(url!)
You should download it first, then save it to a local file.
Code example can be found here: (using AFNetworking)
How I properly setup an AFNetworking Get Request?

Resources