Protocol inheritance from class? - swift

I read the documentation about Protocols and can't find any information about protocol inheriting from a class, but the code compiles. As far as I remember protocols can only inherit other protocols, I have never seen protocol that inherits from a class. I don't even know a language that allows such behaviour.
class A {
}
protocol X: A {
}
// forced to inherit from class A, because of X protocol
class B: A, X {
}
Is it some kind of bug?

Implemented In Swift 5: From the Swift 5 Release notes
Protocols can now constrain their conforming types to those that subclass a given class. Two equivalent forms are supported:
protocol MyView: UIView { /*...*/ }
protocol MyView where Self: UIView { /*...*/ }
See this tweet by John Sundell, showing a possible use case

Related

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

Partial Solution Update at the end!
Attached is code that produces odd behavior. I copied it out of a swift playground so it should run in one fine.
I created a subclass in my project and passed it to my generic class as the concrete type. However, I quickly noticed that only the base class methods are called. This is shown with myBase and mySub below. Despite the generic class being instantiated as <mySub>, only the base methods are called. The print lines for the subclass are never shown.
Well, I found a simple way around that and that is to not inherit from NSObject. When I used swift native classes, the subclass methods are in fact called. These are secondBase and secondSub.
How do I pass a subclass into a generic class and get the actual subclass to receive calls when inheriting from NSObject?
And why would behavior be different?
import Foundation
// The Protocol
protocol P {
init ()
func doWork() -> String
}
// Generic Class
class G<T: P> {
func doThing() -> String {
let thing = T()
return thing.doWork()
}
}
// NSObject Base Class with Protocol
class A1: NSObject, P {
override required init() {
super.init()
}
func doWork() -> String {
return "A1"
}
}
// NSObject Sub Class
class B1: A1 {
required init() {
super.init()
}
override func doWork() -> String {
return "B1"
}
}
// Swift Base Class
class A2: P {
required init() {
}
func doWork() -> String {
return "A2"
}
}
// Swift Sub Class
class B2: A2 {
required init() {
super.init()
}
override func doWork() -> String {
return "B2"
}
}
print ("Sub class failure with NSObject")
print ("Recieved: " + G<B1>().doThing() + " Expected: B1 - NSObject Sub Class Generic (FAILS)")
print ("\nSub class success with Swift Native")
print ("Recieved: " + G<B2>().doThing() + " Expected: B2 - Swift Sub Class Generic (SUCCEEDS)")
print("")
#if swift(>=5.0)
print("Hello, Swift 5.0")
#elseif swift(>=4.1)
print("Hello, Swift 4.1")
#elseif swift(>=4.0)
print("Hello, Swift 4.0")
#elseif swift(>=3.0)
print("Hello, Swift 3.x")
#else
print("Hello, Swift 2.2")
#endif
Output:
Sub class failure with NSObject
Recieved: A1 Expected: B1 - NSObject Sub Class Generic (FAILS)
Sub class success with Swift Native
Recieved: B2 Expected: B2 - Swift Sub Class Generic (SUCCEEDS)
Hello, Swift 5.0
Partial solution update:
Moving the protocol conformance from the base class to the sub class allows the sub class to behave correctly. Definitions become:
class A1: NSObject
class B1: A1, P
The problem is the base class can no longer be used directly at all when no functionality beyond it is needed. This is mostly an issue if the protocol being conformed to has an associated type. When this is true, you must have a concrete class that conforms to the protocol for use in generics.
One use case here is expecting a base class in the generics (with a protocol involving an associated type) which allows something to function without caring what actual sub class was passed in. This actually ends up being a poor man's form of type erasure in some cases. And you can still use the same generic with the subclass.
G<A1>()
G<B1>()
This was derived from a similar question here: Generic Class does not forward delegate calls to concrete subclass
Partial options are:
remove NSObject and use swift native classes only
when NSObject is required, try to separate protocol conformance from inheritance of NSObject
UPDATE ON THE BELOW IDEA: Doesn't Work
I am going to test if providing an additional layer changes the behavior. Basically have 3 layers, Base class inheriting from NSObject, base Protocol class adding the protocol but inheriting from base and then specific classes. If it can distinguish between the base protocol class and the specific subclass in that case, that would be a functional workaround in all use cases. (and may explain why Apple's NSManagedObject works fine)
Still seems like a bug though.
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
This is not an answer so much as a way to avoid the problem.
In most of my code, I did not have to conform to NSObjectProtocol only Equatable and/or Hashable. I have implemented those protocols on the objects that needed it.
I then went through my code, removed all NSObject inheritance except on those classes which inherit from an Apple protocol or object that requires it (Like UITableViewDataSource).
The classes that are required to inherit from NSObject are Generic but they are not typically handed into other Generic classes. Therefore the inheritance works fine. In my MVVM pattern these tend to be the intermediate classes that work with view controllers to make logic like table views reusable. I have a tableController class that conforms to the UITableView protocols and accepts 3 generic viewModel types allowing it to provide the table logic for 95% of my views with no modifications. And when it needs it, subclasses easily provide alternate logic.
This is a better strategy as I am no longer randomly using NSObject for no reason.
This is a second way to avoid the problem.
#matt originally suggested this but then deleted his answer. It is a good way to avoid the problem. His answer was simple. Mark the protocol with objc like this:
// The Protocol
#objc protocol P {
init ()
func doWork() -> String
}
This solves the above sample code and you now get the expected results. But doing this has side effects for swift. At least one of those is here:
How to use #objc protocol with optional and extensions at the same time?
For me, it began a chain of having to make all my protocols objc compatible. That made the change not worth it for my code base. I was also using extensions.
I decided to stay with my original answer at least until Apple fixes this bug or there is a less invasive solution.
I thought this one should be documented in case it helps someone else facing this problem.

Inheritance in Class Vs Protocol

I am little bit messed up with the following concept:
Code 1 :
class New{
func abc(){
print("new class")
}
}
class ClassNew: New {
override func abc() {
print("derived class")
}
}
Code 2:
protocol New{}
extension New{
func abc(){
print("new protocol")
}
}
class ClassNew: New {
func abc() {
print("derived protocol")
}
}
What is the difference between Code 1 and Code 2 as both of them serves the same purpose?
In code 2, classNew is inheriting from new protocol or just conforming to the protocol?
Any Explanation will be highly appreciated!
Code 1 and code 2 are fundamentally not the same.
What is the difference between Code 1 and Code 2 as both of them serves the same purpose?
No they don't. The first one defines a class hierarchy, the second defines a protocol (an API, if you like) and a type that conforms to it.
In code 2, classNew is inheriting from new protocol or just conforming to the protocol?
It's conforming to the protocol. There's no inheritance involved (if you are being pedantic).
Code 1 defines a base class and a class that inherits from it. The subclass overrides the base class for the function abc() and it behaves as you would expect given a class hierarchy i.e.
let x: New = ClassNew()
let y: ClassNew = ClassNew()
print(x.abc()) // prints "derived class"
print(y.abc()) // prints "derived class"
Both print statements call the derived class's abc()
In code 2 you define a protocol with no methods, and an extension to the protocol with an extension method. Note that this is not a "default method" there is nothing in the protocol to default. You then define a class that conforms to the protocol and adds a new method that happens to have the same name as the extension method. The distinction (from the pure class hierarchy) is important because the version of abc() called is determined statically at compile time
protocol New2{}
extension New2{
func abc(){
print("new protocol")
}
}
class ClassNew2: New2 {
func abc() {
print("derived protocol")
}
}
let y2: ClassNew2 = ClassNew2()
let x2: New2 = y2
print(x2.abc()) // prints "new protocol"
print(y2.abc()) // prints "derived protocol"
Even though x2 and y2 are the same object different versions of the function are called. This is because the compiler is not allowed to assume anything about x2 except what it can infer from the protocol. So it doesn't know that the object has anabc() of its own so it must call the extension function.
If you had defined the protocol like this:
protocol New3{
func abc()
}
extension New3{
func abc(){
print("new protocol")
}
}
class ClassNew3: New3 {
func abc() {
print("derived protocol")
}
}
let y3: ClassNew3 = ClassNew3()
let x3: New3 = y3
print(x3.abc()) // prints "derived protocol"
print(y3.abc()) // prints "derived protocol"
This time the compiler knows that the object should have a function abc() and will only use the extension function if it doesn't. Then the normal inheritance rules for classes apply.
Code 2 is an example of protocol extensions. Big difference is that you can't call super.abc() in Code 2 — you have to provide a method implementation that is not in any super context.
Just my opinion — do not use protocol default implementation, as compiler won't save you if you forget to provide "override" when you really need one.
classNew is simply conforming to the protocol in the second code. This is true to every protocol in Swift as inheritance is not currently supported with protocols.
This also answers your first question: there is no significant difference between the two codes in this use case, but, generally speaking, a protocol is good for other kinds of things than subclassing.
Code 1 and Code 2 are exactly same, just conceptually different.
If we put it in simple words, a Class defines the object itself, and a Protocol defines the behaviour of the object.
If compared to Java, a protocol is similar to Interfaces.
Checkout the Protocol Documentation

Can a protocol require conforming types to be a subtype of another type?

For example: I'd like to say in a protocol definition that, a class conforming it, needs to also subclass UIView or another custom class type MyClass. Is that possible?
Yes you can, just define your protocol as:
protocol SomeProtocol where Self: MyClass {
// protocol code here
}

How to make a generic class conform to a protocol for specific type?

Say there exists a generic struct:
public struct Matrix<T> where T: FloatingPoint, T: ExpressibleByFloatLiteral {
// some methods...
}
Is it possible extend the struct to conform to a protocol for constrained T using where clauses? E.g. something like
extension Matrix where T: SpecificClass : SomeProtocol {
// This does not compile :(
}
No, such construct isn't possible (circa Swift 3.1 at least).
For instance:
class SomeClass { }
protocol SomeProtocol { }
extension Matrix: SomeProtocol where T == SomeClass { }
Gives a very clear error message:
Extension of type Matrix with constraints cannot have an inheritance clause.
But all is not lost... as correctly noted by Alexander, there is already a proposal lined up for Swift 4! The feature will be called Conditional Conformances (SE-0143).
A nice example for all protocol-oriented programming hackers out there:
extension Array: Equatable where Element: Equatable {
...
}
If an array contains equatable elements than said array is also equatable.
Update. Swift 4 is out but this feature hasn’t landed yet. We may need to wait until Swift 5 for this...

Swift protocol allows unimplemented function when it exists in base class

Assume the below playground:
protocol MyProtocol
{
func MyFunc()
}
class MyBase : MyProtocol
{
func MyFunc()
{
}
}
class MyClass : MyBase, MyProtocol
{
}
I was expecting this to not compile, and nag about not implementing my protocol. But unlike other languages, Swift seems to think its fine if the protocol conforms to the base class.
How can I get MyClass to force my method to be overridden?
I'm using Swift 1.1, tia.
Your code is definitely valid. MyClass inherits from MyBase which already conforms to MyProtocol, so having the subclass also conform to it is completely redundant. That being said, you are basically looking for an abstract function which does not really exist in Swift, the closest I think you can get to achieving what you want is recommended in this answer. So in your base class just give a warning if not overriden:
class MyBase : MyProtocol {
func MyFunc() {
fatalError("This method must be overridden")
}
}
As recommended in the question comments, a different approach may work better for what you are attempting to do (e.g. composition or using a struct), but it is a bit hard to recommend a different solution without knowing more about what you are trying to achieve (rather than just the expected solution). If you do provide additional details you may receive a more suitable answer.

Categories