Search code examples
objective-cswiftnsobject

How to use NSSet<Class> in Swift (exported as Set<NSObject>)?


I have to fulfill a protocol requirement that is defined in Objective-C like this:

@protocol AProtocol <NSObject>
+ (NSSet<Class> * _Nullable)someClasses;
@end

I want to implement this protocol in a subclass written in Swift. I want to return a Set of classes of another Object. The class I want to return is defined like this:

class B: NSObject {}

The class that conforms to the protocol is defined like this:

class A: NSObject, AProtocol {
    static func someClasses() -> Set<NSObject>? {
        return [B.self]
    }
}

Why is NSSet<Class> bridged to Set<NSObject> instead of Set?

This solution is crashing, how can I solve the problem?


Solution

  • NSSet<Class> is bridged to Set<NSObject> because AnyClass does not conform to Hashable which is a precondition for the ValueType of Set.

    It can be solved with the following extension for NSObjectProtocol:

    extension NSObjectProtocol where Self: NSObject {
        static var objcClass: NSObject {
            return (self as AnyObject) as! NSObject
        }
    }
    

    This returns the class of the object casted as NSObject. It is necessary to cast it first to AnyObject because the type system of Swift is so strong that it would not compile or give a warning when directly casting a type to an instance type. In Objective-C this is fine because Class is also just an object. Since NSObject is implemented in Objective-C and the extension is just for NSObjectProtocol, this is save to use (even with the force cast). Implementing the extension on NSObjectProtocol and not on NSObject itself brings the positive effect that it is not exported to Objective-C.