Write extend file attributes swift example
Here is a possible implementation in Swift 5 as an extension for URL
,
with methods to get, set, list, and remove extended attributes of
a file. (Swift 2, 3, and 4 code can be found in the edit history.)
extension URL {
/// Get extended attribute.
func extendedAttribute(forName name: String) throws -> Data {
let data = try self.withUnsafeFileSystemRepresentation { fileSystemPath -> Data in
// Determine attribute size:
let length = getxattr(fileSystemPath, name, nil, 0, 0, 0)
guard length >= 0 else { throw URL.posixError(errno) }
// Create buffer with required size:
var data = Data(count: length)
// Retrieve attribute:
let result = data.withUnsafeMutableBytes { [count = data.count] in
getxattr(fileSystemPath, name, $0.baseAddress, count, 0, 0)
}
guard result >= 0 else { throw URL.posixError(errno) }
return data
}
return data
}
/// Set extended attribute.
func setExtendedAttribute(data: Data, forName name: String) throws {
try self.withUnsafeFileSystemRepresentation { fileSystemPath in
let result = data.withUnsafeBytes {
setxattr(fileSystemPath, name, $0.baseAddress, data.count, 0, 0)
}
guard result >= 0 else { throw URL.posixError(errno) }
}
}
/// Remove 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 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 buffer with required size:
var namebuf = Array<CChar>(repeating: 0, count: length)
// Retrieve attribute list:
let result = listxattr(fileSystemPath, &namebuf, namebuf.count, 0)
guard result >= 0 else { throw URL.posixError(errno) }
// Extract attribute names:
let list = namebuf.split(separator: 0).compactMap {
$0.withUnsafeBufferPointer {
$0.withMemoryRebound(to: UInt8.self) {
String(bytes: $0, encoding: .utf8)
}
}
}
return list
}
return list
}
/// 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))])
}
}
Example usage:
let fileURL = URL(fileURLWithPath: "/path/to/file")
let attr1 = "com.myCompany.myAttribute"
let attr2 = "com.myCompany.otherAttribute"
let data1 = Data([1, 2, 3, 4])
let data2 = Data([5, 6, 7, 8, 9])
do {
// Set attributes:
try fileURL.setExtendedAttribute(data: data1, forName: attr1)
try fileURL.setExtendedAttribute(data: data2, forName: attr2)
// List attributes:
let list = try fileURL.listExtendedAttributes()
print(list)
// ["com.myCompany.myAttribute", "com.myCompany.otherAttribute", "other"]
let data1a = try fileURL.extendedAttribute(forName: attr1)
print(data1a as NSData)
// <01020304>
// Remove attributes
for attr in list {
try fileURL.removeExtendedAttribute(forName: attr)
}
} catch let error {
print(error.localizedDescription)
}
Write extended file attributes
The problem is with your call to setxattr
. The sizeof
call can't be used. You want:
int result = setxattr(path, attrName, myDataBytes, [myData length], 0, 0);
The call to sizeof(myDataBytes)
will return the size of the pointer, not the length of the data.
Swift - How to modify file metadata like kMDItemDisplayName?
Not all metadata can be changed. Much of it is not stored directly, it's derived or computed based on other metadata.
The display name for a simple file is derived from its name on disk and the system settings, like whether extensions are hidden or shown. The display name for a bundle (like an app) is slightly more complicated, but, assuming you don't find changing the contents of the bundle (which would break its code signature) acceptable, amounts to the same thing. Those are subject to the system language(s).
There are also certain folders whose names can be localized for display, but that's still based on their on-disk name.
So, to change a file's display name, change its actual name on disk.
For other properties, you can look at URL.setResourceValues(_:)
and URLResourceValues
to see which properties are settable. You can also look at URLResourceKey
to see which are documented as "read-write".
Swift set some string value to file's attribute
The documentation says "You can set the following attributes". That means you can only set those specific attributes via those API's.
What you are really looking for are Extended Attributes, and these use a separate set of (C-style) API's.
Something like:
let directory = NSTemporaryDirectory()
let someExampleText = "some sample text goes here"
do {
try someExampleText.write(toFile: "\(directory)/test.txt", atomically: true, encoding: .utf8)
} catch let error {
print("error while writing is \(error.localizedDescription)")
}
let valueString = "setting some value here"
let result = setxattr("\(directory)/test.txt", "com.stackoverflow.test", valueString, valueString.characters.count, 0, 0)
print("result is \(result) ; errno is \(errno)")
if errno != 0
{
perror("could not save")
}
let sizeOfRetrievedValue = getxattr("\(directory)/test.txt", "com.stackoverflow.test", nil, 0, 0, 0)
var data = Data(count: sizeOfRetrievedValue)
let newResult = data.withUnsafeMutableBytes({
getxattr("\(directory)/test.txt", "com.stackoverflow.test", $0, data.count, 0, 0)
})
if let resultString = String(data: data, encoding: .utf8)
{
print("retrieved string is \(resultString)")
}
Swift extension example
Creating an extension
Add a new swift file with File > New > File... > iOS > Source > Swift File. You can call it what you want.
The general naming convention is to call it TypeName+NewFunctionality.swift.
Example 1 - Double
Double+Conversions.swift
import Swift // or Foundation
extension Double {
func celsiusToFahrenheit() -> Double {
return self * 9 / 5 + 32
}
func fahrenheitToCelsius() -> Double {
return (self - 32) * 5 / 9
}
}
Usage:
let boilingPointCelsius = 100.0
let boilingPointFarenheit = boilingPointCelsius.celsiusToFahrenheit()
print(boilingPointFarenheit) // 212.0
Example 2 - String
String+Shortcuts.swift
import Swift // or Foundation
extension String {
func replace(target: String, withString: String) -> String {
return self.replacingOccurrences(of: target, with: withString)
}
}
Usage:
let newString = "the old bike".replace(target: "old", withString: "new")
print(newString) // "the new bike"
Here are some more common String
extensions.
Example 3 - UIColor
UIColor+CustomColor.swift
import UIKit
extension UIColor {
class var customGreen: UIColor {
let darkGreen = 0x008110
return UIColor.rgb(fromHex: darkGreen)
}
class func rgb(fromHex: Int) -> UIColor {
let red = CGFloat((fromHex & 0xFF0000) >> 16) / 0xFF
let green = CGFloat((fromHex & 0x00FF00) >> 8) / 0xFF
let blue = CGFloat(fromHex & 0x0000FF) / 0xFF
let alpha = CGFloat(1.0)
return UIColor(red: red, green: green, blue: blue, alpha: alpha)
}
}
See here also.
Usage:
view.backgroundColor = UIColor.customGreen
Notes
- Once you define an extension it can be used anywhere in your app just like the built in class functions.
- If you are not sure of exactly what the function or property syntax should look like, you can Option+click a similar built in method. For example, when I Option+clicked
UIColor.greenColor
I see the declaration isclass func greenColor() -> UIColor
. That gives me a good clue for how to set up my custom method. - Apple Documentation for Extensions
- In Objective-C extensions are known as categories.
Related Topics
How Does Cellforrowatindexpath Work
Enterprise In-House App Distribution
Swift Performselector:Withobject:Afterdelay: Is Unavailable
When How to Activate/Deactivate Layout Constraints
Why Push Notifications Is Not Working on Testflight
How to Change the Tint Color of the Clear Button on a Uitextfield
Why Does My Programmatically Created Screenshot Look So Bad on iOS 7
How to Retrieve a File Using Wkwebview
How to Color a Uiimage in Swift
Ios4: How to Use Video File as an Opengl Texture
Error When Trying to Obtain a Certificate: the Specified Item Could Not Be Found in the Keychain
Change String Color with Nsattributedstring
How to Apply Multiple Transforms in Swift
Using Custom Font for Entire iOS App Swift
iOS 13.1 Crash in Avaudio Player