Search code examples
iosxcodeswift4xcode9-beta

Swift 4 - Xcode 9 beta 4 - NSKernAttributeName vs NSAttributedStringKey


iOS Deployment Target: iOS 9.3, Base SDK: Latest iOS (iOS 11.0), Xcode 9 Beta 4, Swift 4

The following code builds and runs in Swift 3, Xcode 8:

    let kern = (CTRunGetAttributes(run) as? [String : Any])?
[NSKernAttributeName] as? CGFloat ?? 0

However, the Swift 4 migrator converts it to this:

    let kern = (CTRunGetAttributes(run) as? [String : Any])?
[NSAttributedStringKey.kern] as? CGFloat ?? 0

Xcode 9 now complains:

Cannot subscript a value of type '[String : Any]' with an index of type 'NSAttributedStringKey'

According to the inspector, 'NSAttributedStringKey' is only available in iOS 11. (Keep in mind that I am targeting iOS 9).

If I replace 'NSAttributedStringKey.kern' with 'NSKernAttributeName', the error remains.

Command-clicking on 'NSKernAttributeName' tells me that its availability is iOS 6. However, the inspector also claims its type is 'NSAttributedStringKey', which is only available in iOS 11. I do not know if this is merely an artefact of the API presentation, or a stuff-up, or something I'm missing.

Question: how can I use NSKernAttributeName (or its modernised version) with backward compatibility to iOS 9, with Swift 4?

Thank you for any help.


Solution

  • Type the dictionary as [NSAttributedStringKey : Any] instead of [String : Any]. It's all sugar in the Swift overlay, so it should work even on older macOS/OS X versions.

    EDIT: I should clarify my last sentence a bit. NSAttributedStringKey, as well as all the other new "Key" types in Swift 4, exists only in Swift and not in Objective-C. When a dictionary with NSAttributedStringKey keys gets bridged to Objective-C, all the keys will be turned into NSStrings. Since the actual Foundation, UIKit, etc. frameworks you're working with are written in C/Objective-C, this will "just work", and you don't have to worry about the documentation claiming NSAttributedStringKey is 10.11-only.