swiftcocoaxpcnsxpcconnection

NSXPCInterface setClasses causes "Lazily named class 0x600000xxxxxx wasn’t named by lazy name handler" on Xcode 14+ Swift @objc enum


After updating to Xcode 14 I am getting the SIGABRT crash "Lazily named class 0x600000dc6520 wasn’t named by lazy name handler". In the latest version of Xcode 13 it compiles and runs without any flaw. The only thing different from the very basic is that I use the MyAppSharedObjects to get across more complex objects between the XPC Service and the main app. Apart from that it doesn't look like anything beyond tutorial level NSXPCInterface code to me:

import MyAppScriptSandbox
import MyAppSharedObjects
import Foundation

class ScriptExecutor {
    init?() {
        let incomingClasses = NSSet(array: [
            NSArray.self,
            NSString.self,
            NSValue.self,
            NSNumber.self,
            NSData.self,
            NSDate.self,
            NSNull.self,
            NSURL.self,
            NSUUID.self,
            NSError.self,
            NSDictionary.self,
            ScriptSandboxReply.self,
            AppleScriptError.self,
            AppleScriptErrorType.self
        ]) as Set

        let remoteInterface = NSXPCInterface(with: MyAppScriptSandboxProtocol.self)
        remoteInterface.setClasses( // **** CRASH HAPPENS HERE ****
            incomingClasses,
            for: #selector(MyAppScriptSandboxProtocol.execute(script:withReply:)),
            argumentIndex: 0,
            ofReply: true
        )

import Foundation
import MyAppSharedObjects

@objc
public protocol MyAppScriptSandboxProtocol {
    func execute(
        script: String,
        withReply reply: @escaping (ScriptSandboxReply) -> Void
    )
    func terminate()
}

@objc(ScriptSandboxReply)
public class ScriptSandboxReply: NSObject, NSSecureCoding {
    public static let supportsSecureCoding = true

    public func encode(with coder: NSCoder) {
        // Removed company specific code
    }

    required public init?(coder: NSCoder) {
        // Removed company specific code
    }
}

This data type was the issue:

@objc(AppleScriptErrorType)
public enum AppleScriptErrorType: Int {
    case error
    case noResult
    case errorNorResult // This really shouldn't happen IRL

    static let key = "AppleScriptErrorType"
}

Solution

  • After a bit of dabbing I found the issue. I started to comment out custom data types in the incomingClasses list and while the application crashed upon the first received message it stopped crashing on init. By uncommenting the custom data types one by one I finally found the culprit:

    @objc(AppleScriptErrorType)
    public enum AppleScriptErrorType: Int {
        case error
        case noResult
        case errorNorResult // This really shouldn't happen IRL
    
        static let key = "AppleScriptErrorType"
    }
    

    I have no idea why this perfectly sane enum type could not be transferred anymore after Xcode 14 dropped while it worked in Xcode 13.4.1, I think it might be an Apple bug. However I could get on with my life using the rawValue (so just the int value really) to send the value from one side and to reconstruct the AppleScriptErrorType enum on the receiving side.