How to check if multiple files exist in documents directory? (Swift) - swift

I can check if one file exists with this method:
let fileNameOne = "savedpicture1"
let fileURLOne = documentsDirectoryURL.appendingPathComponent(fileNameOne)
if !FileManager.default.fileExists(atPath: fileURLOne.path) {
removeImage(itemName: "savedpicture1", fileExtension: "jpg")
} else {
print("There was no image to remove")
}
My problem is having to repeat the same lines of code for multiple files. For instance, I would like to check if the files exist in an array of paths, but I would have to repeat the code from above for each file, and it seems too redundant. I'm wondering if there's a way to check multiple files instead of repeating the code for each single path. ".fileExists" only enables me to check one path:
let filePaths = [fileURLOne.path, fileURLTwo.path, fileURLThree.path,
fileURLFour.path]

Write a method for example
func checkFiles(with fileNames: [String] {
for fileName in fileNames {
let fileURL = documentsDirectoryURL.appendingPathComponent(fileName)
if !FileManager.default.fileExists(atPath: fileURL.path) {
removeImage(itemName: fileName, fileExtension: "jpg")
} else {
print("There was no image to remove at", fileURL)
}
}
}
and call it
let fileNames = ["savedpicture1", "savedpicture2", "savedpicture3", "savedpicture4"]
checkFiles(with: fileNames)

Related

How can I iterate inside each directory in resource path

I have a simple scenario, there is a folder called content that contains image file, I want all the image file starting with nssl to be saved to an array , so I do below code, but I cannot seem to think or know a way to find out how I can move in each directory and search for such a file and append to my array , here is my code below , I can get the names of all the directories, but what to do next ?
let path = Bundle.main.resourcePath!
let fm = FileManager.default
do {
let items = try fm.contentsOfDirectory(atPath: path)
for item in items {
}
} catch {
}
FileManager is not needed.
Bundle provides urls(forResourcesWithExtension: subdirectory:) which returns multiple urls for a specific extension
if let urls = Bundle.main.urls(forResourcesWithExtension: "png", subdirectory: "content") {
for url in urls where url.lastPathComponent.hasPrefix("nssl") {
}
}
Change the png extension to the desired type.

Swift: FileManager().fileExists(atPath: (fileURL.path)) without knowing extension

)
today I have a problem and I can't find an easy solution.
With:
FileManager().fileExists(atPath:(fileURL.path))
it's simple to find out if a file exist. Actually I have the file name but don't know the extension. How can I use FileManager() to find a file without the extension. Something like .deletingPathExtension() for FileManger().fileExists?
Something like
ls filename.*
You could create a FileManager extension that retrieves the contents of the directory and filters for files as well as the expected filename.
It might look something like this:
extension FileManager {
func urls(of filename: String, in directory: URL) -> [URL]? {
guard let urls = try? contentsOfDirectory(at: directory, includingPropertiesForKeys: nil, options: [])
else { return nil }
return urls.filter { url in
!url.hasDirectoryPath && url.deletingPathExtension().lastPathComponent == filename
}
}
}
Finally, one would call it something like this:
let directory = URL(string: "file:///Users/stephan/tmp")!
if let urls = FileManager.default.urls(of: "test", in: directory) {
for url in urls {
print("do something with url: \(url)")
}
}

How do I add an Images folder to my FileWrapper

I need a FileWrapper which contains a file and contains a folder.
The file is a single file, and the folder is used to write images to.
The folder also can contain some subfolders. I have a working code, but the issue is that when the Document gets saved, the folder gets re-written which deletes my images and subfolders.
I'm quite sure it has something to do with func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper but I need some help from somebody with more experience with FileWrappers.
This is my code:
struct MyProject: FileDocument {
var myFile: MyFile
static var readableContentTypes: [UTType] { [.myf] }
init(myFile: MyFile = MyFile() {
self.myFile = myFile
}
init(configuration: ReadConfiguration) throws {
let decoder = JSONDecoder()
guard let data = configuration.file.fileWrappers?["MYFProject"]?.regularFileContents else {
throw CocoaError(.fileReadCorruptFile)
}
do {
self.myFile = try decoder.decode(MyFile.self, from: data)
} catch {
throw error
}
}
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
let encoder = JSONEncoder()
do {
let data = try encoder.encode(myFile)
let mainDirectory = FileWrapper(directoryWithFileWrappers: [:])
let myfWrapper = FileWrapper(regularFileWithContents: data)
let imagesWrapper = FileWrapper(directoryWithFileWrappers: [:])
let imageSubFolder = FileWrapper(directoryWithFileWrappers: [:])
for numberString in myFile.numbers {
imageSubFolder.preferredFilename = numberString
imagesWrapper.addFileWrapper(imageSubFolder)
}
myfWrapper.preferredFilename = "MYFProject"
mainDirectory.addFileWrapper(myfWrapper)
imagesWrapper.preferredFilename = "MYFImages"
mainDirectory.addFileWrapper(imagesWrapper)
return mainDirectory
} catch {
throw error
}
}
}
I use this path to write images to.
func getSubFolderImageFolder(documentPath: URL, subFolder: String) -> URL {
let sfProjectPath = documentPath.appendingPathComponent("MYFImages").appendingPathComponent(subFolder)
if !FileManager.default.fileExists(atPath: sfProjectPath.path) {
do {
try FileManager.default.createDirectory(atPath: sfProjectPath.path, withIntermediateDirectories: false, attributes: nil)
return sfProjectPath
} catch {
fatalError(error.localizedDescription)
}
}
else {
return sfProjectPath
}
}
Thanks in advance!
Your getSubFolderImageFolder function is not going to work well with file wrappers. You must use the FileWrapper methods to create the folders and files in the file wrapper.
To add a subfolder to your images folder, create a directory file wrapper the same way you created the imagesWrapper folder for the images. Add the subfolder as a child of the images folder.
let imageSubFolder = FileWrapper(directoryWithFileWrappers: [:])
imagesWrapper.addFileWrapper(imageSubFolder)
You must create a directory file wrapper for each subfolder. I notice in your updated code, you have only one subfolder file wrapper. With only one subfolder file wrapper, you have no way to store an image file in the correct subfolder.
To add the images, start by converting each image into a Data object. Create a regular file wrapper for each image, passing the image data as the argument to regularFileWithContents. Call addFileWrapper to add the image file to the appropriate folder.
let imageFile = FileWrapper(regularFileWithContents: imageData)
imageFile.preferredFilename = "ImageFilename" // Replace with your filename.
imagesWrapper.addFileWrapper(imageFile)
In your case the image subfolders will call addFileWrapper to add the images. The destination for the image file calls addFileWrapper.
You can find more detailed information about file wrappers in the following article:
Using File Wrappers in a SwiftUI App

How can I enumerate through a hard coded directory?

I cannot work out why hard coding a directory doesn’t work when trying to enumerate through a directory.
I have written a simple function to open a dialog and return a selected folder. The function includes a starting directory (directoryURL below):
func selectFolder(title: String, directoryURL: String = ".") -> String? {
let openPanel=NSOpenPanel();
openPanel.title = title
openPanel.allowsMultipleSelection = false
openPanel.canChooseDirectories = true
openPanel.canChooseFiles = false
openPanel.canCreateDirectories = true
openPanel.directoryURL = URL(fileURLWithPath: directoryURL)
if(openPanel.runModal() == NSApplication.ModalResponse.OK) {
return directoryURL; // This won’t work
return openPanel.url!.path // This is OK
}
else {
return nil
}
}
In the above function I have prematurely returned with the original directory which is a string, so the whole process is ignored. If I comment out the first return statement, then it will return the selected directory, which is also a string.
Here is a SwiftUI button to test the function:
Button(action: {
let fileManager = FileManager.default
let sourceFolder = selectFolder(title: "test", directoryURL: "/path/to/folder")
if let enumerator = fileManager.enumerator(
at: URL(fileURLWithPath: sourceFolder!),
includingPropertiesForKeys: [.isRegularFileKey],
options: [.skipsHiddenFiles,.skipsPackageDescendants]
) {
for case let fileURL as URL in enumerator {
print("fileURL: \(fileURL)")
}
}
}) {
Text("Test")
}
The purpose is to iterate through the contents of the directory, including subdirectories.
If I return the hard coded string from the function, the for case let fileURL as URL in enumerator statement has nothing, and there are no results. There are no errors either.
If I return the openPanel.url!.path, the for case … statement prints the directory contents as expected.
I can’t see what the function returns which is different from the original string.
What can I do to get a hard coded string to work?

fileExistsAtPath check for filename?

How to check whether there is a file in a directory with only the name without extension? Now the files are written in my directory, their name will be generated from the id file. Accordingly, when I'm looking for a file, let file = "\ (fileId) .pdf", in the directory it is, but if no extension, it will not be found. Either return as easier extension from the server?
public var isDownloaded: Bool {
let path = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
let url = NSURL(fileURLWithPath: path)
let filePath = url.URLByAppendingPathComponent("\(fileMessageModel.attachment.id)")!.path!
let fileManager = NSFileManager.defaultManager()
return fileManager.fileExistsAtPath(filePath)
}
enumeratorAtPath creates a deep enumerator -- i.e. it will scan contents of subfolders and their subfolders too. For a shallow search, user contentOfDirectortAtPath:
func file(fileName: String, existsAt path: String) -> Bool {
var isFound = false
if let pathContents = try? NSFileManager.defaultManager().contentsOfDirectoryAtPath(path) {
pathContents.forEach { file in
if (file as NSString).stringByDeletingPathExtension == fileName {
isFound = true
return
}
}
}
return isFound
}
if let path = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first {
if file("something", existsAt: path) {
// The file exists, do something about it
}
}
What about iterating over the files in the directory and testing the name with extension excluded?
let filemanager:FileManager = FileManager()
let files = filemanager.enumeratorAtPath(/* your directory path */)
while let file = files?.nextObject() {
// Remove file name extension
// Do file name comparison here
}
In terms of time complexity is will be O(n), however, as long as there are not too many files, you are good to go. On the other hand, if there are many files, you will need to consider a more efficient way to traverse, may be a trie data structure consisted of all file names in that directory.