Search code examples
swiftnsattributedstringswift4

How to convert [String : Any] to [NSAttributedStringKey : Any]


Dealing with some objC API, I receive an NSDictionary<NSString *, id> *> which translates to [String : Any] in Swift and which I was using for NSAttributedString.addAttributes:range:.

However, this method signature has now changed with Xcode 9 and now requires an [NSAttributedStringKey : Any].

let attr: [String : Any]? = OldPodModule.getMyAttributes()
// Cannot assign value of type '[String : Any]?' to type '[NSAttributedStringKey : Any]?'
let newAttr: [NSAttributedStringKey : Any]? = attr
if let newAttr = newAttr {
    myAttributedString.addAttributes(newAttr, range: range)
}

How to convert a [String : Any] to a [NSAttributedStringKey : Any]?


Solution

  • NSAttributedStringKey has an initialiser that takes a String, and you can use Dictionary's init(uniqueKeysWithValues:) initialiser in order to build a dictionary from a sequence of key-value tuples where each key is unique (such as is the case here).

    We just have to apply a transform to attr that converts each String key into an NSAttributedStringKey prior to calling Dictionary's initialiser.

    For example:

    let attributes: [String : Any]? = // ...
    
    let attributedString = NSMutableAttributedString(string: "hello world")
    let range = NSRange(location: 0, length: attributedString.string.utf16.count)
    
    if let attributes = attributes {
        let convertedAttributes = Dictionary(uniqueKeysWithValues:
            attributes.lazy.map { (NSAttributedStringKey($0.key), $0.value) }
        )
        attributedString.addAttributes(convertedAttributes, range: range)
    }
    

    We're using lazy here to avoid the creation of an unnecessary intermediate array.