Include Inheritance Constraint in Swift Generic Types

Is it possible to add type constraints to a Swift protocol conformance extension?

EDIT: As noted in the updated question, this is now possible since Swift 4.1


This is not currently possible in Swift (as of Xcode 7.1). As the error indicates, you can't restrict protocol conformance ("inheritance clause") to a type-constrained extension. Maybe someday. I don't believe there's any deep reason for this to be impossible, but it's currently not implemented.

The closest you can get is to create a wrapper type such as:

struct FriendlyArray<Element: Friendly>: Friendly {
let array: [Element]
init(_ array: [Element]) {
self.array = array
}
func sayHi() {
for elem in array {
elem.sayHi()
}
}
}

let friendly: Friendly = FriendlyArray(["Foo", "Bar"])

(You would likely want to extend FriendlyArray to be a CollectionType.)

For a tale of my own descent into the madness of trying to make this work, and my crawl back from the edge, see NSData, My Old Friend.

How can I create a generic class that is constrainted to structs?

You can't (as of Swift 2.2).

It's simply not possible. There's no struct version of AnyObject, i.e. an AnyValue protocol (automatically implemented for all value types).

I've faced this exact problem myself, and I'm sorry to say that there's no solution other than to strictly abide by an informal protocol. Here's to hoping Swift 3 solves this.

Apple Swift: Generic type constraint for String type only not works

The point of generics is to allow our class to be of multiple different types. We can specify that our generic have some specific aspect.

For example, if we want equatable objects, we will specify this when declaring our generic:

class Foo<T: Equatable> {

}

Or perhaps we want objects that are of class Bar or one of its subclasses:

class Foo<T: Bar> {

}

The advantage to doing this with a class is we can still differentiate between an instance of Foo<ParentClass> and Foo<ChildClass>. If we didn't use generics, and specified our instance variables of type ParentClass, inheritance would allow us to still assign objects of ChildClass to it, but we wouldn't be able to compare objects of Foo to determine what type of Foo it is, unless we use generics. Consider an array of type [ParentClass], which can hold ParentClass or its subclasses, versus an array of type [ChildClass], which cannot hold objects of type ParentClass.


Now, back to your question of Foo<T: String>, the problem here is that String is a struct. It's not a class which can be inherited from, and it's not a protocol which other things can conform to. It's a struct. The only type that Foo can be is Foo<String> because nothing else is a String. And therefore Foo<T: String> is not allowed because it'd be nothing but confusing.

Compare this to the first example, of Foo<T: Equatable>, since things can conform to Equatable, I can instantiate many types of Foo:

let i = Foo<Int>()
let d = Foo<Double>()
let s = Foo<String>()
let c = Foo<Character>()

But if Foo<T: String> were allowed, the only thing I'd be able to instantiate is:

let s = Foo<String>()

And I'd never be able to instantiate any other type of Foo, so what's the point?

swift subclasses used in generics don't get called when inheriting from NSObject

I was able to confirm your results and submitted it as a bug, https://bugs.swift.org/browse/SR-10617. Turns out this is a known issue! I was informed (by good old Hamish) that I was duplicating https://bugs.swift.org/browse/SR-10285.

In my bug submission, I created a clean compact reduction of your example, suitable for sending to Apple:

protocol P {
init()
func doThing()
}

class Wrapper<T:P> {
func go() {
T().doThing()
}
}

class A : NSObject, P {
required override init() {}
func doThing() {
print("A")
}
}

class B : A {
required override init() {}
override func doThing() {
print("B")
}
}

Wrapper<B>().go()

On Xcode 9.2, we get "B". On Xcode 10.2, we get "A". That alone is enough to warrant a bug report.

In my report I listed three ways to work around the issue, all of which confirm that this is a bug (because none of them should make any difference):

  • make the generic parameterized type's constraint be A instead of P

  • or, mark the protocol P as @objc

  • or, don't have A inherit from NSObject


UPDATE: And it turns out (from Apple's own release notes) there's yet another way:

  • mark A's init as @nonobjc


Related Topics



Leave a reply



Submit