How to correctly reference/retrieve a temp file created in AppData for file upload to a server? - ios

So the app I'm making creates a file called "logfile" and I'm trying to send that file via Alamofire upload to a server. The file path printed in the console log is
/var/mobile/Containers/Data/Application/3BE13D78-3BF0-4880-A79A-27B488ED9EFE/Documents/logfile.txt
and the file path I can use to manually access the log created in the .xcappdata is
/AppData/Documents/logfile.txt
To access it, I'm using
let fileURL = Bundle.main.url(forResource: "", withExtension: "txt")
where inbetween the double quotes for "forResource", I've tried both file paths I listed in the previous paragraph as well as just the file name but I'm getting a nil value for file found for either. The file isn't recognized to be there, presumably because the file path I'm using is wrong as Alamofire is returning nil when trying to locate send the file. Anyone know the direct file path I'm supposed to use to be able to grab my file since the other two don't supposedly work? Thank you!

Use below code to get string data from text file to upload to server:
let fileName = "logfile"
let documentDirURL = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let fileURL = documentDirURL.appendingPathComponent(fileName).appendingPathExtension("txt")
print("FilePath: \(fileURL.path)")
var readString = "" // Used to store the file contents
do {
// Read the file contents
readString = try String(contentsOf: fileURL)
} catch let error as NSError {
print("Failed reading from URL: \(fileURL), Error: " + error.localizedDescription)
}
print("File Text: \(readString)") // Send 'readString' to server

If you're dynamically creating the file at runtime, it won't be in your app bundle so the Bundle class won't be able to find it. The directories you see are also dynamically-generated and not only platform-specific, but also device-specific, so you can't use the file paths directly. Instead, you'll have to ask for the proper directory at runtime from the FileManager class, like this:
guard let documents = FileManager.default.urls(for: .documentsDirectory, in: .userDomainMask).first else{
// This case will likely never happen, but forcing anything in Swift is bad
return
}
let logURL = URL(string: "logfile.txt", relativeTo: documents)
do{
let fileContents = String(contentsOf: logURL)
// Send your file to your sever here
catch{
// Handle any errors you might've encountered
}
Note that I'm guessing based on the paths you pasted in your answer you put it in your application's documents directory. That's a perfectly fine place to put this type of thing, but if I'm wrong and you put it in a different place, you'll have to modify this code to point to the right place

Related

Create zip file without third party libraries and parent directory in swift

I need to create a simple zip file with files included without parent directory in swift.
I found a good solution from Robin Kunde https://recoursive.com/2021/02/25/create_zip_archive_using_only_foundation/ but the files have a parent directory in the zip file.
So when you unzip the files, there is a directory with the name e.g. "Documents".
I don't need real compression or other zip features. Our backend server checks the content and the folder must be removed.
I don't think, that this is even possible without a C-library :(
I tried to decode the ZIP-file to UTF-8, delete the folder names and save the data to a file. The file was corrupt.
let fm = FileManager.default
let baseDirectoryUrl = fm.urls(for: .documentDirectory, in: .userDomainMask).first!
let data = try! Data(contentsOf: baseDirectoryUrl.appendingPathComponent("archive.zip"))
let str = String(decoding: data, as: UTF8.self)
let newstr = str.replacingOccurrences(of: "Documents/", with: "")
do {
try newstr.write(to: baseDirectoryUrl.appendingPathComponent("archive_without_parent_name.zip"), atomically: true, encoding: .utf8)
} catch {
print(error.localizedDescription)
}

User's document directory returning nil with FileManager

I'm trying to write a file locally but no success. When I try to get the user's document directory it returns nil and I believe this is why my file is not been stored.
Also, I have many doubts of what the "user's document directory" is supposed to mean. Is it the "Documents/" inside "iCloud Drive" or "on my phone". Should I be looking in another place instead of "Files" app? I'm using the iPhone simulator.
My code is designed as follow. documentFolderURL, fileURL and url are all nil when debugging.
let documentFolderURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last
let ext: String = type ?? "pdf"
let name = "extrato." + ext
let fileURL = documentFolderURL?.appendingPathComponent(name)
do {
if let url = fileURL {
try file.write(to: url, options: .atomic)
}
} catch {...}
Use the throwing API to get an error (there should be none)
do {
let documentFolderURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let ext: String = type ?? "pdf"
let fileURL = documentFolderURL.appendingPathComponent(name).appendingPathExtension(ext)
try file.write(to: fileURL, options: .atomic)
} catch { print(error) }
It seems that you are creating the file successfully, but you aren't looking for it in the right place.
You can navigate to the simulator's User Defaults folder by:
Print the file path of the simulator's documents directory. print(documentFolderURL) should print something like file:///Users/yourname/Library/Developer/CoreSimulator/Devices/8DAF542C-4B37-41D1-BA43-1D7C2A32E585/data/Containers/Data/Application/63545C94-56F5-3B11-B601-543801BE717A/Documents/
Copy the entire url EXCEPT the leading file:// (in other words, start with /User/yourname...
Open your macbook's Finder app, and press command + shift + g. This will allow you to...(drum roll please)...
Paste in the url to navigate to your simulator's documents directory.
Your file should be there :)

iOS - FileManager won't delete json file in document directory

I created a file in the document directory and for some reason when I try to delete it using the code below, it doesn't get deleted. The code doesn't throw any errors, but the file is still there
let fileManager = FileManager.default
if let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first {
let filePath = documentDirectory.appendingPathComponent("data.json")
do {
try fileManager.removeItem(atPath: filePath.path)
} catch let error as NSError {
print(error.localizedDescription)
}
}
I also tried to put it inside another folder and remove the folder but still the same problem.
This is the path where the file is stored :
Users/user1/Library/Developer/CoreSimulator/Devices/76AFDB69-75C8-464E-93F2-6ABF622068FD/data/Containers/Data/Application/7D268156-977A-4A3C-834B-6B13FA3DE76D/Documents/
you can try like below
if let filePath = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("data.json"), fileManager.fileExists(atPath: filePath.path) {
do {
try fileManager.removeItem(atPath: filePath.path)
} catch let error as NSError {
print(error.localizedDescription)
}
}
The code doesn't throw any errors, but the file is still there
No, it isn't. The problem is with the way you are checking to see whether "the file is still there". You are looking on your computer for the file. But iOS files are sandboxed. At the time you are looking, the path where the file was is completely different from your claimed path:
Users/user1/Library/Developer/CoreSimulator/Devices/76AFDB69-75C8-464E-93F2-6ABF622068FD/data/Containers/Data/Application/7D268156-977A-4A3C-834B-6B13FA3DE76D/Documents/
Such paths are not permanent. They are meaningless and should not be used. The only way to know whether the file is still there is with more code from inside iOS, i.e. ask the FileManager. When you do, you will find that you are, indeed, deleting the file successfully.

How to store and view the file in Iphone using IOS Swift

I am new to swift and trying to save the file on iphone and view them using file manager app present in app store. but every time the path looks like its getting stored in my mac machine. below is code which i have written for storing a simple text file
func saveImageDocumentDirectory(){
let str = "Super long string here"
let filename = getDocumentsDirectory().appendingPathComponent("output.txt")
do {
try str.write(to: filename, atomically: true, encoding: String.Encoding.utf8)
print(filename.path)
} catch {
// failed to write file – bad permissions, bad filename, missing permissions, or more likely it can't be converted to the encoding
}
}
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
but path at which file is getting stored is printed as below
/var/mobile/Containers/Data/Application/ACBC0B24-XXXX-XXXX-XXXX-BDAA4901EA41/Documents/output.txt

Swift File Download Issue

I am trying to download a plist file from a remote location and use it in the iOS app I am creating. The file is going to be used for calendar details within the app's calendar. The goal is obviously that I can update the remote file instead of having to push updates to the app itself every time we need to make changes to calendar details.
I started with the code used in this example: Download File From A Remote URL
Here is my modified version:
// Create destination URL
let documentsUrl:URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL!
let destinationFileUrl = documentsUrl.appendingPathComponent("2017.plist")
//let destinationFileUrl = URL(string: Bundle.main.path(forResource: String(currentYear), ofType: "plist")!)
//Create URL to the source file you want to download
let fileURL = URL(string: "https://drive.google.com/open?id=0BwHDQFwaL9DuLThNYWwtQ1VXblk")
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let request = URLRequest(url:fileURL!)
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("Successfully downloaded. Status code: \(statusCode)")
}
do {
try FileManager.default.removeItem(at: destinationFileUrl)
try FileManager.default.moveItem(at: tempLocalUrl, to: destinationFileUrl)
print("File was replaced")
print(NSArray(contentsOf: tempLocalUrl))
//print(tempLocalUrl)
} catch (let writeError) {
print("Error creating a file \(String(describing: destinationFileUrl)) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription as Any);
}
}
task.resume()
I originally tried to overwrite the file that is bundled with the app to being with, that resulted in errors. So I instead tried to just save it in the app's documents folder and that removed that error. I had to make sure and remove any previous version of the file because it was giving me a file already exists error after the first run.
While it says everything is working (The outputs for both successful download and replaced file happen) when I print the contents of the array from the downloaded URL it just gives me nil.
This is my first attempt to use any kind of external resources in an app. Before I have always kept everything internal, so I am sure there is something glaringly obvious I am missing.
Update 1:
I realized I didn't have the correct URL to use to download a file from a Google drive. That line of code has been changed to:
let fileURL = URL(string: "https://drive.google.com/uc?export=download&id=0BwHDQFwaL9DuLThNYWwtQ1VXblk")
So now I actually am downloading the plist like I originally thought I was. Even removing the deletion issue mentioned in the first comment, I still can't get the downloaded file to actually replace the existing one.
Update 2:
I have reduced the actual file manipulation down to the following:
do {
try FileManager.default.replaceItemAt(destinationFileUrl, withItemAt: tempLocalUrl)
print("File was replaced")
print(NSArray(contentsOf: destinationFileUrl))
} catch (let writeError) {
print("Error creating a file \(String(describing: destinationFileUrl)) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription as Any);
}
After the replacement is performed the output of the file shows the correct new contents that were downloaded from the internet.
Later in the code when I try and access the file it seems to be nil in content again.
Look at your download completion code. You:
Delete the file at the destination URL (in case there was one
leftover)
MOVE the temp file to the destination URL (removing it from the temp
URL)
Try to load the file from the temp URL.
What's wrong with this picture?
You are trying to get the contents of the moved file. You already moved the file to destination url and then you are trying to get the contents of the file from temporary location.
For getting file data, Please try the following :
let fileData = try! String(contentsOf: destinationFileUrl, encoding: String.Encoding.utf8)
print(fileData)

Resources