Different path URL for FileManager everytime I open the app [duplicate] - swift

This question already has answers here:
NSFileManager.defaultManager().fileExistsAtPath returns false instead of true
(2 answers)
Closed 8 months ago.
I want to create one folder in the fileManager root path, but before creating it, I want to check that the folder exist or not, and if not, I will create, otherwise will leave it
here are the function that I use
public func isDirectoryExist(path: String) -> Bool {
let fileManager = FileManager.default
var isDir : ObjCBool = false
if fileManager.fileExists(atPath: path, isDirectory:&isDir) {
if isDir.boolValue {
return true
} else {
return false
}
} else {
return false
}
}
public func createNewDirectory(name: String) {
let DocumentDirectory = NSURL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
let DirPath = DocumentDirectory.appendingPathComponent(name)
do
{
try FileManager.default.createDirectory(atPath: DirPath!.path, withIntermediateDirectories: true, attributes: nil)
}
catch let error as NSError
{
Logger.logError("Unable to create directory \(error.debugDescription)")
}
Logger.logInfo("Dir Path = \(DirPath!)")
}
Now, when I check the folder existing, it's always false and create a new folder and it happen every time
func createARObjectDirectory() {
let rootURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
if isDirectoryExist(path: "\(rootURL.absoluteString)\(DefaultURL.arObjectUrlDirectoryName)") {
Logger.logServer("ARObject directly found")
} else {
createNewDirectory(name: DefaultURL.arObjectUrlDirectoryName)
}
}
Then I print the root url, and seems the hash in the middle of url is always different, how I can check it?
file:///var/mobile/Containers/Data/Application/5AD0690B-498D-4309-8BD0-191FB88766AC/Documents/AR-Object/
file:///var/mobile/Containers/Data/Application/41D35A54-1807-417E-AE29-311D43FCC21D/Documents/AR-Object/
file:///var/mobile/Containers/Data/Application/F7E385CC-7921-4C37-B9BF-BCEFFC2AEE9E/Documents/AR-Object/
file:///var/mobile/Containers/Data/Application/4748B014-5E55-46BB-BC83-394A6BC27292/Documents/AR-Object/
Thanks for your help

You are using the wrong API. absoluteString (in rootURL.absoluteString) returns the string representation including the scheme file://. The correct API for file system URLs is path
I recommend to use the URL related API as much as possible
public func directoryExists(at url: URL) -> Bool {
let fileManager = FileManager.default
var isDir : ObjCBool = false
if fileManager.fileExists(atPath: url.path, isDirectory:&isDir) {
return isDir.boolValue
} else {
return false
}
}
and compose the URL in a more reliable way
func createARObjectDirectory() {
let rootURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
if directoryExists(at: rootURL.appendingPathComponent(DefaultURL.arObjectUrlDirectoryName) {
Logger.logServer("ARObject directly found")
} else {
createNewDirectory(name: DefaultURL.arObjectUrlDirectoryName)
}
}
And this is swiftier too
public func createNewDirectory(name: String) {
let documentDirectory = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let dirURL = documentDirectory.appendingPathComponent(name)
do
{
try FileManager.default.createDirectory(at: dirURL, withIntermediateDirectories: false, attributes: nil)
}
catch let error as NSError
{
Logger.logError("Unable to create directory \(error.debugDescription)")
}
Logger.logInfo("Dir Path = \(dirPath.path)")
}

The actual path to your app's documents directory is subject to change. You should use relative paths from the documents directory, and not try to compare paths between runs.
(I believe the path changes each time you rebuild your app or reinstall it, but is {fairly} stable for app store builds.)
Are you saying that the directory you create inside the documents directory goes away between runs? That should not be true. The contents of the documents directory should persist.

Related

FileManager doesn't have permission to access Music directory [duplicate]

This question already has answers here:
NSFileManager.defaultManager().fileExistsAtPath returns false instead of true
(2 answers)
Closed 8 months ago.
I want to create one folder in the fileManager root path, but before creating it, I want to check that the folder exist or not, and if not, I will create, otherwise will leave it
here are the function that I use
public func isDirectoryExist(path: String) -> Bool {
let fileManager = FileManager.default
var isDir : ObjCBool = false
if fileManager.fileExists(atPath: path, isDirectory:&isDir) {
if isDir.boolValue {
return true
} else {
return false
}
} else {
return false
}
}
public func createNewDirectory(name: String) {
let DocumentDirectory = NSURL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
let DirPath = DocumentDirectory.appendingPathComponent(name)
do
{
try FileManager.default.createDirectory(atPath: DirPath!.path, withIntermediateDirectories: true, attributes: nil)
}
catch let error as NSError
{
Logger.logError("Unable to create directory \(error.debugDescription)")
}
Logger.logInfo("Dir Path = \(DirPath!)")
}
Now, when I check the folder existing, it's always false and create a new folder and it happen every time
func createARObjectDirectory() {
let rootURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
if isDirectoryExist(path: "\(rootURL.absoluteString)\(DefaultURL.arObjectUrlDirectoryName)") {
Logger.logServer("ARObject directly found")
} else {
createNewDirectory(name: DefaultURL.arObjectUrlDirectoryName)
}
}
Then I print the root url, and seems the hash in the middle of url is always different, how I can check it?
file:///var/mobile/Containers/Data/Application/5AD0690B-498D-4309-8BD0-191FB88766AC/Documents/AR-Object/
file:///var/mobile/Containers/Data/Application/41D35A54-1807-417E-AE29-311D43FCC21D/Documents/AR-Object/
file:///var/mobile/Containers/Data/Application/F7E385CC-7921-4C37-B9BF-BCEFFC2AEE9E/Documents/AR-Object/
file:///var/mobile/Containers/Data/Application/4748B014-5E55-46BB-BC83-394A6BC27292/Documents/AR-Object/
Thanks for your help
You are using the wrong API. absoluteString (in rootURL.absoluteString) returns the string representation including the scheme file://. The correct API for file system URLs is path
I recommend to use the URL related API as much as possible
public func directoryExists(at url: URL) -> Bool {
let fileManager = FileManager.default
var isDir : ObjCBool = false
if fileManager.fileExists(atPath: url.path, isDirectory:&isDir) {
return isDir.boolValue
} else {
return false
}
}
and compose the URL in a more reliable way
func createARObjectDirectory() {
let rootURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
if directoryExists(at: rootURL.appendingPathComponent(DefaultURL.arObjectUrlDirectoryName) {
Logger.logServer("ARObject directly found")
} else {
createNewDirectory(name: DefaultURL.arObjectUrlDirectoryName)
}
}
And this is swiftier too
public func createNewDirectory(name: String) {
let documentDirectory = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let dirURL = documentDirectory.appendingPathComponent(name)
do
{
try FileManager.default.createDirectory(at: dirURL, withIntermediateDirectories: false, attributes: nil)
}
catch let error as NSError
{
Logger.logError("Unable to create directory \(error.debugDescription)")
}
Logger.logInfo("Dir Path = \(dirPath.path)")
}
The actual path to your app's documents directory is subject to change. You should use relative paths from the documents directory, and not try to compare paths between runs.
(I believe the path changes each time you rebuild your app or reinstall it, but is {fairly} stable for app store builds.)
Are you saying that the directory you create inside the documents directory goes away between runs? That should not be true. The contents of the documents directory should persist.

Want to show user a message when no data available in my document directory in swift

I want to check the pdf file with a perticular user name is available in my document directory or not. By using following code i am able to do this. But when there is no data is avalable in the document directory i want to show user a message. But i am unable to show user any message. How do do this? can anyone help me?
private func checkPatientPdfIsPresentOrNot(selectedPatient: String, completion: (_ present: Bool) -> Void){
if let documentsPathString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
let filemanager = FileManager.default
if let files = filemanager.enumerator(atPath: documentsPathString){
while let file = files.nextObject() {
let nameWithDate = (file as! String).components(separatedBy: ".")
let fileName = nameWithDate[0]
let namewithoutDate = fileName.components(separatedBy: "_")
let name = namewithoutDate[0]
if name == selectedPatient.capitalized{
completion(true)
}
else{
completion(false)
}
}
}
else{
completion(false)
}
}
}
First of all there are – in terms of Swift – a lot of outdated APIs in your code.
Second of all as the entire code is synchronous the completion handler is pointless.
The major issue is that the completion handler is called multiple times. You should call it once passing true if the partial file name matches selectedPatient or passing false after the loop.
This is a suggestion with more contemporary code and a boolean return value.
Show the message to the user if method returns false
private func checkPatientPdfIsPresentOrNot(selectedPatient: String) -> Bool {
let fileManager = FileManager.default
do {
let documentsURL = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let fileURLs = try fileManager.contentsOfDirectory(at: documentsURL, includingPropertiesForKeys: [.nameKey], options: .skipsHiddenFiles)
return fileURLs.first(where: { url -> Bool in
let fileName = url.deletingPathExtension().lastPathComponent
return fileName.components(separatedBy: "_").first == selectedPatient.capitalized
}) != nil
} catch { print(error); return false }
}

Downloading tflite models from server rather than using the models from assets folder in Swift

I have seen tensorflow lite examples for iOS and they resides in the assets folder.
We have some model on the server that I want to download and keep on document directory and use.
Here is how I download and save the model to the document directory.
class ModelFileManager {
let folderRoute = "Models"
func saveModel(with file: String, data: Data, extension fileExtension: String) {
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent(folderRoute, isDirectory: true)
if !FileManager.default.fileExists(atPath: documentsDirectory.path) {
do {
try FileManager.default.createDirectory(atPath: documentsDirectory.path, withIntermediateDirectories: true, attributes: nil)
} catch {
print(error)
}
}
let fileURL = documentsDirectory.appendingPathComponent("\(file).\(fileExtension)", isDirectory: false)
do {
try data.write(to: fileURL)
print("File save at \(fileURL.absoluteString)")
} catch {
print("File can't not be save at path \(fileURL.absoluteString), with error : \(error)");
}
}
func fetchModel(for name: String) -> String {
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent(folderRoute, isDirectory: true)
let fileURL = documentsDirectory.appendingPathComponent("\(name)", isDirectory: false)
return fileURL.absoluteString
}
}
So when I give the path of the file to the Interpreter, it says
The model is not a valid Flatbuffer file
Failed to create the interpreter with error: Failed to load the given model.
I solved this by replacing the file:/// with empty string from absoluteString
func fetchModel(for name: String) -> String {
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent(folderRoute, isDirectory: true)
let fileURL = documentsDirectory.appendingPathComponent("\(name)", isDirectory: false)
return fileURL.absoluteString.replacingOccurrences(of: "file:///", with: "")
}

Creating a directory at /Users/Shared/ - using Swift

I am using the following code to create a directory in /Users/Shared/ to share data of my application between all users. When i run the code gotthe below output.
2019-03-08 19:41:41.751418+0530 MyApp[7224:488397] Couldn't create document directory
2019-03-08 19:41:41.754026+0530 MyApp[7224:488397] Document directory is file:///Users/Appname
let fileManager = FileManager.default
if let tDocumentDirectory = fileManager.urls(for: .userDirectory, in: .localDomainMask).first {
let filePath = tDocumentDirectory.appendingPathComponent("Appname")
if !fileManager.fileExists(atPath: filePath.path) {
do {
try fileManager.createDirectory(atPath: filePath.path, withIntermediateDirectories: true, attributes: nil)
} catch {
NSLog("Couldn't create document directory")
}
}
NSLog("Document directory is \(filePath)")
}
I don't why this error occured. How this can be done?
Please read the log messages carefully.
You are trying to create the folder file:///Users/Appname which is not in /Users/Shared. You have to append "Shared/Appname".
And you are encouraged to use the URL related API of FileManager (and less confusing variable names 😉)
let fileManager = FileManager.default
let userDirectory = try! fileManager.url (for: .userDirectory, in: .localDomainMask, appropriateFor: nil, create: false)
let folderURL = userDirectory.appendingPathComponent("Shared/Appname")
if !fileManager.fileExists(atPath: folderURL.path) {
do {
try fileManager.createDirectory(at: folderURL, withIntermediateDirectories: true, attributes: nil)
} catch {
print("Couldn't create document directory", error)
}
}
print("Document directory is \(folderURL)")

Return file if exists, otherwise return false

I'm trying to make a String extension that searches for a file in my app's directory and either returns that file or returns false if it does not exist. Here's what I have:
extension String {
func doesFileWithNameExist() -> Bool {
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = NSURL(fileURLWithPath: path)
let filePath = url.appendingPathComponent(self+".png")?.path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath!) {
return true
} else {
return false
}
}
}
Right now my function just returns a Bool, but I'm wondering if there's a way to just return the file if it exists, otherwise return false. Is there a way to return different value types from a function?
It's highly recommended to use the URL related API. This returns an optional UIImage:
extension String {
func doesFileWithNameExist() -> UIImage? { // maybe better pngImageInDocumentsFolder()
let fileManager = FileManager.default
do {
let url = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let fullURL = url.appendingPathComponent(self).appendingPathExtension("png")
_ = try fullURL.checkResourceIsReachable()
let data = try Data(contentsOf: fullURL)
return UIImage(data: data)
} catch {
return nil
}
}
}
or using Leo's great reduction (slightly still reduced):
var image: UIImage? {
guard let url = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent(self).appendingPathExtension("‌​png"),
let data = try? Data(contentsOf: url) else { return nil }
return UIImage(data: data)
}
You can return an optional value here, which in Swift is "a thing or nil" – very close to "that file or false." For example, here's a quick tweak to your extension function that returns the path if a file exists there, or nil otherwise:
extension String {
func doesFileWithNameExist() -> String? {
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = NSURL(fileURLWithPath: path)
let filePath = url.appendingPathComponent(self+".png")?.path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath!) {
return filePath
} else {
return nil
}
}
}
A quick editorial comment, though: this kind of behavior doesn't seem especially well-suited to a String extension. I'd consider writing an extension on FileManager instead, passing in a string for the file's name. Given the hardcoding of the "png" extension, maybe something with the following signature?
extension FileManager {
func pathToExistingPNGFile(named name: String) -> String? {
// …
}
}