Sagar D
Sagar D

Reputation: 2618

Subclassing an Objective C class using Swift in a framework target

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.

Upvotes: 1

Views: 983

Answers (1)

Max
Max

Reputation: 22325

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.

Upvotes: 1

Related Questions