Different path after appendingPathComponent(_:) - ios

I am trying to access json files I copied to the ~/Documents folder.
When I check what files are available there, it has those files in an array with paths beginning with file:///private/var/mobile/Containers/Data/Application/95982B17-2C5F-4E3F-8AD7-FB90F557B991/Documents/:
let fileManager = FileManager.default
if let docDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first {
do {
let docs = try fileManager.contentsOfDirectory(at: docDirectory, includingPropertiesForKeys: [], options: .skipsHiddenFiles)
print("Files in ~/Documents are: ")
for doc in docs {
print(doc)
}
} catch let error {
Logger.printLogEntry(message: "Could not get content of documents directory: \(error.localizedDescription)", category: .dev
)
}
}
But when I then add a path like so
let filePath = docDirectory.appendingPathComponent("products.json")
print("File Path is: ", filePath)
it gives me the following path: file:///var/mobile/Containers/Data/Application/95982B17-2C5F-4E3F-8AD7-FB90F557B991/Documents/
This is different on the simulator; paths there remain the same (without the private stuff in front...
Can anyone explain that to me? To be clear, I need to copy / access in different methods, so understanding the way it's accessing differently is crucial to me.

So that hopefully for someone else to be more successful to get an answer when researching this:
/var and /private/var` point to the same folder on a real device, as one is an alias of the other, as mentioned in the comments above. So thanks to the commenters for their hints.

Related

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 / Swift failing to write file as treating file path as directory path [duplicate]

This question already has an answer here:
UIImage(contentsOfFile:) returning nil despite file existing in caches directory [duplicate]
(1 answer)
Closed 4 years ago.
I have the following swift function that I hoped would save incoming bytes to a JPEG file on iOS. Unfortunately an exception is thrown by the call to data.write and I get the error message
The folder “studioframe0.jpg” doesn’t exist. writing to file:/var/mobile/Containers/Data/Application/2A504F84-E8B7-42F8-B8C3-3D0A53C1E11A/Documents/studioframe0.jpg -- file:///
Why does iOS think it is a directory path to a directory which does not exist as opposed to a file that I am asking it to write?
func saveToFile(data: Data){
if savedImageCount < 10 {
guard let documentDirectoryPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
return
}
let imgPath = URL(fileURLWithPath: documentDirectoryPath.appendingPathComponent("studioframe\(savedImageCount).jpg").absoluteString)
savedImageCount += 1
do {
try data.write(to: imgPath, options: .atomic)
print("Saved \(imgPath) to disk")
} catch let error {
print("\(error.localizedDescription) writing to \(imgPath)")
}
}
}
URL(fileURLWithPath together with absoluteString is wrong.
You would have to write (note the different URL initializer):
let imgPath = URL(string: documentDirectoryPath.appendingPathComponent("studioframe\(savedImageCount).jpg").absoluteString)
but this (URL → String → URL) is very cumbersome, there is a much simpler solution, please consider the difference between (string) path and URL
let documentDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! // the Documents directory is guaranteed to exist.
let imgURL = documentDirectoryURL.appendingPathComponent("studioframe\(savedImageCount).jpg")
...
try data.write(to: imgURL, options: .atomic)

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 correctly reference/retrieve a temp file created in AppData for file upload to a server?

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

How can I purge just images in cache directory?

I want to clear all images in default cache directory every 1 minute but the cache files do not have extensions to specific their type and I don't know how to delete just images like PNG (not other data).
this is sample code I saw on this site:
let fileManager = FileManager.default
let documentsUrl = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! as NSURL
let documentsPath = documentsUrl.path
do {
if let documentPath = documentsPath
{
let fileNames = try fileManager.contentsOfDirectory(atPath: "\(documentPath)")
print("all files in cache: \(fileNames)")
for fileName in fileNames {
if (fileName.hasSuffix(".png"))
{
let filePathName = "\(documentPath)/\(fileName)"
try fileManager.removeItem(atPath: filePathName)
}
}
let files = try fileManager.contentsOfDirectory(atPath: "\(documentPath)")
print("all files in cache after deleting images: \(files)")
}
} catch {
print("Could not clear temp folder: \(error)")
}
possibility:
you could use ImageIO to test EVERY file before deleting it but that would mean reading it before removing it. It'd replace testing for a suffix BUT
as it'd be really unnecessarily expensive IMHO I wont even provide code.
okay way:
=> Rename your images to have a suffix or prefix so you can identify them by name (n calls)
good way
=> put the images in a seperate folder and just remove the folder to purge them. (1 call)

Resources