Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift thinks I'm subclassing NSSet wrongly but I'm not subclassing it at all

I'm getting a bizarre error message from the Swift compiler:

<unknown>:0: error: 'required' initializer 'init(arrayLiteral:)' must be provided by subclass of 'NSSet'
Foundation.NSSet:2:33: note: 'required' initializer is declared in superclass here
    required public convenience init(arrayLiteral elements: Any...)
                                ^

Note that none of my code is subclassing NSSet (nor Set). So I have no idea why Swift thinks I was doing it at all, not to mention wrongly.

My project is a little special in that I have Objective-C classes that are calling C++ code (through Objective-C) and a module.map file to let Swift call into a C library.

The error occurs when compiling Swift files (I get it from several Swift files, but only some of them use the C library, but all of them so far seem to be talking to ObjC classes). As you can see, Swift has no line number for its error where someone is subclassing NSSet though.

Has anyone seen this? Does anyone know how to diagnose this issue? I tried commenting out bits of the code, but I can't seem to find anything that helps track this issue down.

To emphasize: I am not doing any subclassing of NSSet. I'm not even using NSSet in my code, I think. That is something the Swift compiler just seems to be making up.

Update: Another question gave me the idea that it may be related to instantiating an instance from a Class object. I have a class that has a Class that I later want to instantiate:

@interface MYInfo : NSObject

@property Class myClass;

@end

Later I use that from Swift to instantiate it:

guard let myClass = info.myClass.init() as? MyClass else { return }

If I remove this last line, the error message disappears.

like image 629
uliwitness Avatar asked Apr 24 '19 13:04

uliwitness


Video Answer


1 Answers

So figured it out, thanks to that other question (which is quite different though):

  1. Swift Compiler is REALLY stupid when you try to init a variable typed as ObjC's Class type and presents the misleading error 'required' initializer 'init(arrayLiteral:)' must be provided by subclass of 'NSSet' even though NSSet is not involved. I guess NSSet is the first or last ObjC init method it knows or something.

  2. Swift compiler also shows this error on random source files (even empty ones!) because of the "magic" way in which Swift parses all other files in the project so you can reference classes in them without an include statement like C has it.

  3. The correct way to instantiate a Class is to first typecast it:

guard let myClass = info.myClass as? (NSObject & MyClass).Type else { return }
let myInstance = myClass.init()

This was way harder to figure out than it should have been.

like image 78
uliwitness Avatar answered Sep 22 '22 05:09

uliwitness