I\'m trying to set the colored labels shown by the finder. The only function I know is setResourceValue. But this needs localized names!
I could image my mother lang
History
First there was my previous answer, which works for setting one color label to a file: https://stackoverflow.com/a/39751001/2227743.
Then @mz2 posted this excellent answer which successfully sets several color labels to a file and explains the process: https://stackoverflow.com/a/40314367/2227743.
And now this little add-on, a simple follow-up to @mz2's answer.
Solution
I've simply implemented @mz2's suggestion: I've expanded his enum example with methods for fetching the Finder's preferences and extract the correct localized label color names before setting the attributes to the file.
enum LabelColors: Int {
case none
case gray
case green
case purple
case blue
case yellow
case red
case orange
func label(using list: [String] = []) -> String? {
if list.isEmpty || list.count < 7 {
switch self {
case .none: return nil
case .gray: return "Gray\n1"
case .green: return "Green\n2"
case .purple: return "Purple\n3"
case .blue: return "Blue\n4"
case .yellow: return "Yellow\n5"
case .red: return "Red\n6"
case .orange: return "Orange\n7"
}
} else {
switch self {
case .none: return nil
case .gray: return list[0]
case .green: return list[1]
case .purple: return list[2]
case .blue: return list[3]
case .yellow: return list[4]
case .red: return list[5]
case .orange: return list[6]
}
}
}
static func set(colors: [LabelColors],
to url: URL,
using list: [String] = []) throws
{
// 'setExtendedAttributeData' is part of https://github.com/billgarrison/SOExtendedAttributes
try (url as NSURL).setExtendedAttributeData(propertyListData(labels: colors, using: list),
name: "com.apple.metadata:_kMDItemUserTags")
}
static func propertyListData(labels: [LabelColors],
using list: [String] = []) throws -> Data
{
let labelStrings = labels.flatMap { $0.label(using: list) }
return try PropertyListSerialization.data(fromPropertyList: labelStrings,
format: .binary,
options: 0)
}
static func localizedLabelNames() -> [String] {
// this doesn't work if the app is Sandboxed:
// the users would have to point to the file themselves with NSOpenPanel
let url = URL(fileURLWithPath: "\(NSHomeDirectory())/Library/SyncedPreferences/com.apple.finder.plist")
let keyPath = "values.FinderTagDict.value.FinderTags"
if let d = try? Data(contentsOf: url) {
if let plist = try? PropertyListSerialization.propertyList(from: d,
options: [],
format: nil),
let pdict = plist as? NSDictionary,
let ftags = pdict.value(forKeyPath: keyPath) as? [[AnyHashable: Any]]
{
var list = [(Int, String)]()
// with '.count == 2' we ignore non-system labels
for item in ftags where item.values.count == 2 {
if let name = item["n"] as? String,
let number = item["l"] as? Int {
list.append((number, name))
}
}
return list.sorted { $0.0 < $1.0 }.map { "\($0.1)\n\($0.0)" }
}
}
return []
}
}
Usage:
do {
// default English label names
try LabelColors.set(colors: [.yellow, .red],
to: fileURL)
// localized label names
let list = LabelColors.localizedLabelNames()
try LabelColors.set(colors: [.green, .blue],
to: fileURL,
using: list)
} catch {
print("Error when setting label color(s): \(error)")
}