Search code examples
swiftgenericsinheritanceaccess-levels

Swift classes with generics allow superclasses to be inaccessible


In an iOS framework, if I create a public class with an internal superclass, I get an error:

internal class Animal { }

public class Dog: Animal {
    var name: String
    public init(name: String) {
        self.name = name
    }
}

Error: Class cannot be declared public because its superclass is internal

This makes sense. However, if I do the same thing but use a generic in the class declaration, no error is shown.

internal class Car<Type: Equatable> { }

public class Camry<Type: Equatable>: Car<Type> {
    var name: String
    public init(name: String) {
        self.name = name
    }
}

Is this just a bug? Should both of these examples actually be giving an error?

If you include the framework in a project, you can use the Camry class just fine, and the app will build and run on both a simulator and a device. However, I've noticed in certain cases that if you try to archive an app with these internal generic classes, you will sometimes get a Mach-O Linker Error.

Undefined symbols for architecture arm64

I haven't been able to nail down exactly why this occasionally happens though.

EDIT: The linker error happens when you have a public initializer in the internal class. So it is allowed to include code like this in a framework:

internal class Car<Type: Equatable> {
    var wheels: Type
    public init(wheels: Type) {
        self.wheels = wheels
    }
}

public class Camry<Type: Equatable>: Car<Type> {
    var name: String
    public init(name: String, wheels: Type) {
        self.name = name
        super.init(wheels: wheels)
    }
}

And you could use Camry<Int>(name: "My Camry", wheels: 4) without a problem in an app that included that framework, but when you try to archive the project, you'll get a linker error.


Solution

  • As suggested by Rob Napier, this is indeed a bug. It was accepted as issue SR-6206 on bugs.swift.org and should be fixed in Swift 4.1.