Cocoa/Swift: Loop through names of folder in path - cocoa

I'm currently programming an os x application with swift, but I can't figure how to loop through or even get the names of all folders at a certain path. Maybe something with fm.enumeratorAtPath?

I use enumeratorAtURL. Here's some code that shows an example of how to print the directories in the user's home directory.
if let dirURL = NSURL(fileURLWithPath: NSHomeDirectory()) {
let keys = [NSURLIsDirectoryKey, NSURLLocalizedNameKey]
let fileManager = NSFileManager.defaultManager()
let enumerator = fileManager.enumeratorAtURL(
dirURL,
includingPropertiesForKeys: keys,
options: (NSDirectoryEnumerationOptions.SkipsPackageDescendants |
NSDirectoryEnumerationOptions.SkipsSubdirectoryDescendants |
NSDirectoryEnumerationOptions.SkipsHiddenFiles),
errorHandler: {(url, error) -> Bool in
return true
}
)
while let element = enumerator?.nextObject() as? NSURL {
var getter: AnyObject?
element.getResourceValue(&getter, forKey: NSURLIsDirectoryKey, error: nil)
let isDirectory = getter! as Bool
element.getResourceValue(&getter, forKey: NSURLLocalizedNameKey, error: nil)
let itemName = getter! as String
if isDirectory {
println("\(itemName) is a directory in \(dirURL.absoluteString)")
//do something with element here.
}
}
}

Related

Is there a way of producing a playground app?

Xcode newbie.(me.dumb)
First time poster.
I have the following code and it tested flawlessly in Xcode Playground(h/t D.Budd Data Science).
I want to turn the code into a MacOS app and I cannot fathom the correct Project and Settings
to do this or do I need to import the Playground into a Project and modify it?
Also this app needs to run in the background (ie, no Window/UI) as this would destroy the current window scene(MagicMirror), sort of like an Automater app can do. It would have an app icon, of course.
Thanks so much for any help.
import Cocoa
extension String{
// get the file name from string
func fileName() -> String{
return URL(fileURLWithPath: self).deletingPathExtension().lastPathComponent
}
// get the file extension from a string
func fileExtension() -> String{
return URL(fileURLWithPath: self).pathExtension
}
}
func readFile(inputFile: String) -> String{
//split the file extension and file name
let fileExtension = inputFile.fileExtension()
let fileName = inputFile.fileName()
//get the file URL
let fileURL = try! FileManager.default.url(for: .desktopDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let inputFile = fileURL.appendingPathComponent(fileName).appendingPathExtension(fileExtension)
//get the data
do{
let saveData = try String(contentsOf: inputFile)
return saveData
} catch {
return error.localizedDescription
}
}
func writeFile(outputFile: String, stringData: String){
let fileExtension = outputFile.fileExtension()
let fileName = outputFile.fileName()
//get the fileURL
let fileURL = try! FileManager.default.url(for: .desktopDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let outputFile = fileURL.appendingPathComponent(fileName).appendingPathExtension(fileExtension)
//save the data
guard let data = stringData.data(using: .utf8) else {
print("No Can Do")
return
}
do {
try data.write(to: outputFile)
print("data written: /data")
} catch {
print(error .localizedDescription)
}
let myData = readFile(inputFile: "test.txt")
writeFile(outputFile: "output.txt", stringData: myData)
}

Read & Write file tag in cocoa app OS X with swift or obj-c

Is there any way to read/write file tags without shell commands? Already tried NSFileManager and CGImageSource classes. No luck so far.
An NSURL object has a resource for key NSURLTagNamesKey. The value is an array of strings.
This Swift example reads the tags, adds the tag Foo and write the tags back.
let url = NSURL(fileURLWithPath: "/Path/to/file.ext")
var resource : AnyObject?
do {
try url.getResourceValue(&resource, forKey: NSURLTagNamesKey)
var tags : [String]
if resource == nil {
tags = [String]()
} else {
tags = resource as! [String]
}
print(tags)
tags += ["Foo"]
try url.setResourceValue(tags, forKey: NSURLTagNamesKey)
} catch let error as NSError {
print(error)
}
The Swift 3+ version is a bit different. In URL the tagNames property is get-only so it's necessary to bridge cast the URL to Foundation NSURL
var url = URL(fileURLWithPath: "/Path/to/file.ext")
do {
let resourceValues = try url.resourceValues(forKeys: [.tagNamesKey])
var tags : [String]
if let tagNames = resourceValues.tagNames {
tags = tagNames
} else {
tags = [String]()
}
tags += ["Foo"]
try (url as NSURL).setResourceValue(tags, forKey: .tagNamesKey)
} catch {
print(error)
}
#vadian's answer in Swift 4.0
('NSURLTagNamesKey' has been renamed to 'URLResourceKey.tagNamesKey')
let url = NSURL(fileURLWithPath: "/Path/to/file.ext")
var resource : AnyObject?
do {
try url.getResourceValue(&resource, forKey: URLResourceKey.tagNamesKey)
var tags : [String]
if resource == nil {
tags = [String]()
} else {
tags = resource as! [String]
}
print(tags)
tags += ["Foo"]
try url.setResourceValue(tags, forKey: URLResourceKey.tagNamesKey)
} catch let error as NSError {
print(error)
}

Swift. Can`t save file to DocumentDirectory. Whats wrong?

Here`s my code:
let fileName = "someFileName"
func saveDataToFile(urlStr:String){
let url = NSURL(string: urlStr)
var data:NSData!
let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
let directory = paths[0]
let filePath = directory.stringByAppendingPathComponent(self.fileName)
print(filePath)//prints /Users/.../Library/Developer/CoreSimulator/Devices/1013B940-6FAB-406B-96FD-1774C670A91E/data/Containers/Data/Application/2F7139D6-C137-48BF-96F6-7579821B17B7/Documents/fileName
let fileManager = NSFileManager.defaultManager()
data = NSData(contentsOfURL: url!)
print(data) // prints a lot of data
if data != nil{
fileManager.createFileAtPath(filePath, contents: data, attributes: nil)
}
}
Now I want to read this data:
func readDataFromFile(){
let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
let directory = paths[0]
let filePath = directory.stringByAppendingPathComponent(self.fileName)
print(filePath) // prints the same path
let fileManager = NSFileManager.defaultManager()
if fileManager.fileExistsAtPath(filePath){
data = fileManager.contentsAtPath(filePath)
}else{
print("*****BAD*****") // always prints this which means that file wasn`t created
}
}
What`s wrong with the first func? What is the right way to save file to DocumentDirectory?
OK, in this case the answer was following:
First need to create directory (aka folder) and only after that create file inside that directory.
Added to code this:
let fullDirPath = directory.stringByAppendingPathComponent(folderName)
let filePath = fullDirPath.stringByAppendingPathComponent(fileName)
do{
try fileManager.createDirectoryAtPath(fullDirPath, withIntermediateDirectories: false, attributes: nil)
}catch let error as NSError{
print(error.localizedDescription)
}
And as I said after this you create your file:
fileManager.createFileAtPath(filePath, contents: data, attributes: nil)
Thanks to Eric.D
Hope someone will find this useful.

How to fix `Ambiguous use of 'subscript'` every time?

I'm using this class that was written in Swift 1.2 and now I want to use it with Swift 2.0.
I get an error: Ambiguous use of 'subscript' # let artist = result["name"] as! String
} else if let jsonArtists = jsonResult["artists"] as? NSDictionary {
if let results:NSArray = jsonArtists["items"] as? NSArray {
dispatch_async(dispatch_get_main_queue(), {
self.searching = false
var suggestionResults: [spotifySearchResult] = []
for result in results {
let artist = result["name"] as! String
var sugresult = spotifySearchResult()
sugresult.artist = artist
if !suggestionResults.contains(sugresult) {
suggestionResults.append(sugresult)
}
}
handler(suggestionResults)
})
}
}
}
I tried different fixes such as let artist = (result["name"] as! String) or let artist = (result["name"] as! String) as! String
But nothing worked.
I know that the question was already post 4 times but, I can't find anyone explaining how to fix it in every case, only case by case.
Can someone explain me how to investigate to fix it? Not just only a fix for my case. I would prefer fix it by myself with your hints!
BTW what does mean subscript? Is subscript the thing between quotation mark? IMHO the error message is a bit vague...
EDIT:
class spotifySearchResult : NSObject {
var artist=""
var track=""
var duration=0
var spotifyURL = NSURL()
var spotifyURI = NSURL()
override func isEqual(theObject: AnyObject?) -> Bool {
if let myObject = theObject as? spotifySearchResult {
return (myObject.artist.uppercaseString == self.artist.uppercaseString && myObject.track.uppercaseString == self.track.uppercaseString)
}
return false
}
}
Subscription means to use the shorter syntax item["key"] for item.objectForKey["key"]
results seems to be an array of dictionaries so I suggest to cast down to a more specific type
if let results = jsonArtists["items"] as? [[String:AnyObject]] {
or even, if all values are guaranteed to be strings
if let results = jsonArtists["items"] as? [[String:String]] {

Get mounted volumes list with Swift?

Does anyone know how to get a list of all removable volumes mounted with Swift?
I've already tried this, but it return a list of all files and subfolders of external drivers:
let filemanager:NSFileManager = NSFileManager()
let files = filemanager.enumeratorAtPath("/Volumes")
while let file = files?.nextObject() {
println(file)
menu.addItem(NSMenuItem(title: file as! String, action: Selector(""), keyEquivalent: ""))
}
This prints the list of all mounted volumes:
let filemanager = NSFileManager()
let keys = [NSURLVolumeNameKey, NSURLVolumeIsRemovableKey, NSURLVolumeIsEjectableKey]
let paths = filemanager.mountedVolumeURLsIncludingResourceValuesForKeys(keys, options: nil)
if let urls = paths as? [NSURL] {
for url in urls {
println(url)
}
}
You can of course filter to get only the paths inside the "Volumes" directory:
let filemanager = NSFileManager()
let keys = [NSURLVolumeNameKey, NSURLVolumeIsRemovableKey, NSURLVolumeIsEjectableKey]
let paths = filemanager.mountedVolumeURLsIncludingResourceValuesForKeys(keys, options: nil)
if let urls = paths as? [NSURL] {
for url in urls {
if url.relativePath?.pathComponents.count > 1 {
if url.relativePath?.pathComponents[1] == "Volumes" {
println(url)
}
}
}
}
And with Swift 2 there's two differences: pass [] instead of nil for the filemanager's options, and there's no need to cast the array of NSURLs:
let filemanager = NSFileManager()
let keys = [NSURLVolumeNameKey, NSURLVolumeIsRemovableKey, NSURLVolumeIsEjectableKey]
let paths = filemanager.mountedVolumeURLsIncludingResourceValuesForKeys(keys, options: [])
if let urls = paths {
for url in urls {
if url.relativePath?.pathComponents.count > 1 {
if url.relativePath?.pathComponents[1] == "Volumes" {
print(url)
}
}
}
}
Update for Swift 2.1
let keys = [NSURLVolumeNameKey, NSURLVolumeIsRemovableKey, NSURLVolumeIsEjectableKey]
let paths = NSFileManager().mountedVolumeURLsIncludingResourceValuesForKeys(keys, options: [])
if let urls = paths {
for url in urls {
if let components = url.pathComponents
where components.count > 1
&& components[1] == "Volumes" {
print(url)
}
}
}
Update for Swift 3
let keys: [URLResourceKey] = [.volumeNameKey, .volumeIsRemovableKey, .volumeIsEjectableKey]
let paths = FileManager().mountedVolumeURLs(includingResourceValuesForKeys: keys, options: [])
if let urls = paths {
for url in urls {
let components = url.pathComponents
if components.count > 1
&& components[1] == "Volumes"
{
print(url)
}
}
}
On Unix systems a filesystem object with a system file number of 2 is a mount, regardless of a remote (nfs, smb, afp) or a local mount.
Here's an example:
let path = "/System/Volumes/Preboot"
let systemAttributes = try FileManager.default.attributesOfItem(atPath: String(describing: path))
if let fileSystemFileNumber = systemAttributes[.systemFileNumber] as? NSNumber {
print("System File Number: \(fileSystemFileNumber)")
}
So maybe this could be a short way to find mounts
let keys: [URLResourceKey] = [
.volumeNameKey,
.volumeIsRemovableKey,
.volumeIsEjectableKey,
.volumeAvailableCapacityKey,
.volumeTotalCapacityKey,
.volumeUUIDStringKey,
.volumeIsBrowsableKey,
.volumeIsLocalKey,
.isVolumeKey,
]
let manager = FileManager.default.mountedVolumeURLs(includingResourceValuesForKeys: keys)
if let urls = manager {
print(urls)
}
This code is working fine for MacOS, however on the iOS side it always returns nil. Is there any known workaround?
Thanks!

Resources