Swift protocols behavior - swift

Let's assume I have protocol FooProtocol in Swift with one method with its default implementation:
protocol FooProtocol {
func foo()
}
extension FooProtocol {
func foo() {
print("protocol")
}
}
and class FooClass with cast instance:
class FooClass : FooProtocol {
func foo() {
print("class")
}
}
let A = FooClass() as FooProtocol
A.foo()
As expected, "class" will be printed in console.
However, if I make foo() method of my protocol being optional - "protocol" will be printed instead.
#objc protocol FooProtocol {
#objc optional func foo()
}
But if I call A.foo?() rather than A.foo() - "class" will be printed again.
I wonder, just for theoretical purposes, what the hell is happening here? Any explanations are appreciated.

You cannot call optional protocol method without optional chaining. I.e. if you comment out
extension FooProtocol {
func foo() {
...
and you try to call A.foo(), you will get a compilation error:
value of optional type '(() -> ())?' must be unwrapped to a value of type '() -> ()'
As you see the signature of optional is not even the same as non-optional. So by calling A.foo(), you are not calling method you implemented, you are calling default protocol implementation directly.
It also makes sense since primary reason for #objc optional is interpolation with Objective C, where default protocol implementation won't be visible. So relying on protocol function for objc function would produce different result on swift and objective c, which is undesired.
I cannot say if there's any loophole to use both optional and default protocol implementation on the same function. But question is: do you really need it?
If you are concerned with interpolation, you need equal solution for both Swift and Objective c, and that means you rather need basic protocol implementation, which both Swift and objc can inherit
If you need no interpolation, you don't really need optional, and can rely on default protocol implementation alone.

Related

Accessing optional method in protocol returns scope error

I've exhausted every SO thread about this topic and haven't gotten anywhere with this error.
Code Setup:
#objc protocol MyProtocol: NSObjectProtocl{
#objc optional func myFunction()
}
class MyClass: NSObject, MyProtocol {
func doSomething() {
myFunction?() //Error: cannot find myFunction in scope.
self.myFunction() //Error: Value of type 'MyClass' has no member 'myFunction'
}
}
Things I've tried:
Using responds(to:) to check selector. The same error pops up when calling the function afterward.
if self.responds(to: #selector(MyProtocol.myFunction)){
self.myFunction?() //Error: Value of type 'MyClass' has no member 'myFunction'
}
Using if let to check if the function is implemented runs into the same error above.
Giving the optional function a return type #objc optional func myFunction() -> NSError?
Using a default implementation works, but doesn't allow me to use the #objc tag.
extension MyProtocol {
#objc func myFunction(){ //Error: #objc can only be used with members of classes, #objc protocols, and concrete extensions of classes.
}
}
Other Objective-C implementations that use MyClass need myFunction, so I need to expose myFunction with #objc.
Do I have to implement myFunction inside MyClass if I want to use it. This problem is when converting a file from Obj-C to Swift, where Obj-C allows calling optional methods without having to check their implementation.
class MyClass: NSObject, MyProtocol { ... }
This says that MyClass implements MyProtocol, which allows, but does not require myFunction. The compiler can clearly see that MyClass does not implement myFunction, so self.myFunction() isn't a thing. It could be a thing, but it's clearly not a thing. The protocol doesn't play into this.
You could "work around" this by saying "I don't mean MyClass; I mean MyProtocol":
func doSomething() {
(self as MyProtocol).myFunction?()
}
But this is kind of silly IMO. self doesn't implement myFunction and this is known at compile time.
If the point is to implement this via subclassing, the correct way to do that is to just implement it as empty in the superclass:
class MyClass: NSObject, MyProtocol {
func myFunction() {}
func doSomething() {
myFunction()
}
}
This is precisely equivalent to "do nothing" while allowing overrides in subclasses.

Swift Protocol inheriting Protocol method with same name

While reading swift forum about exceptions I found interesting issue. One of the examples about exceptions was something like this:
protocol Base {
func foo() throws -> Int
}
protocol Refined: Base {
func foo() -> Int
}
struct Test: Refined {
func foo() -> Int {
0
}
}
It's interesting, I thought it was typo that it would not compile, but it does. I am not sure how this does work behind the scenes. I mean when protocol adopts another protocol it adopts also its requirements. But in this case declaring same method without throws somehow satisfies also first protocol Base.
At the very least I expected Test to need to have 2 implementations of foo. What am I missing here?
This is because a non-throwing function is by definition a sub-type of a throwing function
From the Swift Programming Language book
The throws keyword is part of a function’s type, and nonthrowing functions are subtypes of throwing functions. As a result, you can use a nonthrowing function in the same places as a throwing one.
But you can't do it the other way around so the below code will generate an error
protocol Base {
func foo() -> Int
}
protocol Refined: Base {
func foo() throws -> Int //error: Cannot override non-throwing method with throwing method
}
Also note that this is not only for protocols, if you remove the func declaration from Refined you can still implement the function in Test as non throwing.

Swift: Passing conformed object instances to generic methods

I have the following playground:
// : Playground - noun: a place where people can play
import Foundation
// Define a protocol
protocol MyProtocol
{
func getMeAString() -> String!
}
// Create a class that conforms to the protocol
class ClassThatConformsToProtocol: MyProtocol
{
func getMeAString() -> String! {
return "hey!"
}
}
// Define a function that takes a generic, but ensure the generic conforms to the protocol
func logTheThing<T: MyProtocol>(theThing: T!)
{
theThing.getMeAString()
}
// Let's create an instance
let instanceOfClassThatConforms = ClassThatConformsToProtocol()
// We're now going to see if we can pass the object to our generic method
logTheThing(instanceOfClassThatConforms)
// It works!
// Let's create another method, but this one only knows the protocol its parameter conforms to, the in implementing class
func takeInAnObjectThatWeOnlyKnowItsProtocol(object:MyProtocol)
{
logTheThing(object) // error: cannot convert value of type 'MyProtocol' to expected argument type '_!'
}
As mentioned, I receive an error stating:
error: cannot convert value of type 'MyProtocol' to expected argument
type '_!'
For what reason would I not be able to pass this conformed object into the generic method?
If the compiler knows an object conforms to the protocol, for what reason would I NOT be able to pass it into a generic method?
A protocol does not conform to itself. You must make this generic:
func takeInAnObjectThatWeOnlyKnowItsProtocol<T: MyProtocol>(object: T)
{
logTheThing(object)
}
To the next question: why doesn't a protocol conform to itself? Because it doesn't. Eventually it probably will, but it doesn't today.
That said, given this specific code, there's no reason to make this generic at all. Just pass the protocol and it'll do exactly what you want:
func logTheThing(theThing: MyProtocol) {
theThing.getMeAString()
}
func takeInAnObjectThatWeOnlyKnowItsProtocol(object: MyProtocol) {
logTheThing(object)
}
Passing object: MyProtocol means "any type that conforms to MyProtocol" and that matches in both places. Passing <T: MyProtocol>(object: T) means "a specific, concrete type that conforms to MyProtocol" and "any type that conforms to MyProtocol" is not a "specific, concrete type" and so fails (today; again, they'll probably fix that some day).
(Unrelated note: There is never a good reason to return String! in Swift. Just return String. And you'd get a better error message and fewer other little problems if you don't use T! and just use T in your logging call. Unless you're bridging to ObjC, there is almost never a reason to pass or return ! types. They only make sense in pure Swift for properties.)

Protocol extensions on Structs causes compile error 'Self' constrained to non-protocol type

I'm attempting to apply a constrained protocol extension to a struct (Swift 2.0) and receiving the following compiler error:
type 'Self' constrained to non-protocol type 'Foo'
struct Foo: MyProtocol {
let myVar: String
init(myVar: String) {
self.myVar = myVar
}
}
protocol MyProtocol {
func bar()
}
extension MyProtocol where Self: Foo {
func bar() {
print(myVar)
}
}
let foo = Foo(myVar: "Hello, Protocol")
foo.bar()
I can fix this error by changing struct Foo to class Foo but I don't understand why this works. Why can't I do a where Self: constrained protocol a struct?
This is an expected behaviour considering struct are not meant to be inherited which : notation stands for.
The correct way to achieve what you described would be something like equality sign like:
extension MyProtocol where Self == Foo {
func bar() {
print(myVar)
}
}
But this doesn't compile for some stupid reason like:
Same-type requirement makes generic parameter Self non-generic
For what it's worth, you can achieve the same result with the following:
protocol FooProtocol {
var myVar: String { get }
}
struct Foo: FooProtocol, MyProtocol {
let myVar: String
}
protocol MyProtocol {}
extension MyProtocol where Self: FooProtocol {
func bar() {
print(myVar)
}
}
where FooProtocol is fake protocol which only Foo should extend.
Many third-party libraries that try to extend standard library's struct types (eg. Optional) makes use of workaround like the above.
I just ran into this problem too. Although I too would like a better understanding of why this is so, the Swift language reference (the guide says nothing about this) has the following from the Generic Parameters section:
Where Clauses
You can specify additional requirements on type parameters and their
associated types by including a where clause after the generic
parameter list. A where clause consists of the where keyword, followed
by a comma-separated list of one or more requirements.
The requirements in a where clause specify that a type parameter
inherits from a class or conforms to a protocol or protocol
composition. Although the where clause provides syntactic sugar for
expressing simple constraints on type parameters (for instance, T:
Comparable is equivalent to T where T: Comparable and so on), you can
use it to provide more complex constraints on type parameters and
their associated types. For instance, you can express the constraints
that a generic type T inherits from a class C and conforms to a
protocol P as <T where T: C, T: P>.
So 'Self' cannot be a struct or emum it seems, which is a shame. Presumably there is a language design reason for this. The compiler error message could certainly be clearer though.
As Foo is an existing type, you could simply extend it this way:
struct Foo { // <== remove MyProtocol
let myVar: String
init(myVar: String) {
self.myVar = myVar
}
}
// extending the type
extension Foo: MyProtocol {
func bar() {
print(myVar)
}
}
From The Swift Programming Language (Swift 2.2):
If you define an extension to add new functionality to an existing type, the new functionality will be available on all existing instances of that type, even if they were created before the extension was defined.

Swift: cannot mix protocols with generics

Say I have the following code:
protocol A {}
struct B: A {}
func b() -> A {
return B()
}
func f<T where T: A>(t: T) -> T {
return t
}
f(b())
This results in the following error (Xcode 6.3 beta 3):
Playground execution failed: Test.playground:12:1: error: generic parameter 'T' cannot be bound to non-#objc protocol type 'A'
f(b())
^
Test.playground:8:6: note: in call to function 'f'
func f<T where T: A>(t: T) -> T {
^
I feel like this is a shortcoming with the implementation of generics in Swift. Is there a way to nicely work around this issue while still keeping the generics?
In my code f() has additional type requirements; I could just forget about protocols and generics altogether, but I don't accept defeat that easy ;-).
Implicite down casting might not be allowed here, you need to force it in the implementation of method b() :
func b () -> A {
return B() as A
}
If you mark the protocol with #objc and change the struct to a class it will work, because then you will be in Objective C compatible territory for which different rules exist.
The reason is that in Swift protocols are not considered to be concrete implementations of themselves due to possible static requirements of the protocol and/or associated types. In my point of view this is indeed a (very annoying) shortcoming of Swift. See also issue:
Protocol doesn't conform to itself?