1

I have a framework target in which most of the classes are written in Objective C. Recently we have started introducing Swift files in the code. We make private Objective C files available to swift code using modules(more on this can be found here).

This approach worked well until recently when I tried subclassing one of my Objective C class using Swift, I got an error in the Generated MyFramework-Swift.h file which said "Module TestSwift not found" where TestSwift is the name of the module I provided in the modulemap file. However, if I try subclassing the classes which are listed in the umbrella header of my framework(public classes), it works.

import TestSwift

@objc public class NewSwiftClass: ExistingObjectiveCClass {
    //throws error in the generated MyFramework-Swift.h file while compiling
}

If I keep my swift class internal, it works

import TestSwift

@objc class NewSwiftClass: ExistingObjectiveCClass {
    //works fine
}

but I would like to use this Swift class in my Objective C files hence cannot keep it internal.

TL;DR: I'm unable to subclass an existing Objective C class using Swift inside a framework target.

Sagar D
  • 2,588
  • 1
  • 18
  • 31

1 Answers1

1

I believe this is impossible in Swift because it's impossible in Objective-C.

If you have a class A in your framework that is not part of your umbrella header, and you want B to subclass it and be in your umbrella header, you can't do it.

You have to declare the inheritance in your interface declaration @interface B: A, which goes in B's header and thus in the umbrella header. But the compiler is going to complain: "What is A?" You could import A's header there, but unlike Swift's import, Objective-C's #import literally drops the contents of A's header into the B header. Which means A is now in the umbrella header too i.e. public.

Mixing Swift with Objective-C isn't magic. The compiler still needs to be able to make a valid Objective-C header that accurately describes the Swift interface. So unless you can think of a way to make Objective-C do this, you can't do it in Swift.

The only alternative I can think of is to change your "is a" relationship into a "has a" relationship i.e.

@objc public class NewSwiftClass {
    let parent: ExistingObjectiveCClass
}

obviously you lose most of the benefits of actual inheritance but you'll still have the parent around as a substitute for super. You could also declare a public protocol that both classes conform to to ensure that you get consistency between their methods.

Max
  • 21,123
  • 5
  • 49
  • 71
  • 1
    Thanks, @Max So if I understand correctly, I cannot subclass an Objective c class using a swift class until 1. I make the objective c interface (i.e. the .h file) public 2. I make the swift class internal. – Sagar D Jul 09 '20 at 13:40
  • But if we go according to this analogy, I wonder how can I declare a public property of the same private objc class in my public swift class? How does that work? – Sagar D Jul 10 '20 at 04:43
  • @SagarD without a qualifier, as in my answer, the property is `internal` and not visible outside the framework. You _might_ have to explicitly declare it as private if the compiler complains – Max Jul 10 '20 at 13:46