If you’re tempted to try accessing xattrs from Swift, you will quickly be disappointed to see that the interface to key functions like getxattr()
is very old and un-Swiftian. Rather than writing your own wrapper, or struggling on with that raw interface, I recommend that you use this extension to the Swift URL class, written by Martin R and provided on StackOverflow.
Copy and paste the code sections into a Swift source file, then import it where needed. I also provide a couple of examples which you may find helpful.
import Foundation
extension URL {
/// Get an extended attribute:
func extendedAttribute(forName name: String) throws -> Data {
let data = try self.withUnsafeFileSystemRepresentation { fileSystemPath -> Data in
// Determine the size of the attribute
let length = getxattr(fileSystemPath, name, nil, 0, 0, 0)
guard length >= 0 else { throw URL.posixError(errno) }
// Create a buffer of the required size
var data = Data(count: length)
// Retrieve the attribute and return it
let result = data.withUnsafeMutableBytes {
getxattr(fileSystemPath, name, $0, data.count, 0, 0)
}
guard result >= 0 else { throw URL.posixError(errno) }
return data
}
return data
}
A skeleton example calling this might be:
do {
let data1 = try theSourceURL.extendedAttribute(forName: item)
// do whatever you will
} catch let error {
self.appendOutputText(string: error.localizedDescription)
}
As with all these functions, because they can throw an error, you need to place them in a do … catch …
structure, or similar, to catch and handle errors. In this case, appendOutputText()
simply displays the error message in a textbox.
/// Set an extended attribute:
func setExtendedAttribute(data: Data, forName name: String) throws {
try self.withUnsafeFileSystemRepresentation { fileSystemPath in
let result = data.withUnsafeBytes {
setxattr(fileSystemPath, name, $0, data.count, 0, 0)
}
guard result >= 0 else { throw URL.posixError(errno) }
}}
/// Remove an extended attribute:
func removeExtendedAttribute(forName name: String) throws {
try self.withUnsafeFileSystemRepresentation { fileSystemPath in
let result = removexattr(fileSystemPath, name, 0)
guard result >= 0 else { throw URL.posixError(errno) }
}}
/// Get a list of all extended attributes:
func listExtendedAttributes() throws -> [String] {
let list = try self.withUnsafeFileSystemRepresentation { fileSystemPath -> [String] in
let length = listxattr(fileSystemPath, nil, 0, 0)
guard length >= 0 else { throw URL.posixError(errno) }
// Create a buffer of the required size
var data = Data(count: length)
// Retrieve the attribute list
let result = data.withUnsafeMutableBytes {
listxattr(fileSystemPath, $0, data.count, 0)
}
guard result >= 0 else { throw URL.posixError(errno) }
// Extract the attribute names into an Array of Strings
let list = data.split(separator: 0).flatMap {
String(data: Data($0), encoding: .utf8)
}
return list
}
return list
}
This is simply called in another do … try …
structure:
do {
let stringArr = try theSourceURL.listExtendedAttributes()
if stringArr.count > 0 {
// do whatever you wish
}
} catch let error {
self.appendOutputText(string: error.localizedDescription)
}
The extension relies on this helper:
/// Helper function to create an NSError from a Unix errno:
private static func posixError(_ err: Int32) -> NSError {
return NSError(domain: NSPOSIXErrorDomain, code: Int(err), userInfo: [NSLocalizedDescriptionKey: String(cString: strerror(err))])
}}
I have been using this extension in xattred (available in Downloads above) and XattrXverser, which sometimes makes millions of calls to these functions, and they have never let me down. So long as you catch any errors which they throw, your app should be rock solid.