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
How to Catch an Exception in Swift
Does Cidetector for Other Barcode Types
How to Read a File from The Filesystem in a Swift Command Line App
Cllocationmanager and Tvos - Requestwheninuseauthorization() Not Prompting
How to Get The Advantages of Scenekit's Level Editor Programatically
Disable Bringing App Window to Front. After Closing Another Window
Spritekit: Sprites Are Moving Through Each Other with a Physicsbody Already Set
How to Reinterpret_Cast in Swift
Nsundomanager: Capturing Reference Types Possible
How to Use Protocols for Stucts to Emulate Classes Inheritance
How to Get an Array of Days Between Two Dates in Swift
Abstract Class and Abstract Function in Swift
Creating Semaphore with Initial Value of 0 Make Issues with Execution
How to Convert Curl Request to Swift Using Alamofire
Swift-Animate Cashapelayer Stroke Color
Swiching Between 2 Diferent Nsviewcontrollers with Data
Protocol Extension Doesn't Work with Rct_Export_View_Property