Get the Contents of multiple files in directory - swift

I need to get the contents from multiple plist files and bring them into a single dictionary which is then displayed in a tableView
Using this code I can manually get each path and the contents of the file but I need to be able to do this for all plist files in the directory not just these predefined ones.
func getFiles() {
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let path = documentDirectory.appending("/MainFolder/File1.plist")
let path1 = documentDirectory.appending("/MainFolder/File2.plist")
let path2 = documentDirectory.appending("/MainFolder/File3.plist")
tableViewData = [NSDictionary(contentsOfFile: path) as! [String : String], NSDictionary(contentsOfFile: path1) as! [String : String], NSDictionary(contentsOfFile: path2) as! [String : String]]
print(tableViewData)
}
I the display tableViewData in my tableView which gives me each files contents on its own row.
I am guessing I probably need an array of file urls filtered by .plist and then some way to get the contents of each file into a [String : String] dictionary.
I am new to swift, any help or a better way to do this would be great

First of all don't use outdated and objective-c-ish NSSearchPathForDirectoriesInDomains in Swift. Use the modernFileManager API.
Second of all don't use objective-c-ish NSDictionary(contentsOf to read property list data. Use PropertyListSerialization.
The function throws that means it hands over all possible errors to the caller. It filters the URLs in the directory by the plist extension and uses the map function to get the dictionary for each URL.
func getFiles() throws {
let documentDirectory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let subFolderURL = documentDirectory.appendingPathComponent("MainFolder")
let allFiles = try FileManager.default.contentsOfDirectory(at: subFolderURL, includingPropertiesForKeys: nil)
let properListFiles = allFiles.filter{$0.pathExtension == "plist"}
tableViewData = try properListFiles.compactMap { url -> [String:String]? in
let data = try Data(contentsOf: url)
return try PropertyListSerialization.propertyList(from: data, format: nil) as? [String:String]
}
print(tableViewData)
}
Be aware that in sandboxed apps the Documents folder is located in the application container.

You can read all the plists and iterate over them adding their contents either to 1 array, or to an already kept dictionary.
do {
let directoryContents = try FileManager.default.contentsOfDirectory(at: documentDirectory, includingPropertiesForKeys: nil, options: [])
let allPlistFiles = directoryContents.filter{ $0.pathExtension == "plist" }
var aPlistArray = []
for aPlist in allPlistFiles {
aPlistArray.append(NSDictionary(contentsOfFile: path) as! [String : String])
}
} catch {
print(error)
}

Related

SWIFT writing to plist is not updating

I'm trying to write to the plist and I'm using two approaches but none of them work for me.
I'm not getting any errors though and when I print the paths I can see that plist exist, however you can see from the screenshot that the plist it is not getting updated/populated.
let path = Bundle.main.path(forResource: "Employee", ofType: "plist")!
let data : NSDictionary =
["A": [["userid":"1","username":"AAA","usergroupid":"2"], ["userid":"33","username":"ABB","usergroupid":"8"]],
"B": [["userid":"2","username":"BBB","usergroupid":"8"], ["userid":"43","username":"ABC","usergroupid":"8"]] ]
//first approach
let favoritesDictionary = NSDictionary(object: data, forKey: ("Favorites" as NSString?)!)
print(path)
let succeeded = favoritesDictionary.write(toFile: path, atomically: true)
//second approach
let bundlePath = Bundle.main.path(forResource: "Employee", ofType: "plist")!
print(bundlePath)
let dictionary = NSMutableDictionary(contentsOfFile: bundlePath)
dictionary?.setObject(data, forKey: ("Locations" as NSString?)!)
dictionary?.write(toFile: bundlePath, atomically: true)
Can someone please help?
This is a short tutorial.
Create your plist file and put it in the application bundle.
In AppDelegate create a computed property to get the current Documents folder and append the file path
var employeePlistURL : URL {
let documentsFolderURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
return documentsFolderURL.appendingPathComponent("Employee.plist")
}
In AppDelegate applicationWillFinishLaunching register a key-value pair for the firstLaunch flag in UserDefaults and copy the plist into the documents folder if the flag is true
func applicationWillFinishLaunching(_ aNotification: Notification) {
let defaults = UserDefaults.standard
defaults.register(defaults: ["firstLaunch":true])
if defaults.bool(forKey: "firstLaunch") {
let sourceFile = Bundle.main.url(forResource: "Employee", withExtension: "plist")!
try? FileManager.default.copyItem(at: sourceFile, to: employeePlistURL)
defaults.set(false, forKey: "firstLaunch")
}
}
Wherever you need to read and write the property list create also the computed property and add a property for the dictionary
var employees = [String:Any]()
and two methods to load and save the data
func loadEmployees() {
do {
let data = try Data(contentsOf: employeePlistURL)
guard let plist = try PropertyListSerialization.propertyList(from: data, format: nil) as? [String:Any] else { return }
employees = plist
} catch { print(error) }
}
func saveEmployees() {
do {
let data = try PropertyListSerialization.data(fromPropertyList: employees, format: .binary, options: 0)
try data.write(to: employeePlistURL)
} catch { print(error) }
}
A better way is to use structs and PropertyListEncoder/-Decoder but as the literal dictionary and the screenshot in the question are rather different I provide the common Dictionary / PropertyListSerialization way.

How play music as Document directory in swift?

I can't read the file in the document
Get file in the document and show on the table view
if file as mp3 music did select on row play music
func loadMusic()
{
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let files = FileManager().enumerator(atPath: documentsPath)
var myFiles:[String] = []
while let file: AnyObject = files?.nextObject() as AnyObject? {
myFiles.append(file as! String)
print(file)
}
I use this function to see log name of file in document but I can't see in my app.
its better that you create array from your local media and then show this array in tableView.
use this method for create array from local media
var localMedia = [String]() // array for save local audio names
let directory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
if let urlArray = try? FileManager.default.contentsOfDirectory(at: directory,
includingPropertiesForKeys: [.contentModificationDateKey],
options:.skipsHiddenFiles) {
localMedia = urlArray.map { url in
(url.lastPathComponent, (try? url.resourceValues(forKeys: [.contentModificationDateKey]))?.contentModificationDate ?? Date.distantPast)
}.sorted(by: { $0.1 > $1.1 }).map { $0.0 }
print("media = \(localMedia)")

Get Full File Path During Enumeration

I'm using the following code to enumerate a user selected path and populate an array with files names with specific extensions.
let enumerator:FileManager.DirectoryEnumerator = fileManager.enumerator(atPath: path)!
while let element = enumerator.nextObject() as? String {
if ((element.hasSuffix("jpg"))||element.hasSuffix("playground")) {
if(!self.yourArray.contains(element))
{
self.yourArray.append(element)
}
}
I need to get the complete file path to add the file to a processing queue.
Also how I can prevent the enumerator from exploring sub directories.
Please advice.
First of all use the URL related API, it's more versatile.
Unlike the DirectoryEnumerator contentsOfDirectory returns the URLs of all items in the directory without the subdirectories.
An easy solution is an array of path extensions and the filter function
let url = URL(fileURLWithPath: "/Users/myUser/Pictures/")
let filterExtensions = ["jpg", "playground"]
do {
self.yourArray = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil)
.filter{ filterExtensions.contains($0.pathExtension) }
print(yourArray)
} catch {
print(error)
}
Be aware that yourArray will contain URLs, not string paths.
To use a directory enumerator:
This will allow you to get all the files in all subdirectories, not just the one level deep. Turn the path that you provided to fileManager.enumerator(atPath: path)! into a URL like below and then append the path provided by directory enumerator to this URL:
let directoryUrl = URL(fileURLWithPath: path)
let pathUrl = directoryUrl.appendingPathComponent(element)
If I was writing your code, I would also do some renames/ improvements:
func main() {
// Set up your variables
let fileManager = FileManager.default
let path = "/Users/username"
var files = [URL]() // Using a URL instead of a path.
let enumerator: FileManager.DirectoryEnumerator = fileManager.enumerator(atPath: path)!
let directoryUrl = URL(fileURLWithPath: path)
while let filepath = enumerator.nextObject() as? String {
if ((filepath.hasSuffix(".jpg")) || filepath.hasSuffix(".playground")) {
let pathUrl = directoryUrl.appendingPathComponent(filepath)
files.append(pathUrl)
}
}
}

Check for Property List

I'm currently learning Swift 2 on XCode 7 and trying to figure out how to test if a property list is available to read from.
I have a convenience initializer that works but I want to implement a test to see if the propertyList exists, otherwise just create an empty array.
Here's my code far:
Property List creation and write
let propertyList: NSArray = photoGrid.photos.map { $0.propertyListRepresentation() }
let path = NSSearchPathForDirectoriesInDomains(
.DocumentDirectory,
.UserDomainMask,
true)[0] as NSString
let file = path.stringByAppendingPathComponent("data.plist")
propertyList.writeToFile(file, atomically: true)
Convenience Init
convenience init(propertyList: NSArray) {
self.init()
// test if property list exists {
self.photos = propertyList.map { (param: AnyObject) -> Photo in
let pl = param as! NSDictionary
let photo = Photo(propertyList: pl)
return photo!
}
// } else {
// print("Property List does not exist... Created empty object array)
}
First of all use the URL related API to get the file URL
let documentDirectoryURL = try! NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)
let fileURL = documentDirectoryURL.URLByAppendingPathComponent("data.plist")
Second of all, there is a class NSPropertyListSerialization which is preferable to the implicit property list serialization of NSArray.
Third of all, in Swift use native collection types rather than the type-unspecified Foundation classes.
This is an init method which creates the file URL, checks file exists and assigns the mapped Photo instances or an empty Photo array to the instance variable.
init() {
let documentDirectoryURL = try! NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)
let fileURL = documentDirectoryURL.URLByAppendingPathComponent("data.plist")
if let data = NSData(contentsOfURL:fileURL), propertyList = try! NSPropertyListSerialization.propertyListWithData(data, options: [], format: nil) as? [[String:AnyObject]] {
self.photos = propertyList.map { Photo(propertyList: $0)! }
} else {
self.photos = [Photo]()
}
}
The two try! expressions are safe because the document directory exists and the property list file has a predictable format.

Search for all txt files in directory - Swift

a. How should I get all the txt files in directory?
i got a path of directory and now i should find all the txt files and change every one a little.
i try to run over all the files:
let fileManager = NSFileManager.defaultManager()
let enumerator:NSDirectoryEnumerator = fileManager.enumeratorAtPath(folderPath)
while let element = enumerator?.nextObject() as? String {
}
}
but I stuck there. How can I check if the filetype is text?
b. When i get to a directory (in the directory I run), I want get in and search there too, and in the end get out to the place I was and continue.
a is much more important to me but if I get an answer to b too it will be nice.
a. Easy and simple solution for Swift 3:
let enumerator = FileManager.default.enumerator(atPath: folderPath)
let filePaths = enumerator?.allObjects as! [String]
let txtFilePaths = filePaths.filter{$0.contains(".txt")}
for txtFilePath in txtFilePaths{
//Here you get each text file path present in folder
//Perform any operation you want by using its path
}
Your task a is completed by above code.
When talking about b, well you don't have to code for it because we are here using a enumerator which gives you the files which are inside of any directory from your given root directory.
So the enumerator does the work for you of getting inside a directory and getting you their paths.
You can use for .. in syntax of swift to enumerate through NSEnumerator.
Here is a simple function I wrote to extract all file of some extension inside a folder.
func extractAllFile(atPath path: String, withExtension fileExtension:String) -> [String] {
let pathURL = NSURL(fileURLWithPath: path, isDirectory: true)
var allFiles: [String] = []
let fileManager = NSFileManager.defaultManager()
if let enumerator = fileManager.enumeratorAtPath(path) {
for file in enumerator {
if let path = NSURL(fileURLWithPath: file as! String, relativeToURL: pathURL).path
where path.hasSuffix(".\(fileExtension)"){
allFiles.append(path)
}
}
}
return allFiles
}
let folderPath = NSBundle.mainBundle().pathForResource("Files", ofType: nil)
let allTextFiles = extractAllFile(atPath: folder!, withExtension: "txt") // returns file path of all the text files inside the folder
I needed to combine multiple answers in order to fetch the images from a directory and I'm posting my solution in Swift 3
func searchImages(pathURL: URL) -> [String] {
var imageURLs = [String]()
let fileManager = FileManager.default
let keys = [URLResourceKey.isDirectoryKey, URLResourceKey.localizedNameKey]
let options: FileManager.DirectoryEnumerationOptions = [.skipsPackageDescendants, .skipsSubdirectoryDescendants, .skipsHiddenFiles]
let enumerator = fileManager.enumerator(
at: pathURL,
includingPropertiesForKeys: keys,
options: options,
errorHandler: {(url, error) -> Bool in
return true
})
if enumerator != nil {
while let file = enumerator!.nextObject() {
let path = URL(fileURLWithPath: (file as! URL).absoluteString, relativeTo: pathURL).path
if path.hasSuffix(".png"){
imageURLs.append(path)
}
}
}
return imageURLs
}
and here is a sample call
let documentsDirectory = FileManager.default.urls(for:.documentDirectory, in: .userDomainMask)[0]
let destinationPath = documentsDirectory.appendingPathComponent("\(filename)/")
searchImages(pathURL: projectPath)
Swift 4
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let url = URL(fileURLWithPath: documentsPath)
let fileManager = FileManager.default
let enumerator: FileManager.DirectoryEnumerator = fileManager.enumerator(atPath: url.path)!
while let element = enumerator.nextObject() as? String, element.hasSuffix(".txt") {
// do something
}
let fileManager = NSFileManager.defaultManager()
let enumerator:NSDirectoryEnumerator = fileManager.enumeratorAtPath(folderPath)
while let element = enumerator?.nextObject() as? String where element.pathExtension == "txt" {
// element is txt file
}
let fileManager = NSFileManager.defaultManager()
let enumerator:NSDirectoryEnumerator = fileManager.enumeratorAtPath(folderPath!)!
while let element = enumerator.nextObject() as? String {
if (element.hasSuffix(".txt")) { // element is a txt file }
}