Swift: Extracting/Downcasting Cftype Based Coretext Types in a Cfarray

How to convert CFArray to Swift Array?

Here is how to do this, based on the current state of the Swift compiler and Swift documentation. Hopefully this gets cleaned up in later betas.

UPDATE: Since Beta 5, reinterpretCast has been renamed to unsafeBitCast, and a CTLine object must be sent to it as an input. Way #2 still does not work.


Way #1 - Use the CFArray directly

let line: CTLine = reinterpretCast(CFArrayGetValueAtIndex(lines, 0))

Regarding Gary Makin's comments - The Ref can be dropped from CTLineRef, but this does not change ARC vs non-ARC. According to Using Swift with Cocoa and Objective-C pages 53-54, ref and non-ref are identical to the compiler. Attempting to call CFRelease causes a compiler error.


Way #2 - Convert the CFArray to a Swift array - Does not currently work

Ideally, we want to convert lines to a Swift array of CTLine objects since we know that's what is returned by CTFrameGetLines, giving us type safety after the conversion. Probably due to a compiler bug, the array can be converted to an [AnyObject] array, but not to [CTLine]. According to Apple's documentation, this should work:

let linesNS: NSArray  = CTFrameGetLines(frame)
let linesAO: [AnyObject] = linesNS as [AnyObject]
let lines: [CTLine] = linesAO as [CTLine]

This converts CFArray to NSArray, then NSArray to Swift Array [AnyObject], then downcasts that array to the specific type CTLine. This compiles, but when it is run, there is an EXC_BREAKPOINT crash on the last line.

Swift error while downcasting 'Any'

SecIdentity is “an abstract Core Foundation-type object representing an identity, ” and the type of Core Foundation types can be
checked with CFGetTypeID(). So you can check the type ID first. If it matches the type ID of an
SecIdentity then the forced cast is safe:

guard let cfIdentity = firstItem[kSecImportItemIdentity as String] as CFTypeRef?,
CFGetTypeID(cfIdentity) == SecIdentityGetTypeID() else {
throw AnError()
}
let identity = cfIdentity as! SecIdentity

See also the bug report SR-7015 The CoreFoundation conditional downcast diagnostic is not as helpful as it should be:

The diagnostic should be updated with a message that informs the developer to compare CFTypeIds (with a fixit if possible).

Trouble retrieving a CGColor from a Swift dictionary

The problem is that Core Foundation objects are opaque, therefore a value of type CGColor is nothing more than an opaque pointer – Swift itself currently doesn't know anything about the underlying object. This therefore means that you cannot currently use is or as? to conditionally cast with it, Swift has to always allow the given cast to succeed (this will hopefully change in the future though – ideally the Swift runtime would use CFGetTypeID to check the type of the opaque pointer).

One solution, as shown by Martin in this Q&A, is to use CFGetTypeID in order to check the type of the Core Foundation object – which, I would recommend factoring out into a function for convenience:

func maybeCast<T>(_ value: T, to cfType: CGColor.Type) -> CGColor? {
guard CFGetTypeID(value as CFTypeRef) == cfType.typeID else {
return nil
}
return (value as! CGColor)
}

// ...

if let color = maybeCast(things["color"], to: CGColor.self) {
print(color)
} else {
print("nil, or not a color")
}

And you could even generalise this to other Core Foundation types with a protocol:

protocol CFTypeProtocol {
static var typeID: CFTypeID { get }
}

func maybeCast<T, U : CFTypeProtocol>(_ value: T, to cfType: U.Type) -> U? {
guard CFGetTypeID(value as CFTypeRef) == cfType.typeID else {
return nil
}
return (value as! U)
}

extension CGColor : CFTypeProtocol {}
extension CGPath : CFTypeProtocol {}

// Some CF types don't have their ID imported as the 'typeID' static member,
// you have to implement it yourself by forwarding to their global function.
extension CFDictionary : CFTypeProtocol {
static var typeID: CFTypeID { return CFDictionaryGetTypeID() }
}

// ...

let x: Any? = ["hello": "hi"] as CFDictionary

if let dict = maybeCast(x, to: CFDictionary.self) {
print(dict)
} else {
print("nil, or not a dict")
}

Adding item to keychain using Swift

In the xcode 6.0.1 you must do this!!

let kSecClassValue = NSString(format: kSecClass)
let kSecAttrAccountValue = NSString(format: kSecAttrAccount)
let kSecValueDataValue = NSString(format: kSecValueData)
let kSecClassGenericPasswordValue = NSString(format: kSecClassGenericPassword)
let kSecAttrServiceValue = NSString(format: kSecAttrService)
let kSecMatchLimitValue = NSString(format: kSecMatchLimit)
let kSecReturnDataValue = NSString(format: kSecReturnData)
let kSecMatchLimitOneValue = NSString(format: kSecMatchLimitOne)


Related Topics



Leave a reply



Submit