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 anSecIdentity
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
iOS PDFkit Displaymode = Singlepage Only Shows the First Page of the PDF
How to Convert Base64 into Nsdata in Swift
Changing Constraint When Keyboard Appears - Swift
Scrolling in Uicollectionview Selects Wrongs Cells - Swift
Pausing Timer When App Is in Background State Swift
Ios11 Uibarbuttonitem Not Working
How to Find the Index of a Tuple Element from an Tuple Array? iOS, Swift
Showing Hidden View Really Slow
If Let Doesn't Unwrap Optional Value for Mkannotation's Title Property
How to Detect Which Image Has Been Tapped in Swift
How to Add Two or More Buttons to Annotationview: Mkannotationview
Show More Button Next to End of Text Swift
How to Transfer Data Between Parent and Child View Controllers
Realmswift Cannot Cast Results<Someojbect> to Results<Object>
iOS Revoke Location Services Permission Programmatically