Description
Previous ID | SR-15619 |
Radar | None |
Original Reporter | VaslD (JIRA User) |
Type | Improvement |
Environment
Xcode 13.2 (13C90)
Swift 5.5.2 (swiftlang-1300.0.47.5 clang-1300.0.29.30)
Additional Detail from JIRA
Votes | 0 |
Component/s | Foundation |
Labels | Improvement |
Assignee | None |
Priority | Medium |
md5: e6df2a63483a7e7535510856618f123e
Issue Description:
Many system frameworks originally written in Objective-C are accessible in Swift, but when dealing with bridged enums they can be easily misused.
For example, the following code produces nil:
import AppKit // or UIKit
import Foundation
let html = "<html></html>".data(using: .utf8)!
let attributed = try? NSAttributedString(data: html, options: [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8
], documentAttributes: nil)
print(attributed?.string)
Not because the HTML is invalid, but because String.Encoding.utf8 is an enum bridged from Objective-C and cannot be used as-is to call Objective-C APIs in Swift. Swift enum gets boxed to type __SwiftValue thus the Objective-C API has no idea how to deal with it.
Changing the code to use the same logic but with enum's raw value succeeds:
let html = "<html></html>".data(using: .utf8)!
let attributed = try? NSAttributedString(data: html, options: [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue
], documentAttributes: nil)
print(attributed?.string)
The error message provides literally no indication of the real failure as well:
Error Domain=NSCocoaErrorDomain
Code=256 "The file couldn’t be opened."
UserInfo=
{
NSUnderlyingError=0x100709ac0
{
Error Domain=NSCocoaErrorDomain
Code=4866 "The data couldn’t be written because it isn’t in the correct format."
UserInfo=
{
NSUnderlyingError=0x1007099f0
{
Error Domain=NSCocoaErrorDomain
Code=4864 "This decoder will only decode classes that adopt NSSecureCoding. Class '__SwiftValue' does not adopt it."
UserInfo=
{
NSDebugDescription=This decoder will only decode classes that adopt NSSecureCoding. Class '__SwiftValue' does not adopt it.
}
}
}
}
}
The only excuse for this behavior is that the documentation did state the key .characterEncoding in this dictionary should have a value of NSNumber (i.e. Swift numeric types, not enums). But this caveat is too easy to miss. The documented way is also counterintuitive, and does not exhibit the same behavior like other Objective-C types bridged to Swift (e.g. NSString/String), where corresponding Swift types can be used in system API calls.
Many third-party libraries written in Objective-C have the same problem. I've seen similar crash with SDWebImage, only their documentation has no indication that an NSNumber (or other raw values) should be used in place of Swift enums.