NSAttributedString.enumerateAttribute(_:in) crashes with custom attribute in macOS Sequoia

The following code crashes on macOS 15 Sequoia:

import Foundation

let key = NSAttributedString.Key("org.example.key")
let value = Value()
let string = NSMutableAttributedString()
string.append(NSAttributedString(string: "a", attributes: [:]))
string.append(NSAttributedString(string: "b", attributes: [key: value]))
string.append(NSAttributedString(string: "c", attributes: [:]))
string.enumerateAttribute(key, in: NSRange(location: 0, length: string.length)) { value, range, stop in
    print(range)
}

class Value: Equatable, Hashable {
    
    static func == (lhs: Value, rhs: Value) -> Bool {
        return lhs === rhs
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(ObjectIdentifier(self))
    }
    
}

The error is

EXC_BAD_ACCESS (code=1, address=0x0)

I wanted to run it on my external macOS 14 partition to confirm that it didn't crash before updating to macOS 15, but for some reason macOS will just restart and boot again into macOS 15. So I tried with macOS 13, which I was allowed to start for some reason, and I was able to confirm that the code doesn't crash.

Is this a known issue, and is there a workaround? Removing the two lines that add the letters a and c, or just declaring class Value without conformance to Equatable, Hashable, interestingly, solves the issue.

Answered by DTS Engineer in 806566022

The issue here is that your custom attribute value is a Swift class. If you change it to a subclass of NSObject, the code runs as expected:

class Value: NSObject {
    
    static func == (lhs: Value, rhs: Value) -> Bool {
        return lhs === rhs
    }
    
    override var hash: Int {
        Int(bitPattern: .init(self))
    }
}

I suspect this is fallout from our major rewrite of Foundation. See What’s next for Foundation for more on that.

If this is causing an existing product to fail in the field, I encourage you to file a bug about it. In that case, please post your bug number, just for the record.

If not, subclassing NSObject seems to be the right choice here for the long term. You are already neck deep in NSXxx classes anyway.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

AFAIK, you cannot use a custom key as:

NSAttributedString.Key("org.example.key")

The attributes key:value is predefined (see Xcode doc for NSAttributedString.Key).

And the value must correspond to the key.

What would you expect from

NSAttributedString(string: "b", attributes: [key: value])

What would you expect from

I use this to correlate a text range with a custom object that I need when the user modifies that range. The documentation says right in the first paragraph:

Attributed strings support many different kinds of attributes, including Custom attributes you define for your app

Accepted Answer

The issue here is that your custom attribute value is a Swift class. If you change it to a subclass of NSObject, the code runs as expected:

class Value: NSObject {
    
    static func == (lhs: Value, rhs: Value) -> Bool {
        return lhs === rhs
    }
    
    override var hash: Int {
        Int(bitPattern: .init(self))
    }
}

I suspect this is fallout from our major rewrite of Foundation. See What’s next for Foundation for more on that.

If this is causing an existing product to fail in the field, I encourage you to file a bug about it. In that case, please post your bug number, just for the record.

If not, subclassing NSObject seems to be the right choice here for the long term. You are already neck deep in NSXxx classes anyway.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

The issue here is that your custom attribute value is a Swift class. If you change it to a subclass of NSObject, the code runs as expected:

Works perfectly, thank you! I opened FB15296006.

I just wanted to add that the problem is caused by classes that don't inherit from NSObject. Initially, I interpreted this as "don't use Swift types at all". But I just noticed that my other project uses a Swift enum and it works fine.

NSAttributedString.enumerateAttribute(_:in) crashes with custom attribute in macOS Sequoia
 
 
Q