Skip to content

[SR-15619] Objective-C Enums Bridged to Swift Should Be Unwrapped Automatically #3187

Open
@swift-ci

Description

@swift-ci
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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions