Swift 1.2 protocols and optional functions

Before upgrading to Xcode 6.4 and Swift 1.2, the following code worked in playground:


@objc protocol Speaker {
    func Speak()
    optional func TellJoke()
}

class Deb: Speaker {
    func Speak() {
        println("Hello, I am Deb!")
    }
    func TellJoke() {
            println("Q: What did Sushi A say to Sushi B?")
    }
}

class Bryan: Speaker {
    func Speak() {
      println("Yo, I am Bryan!")
    }
    func TellJoke() {
        println("Q: What's the object-oriented way to become wealthy?")
    }
    func WriteTutorial() {
        println("I'm on it!")
    }
}

var speaker:Speaker
speaker = Bryan()
speaker.Speak()
(speaker as Bryan).WriteTutorial()
speaker = Deb()
speaker.Speak()
speaker.TellJoke?()

Now after upgrading I had to change the class declarations as below and the optional chaining (line 33 above) always returns nil. In order to get it to work, I have to downcast it to the specific class which sort of defeats the purpose:

@objc protocol Speaker {
    func Speak()
    optional func TellJoke()
}
class Deb: Speaker {
    @objc func Speak() { //had to put @objc in front of required functions in order to compile
        println("Hello, I am Deb!")
    }
    func TellJoke() {
        println("Q: What did Sushi A say to Sushi B?")
    }
}
class Bryan:Speaker {
    @objc func Speak() { //had to put @objc in front of required functions in order to compile
        println("Yo, I'm Bryan!")
    }
    func TellJoke() {
        println("Q: What's the object-oriented way to become wealthy? ")
    }
    func WriteTutorial() {
        println("I'm on it!")
    }
}

var speaker:Speaker
speaker = Deb()
speaker.TellJoke?() //always returns nil regardless of whether it implements the optional method now.
(speaker as! Deb).TellJoke() //required to get it to call the optional method but seems to defeat the purpose of optional methods


I haven't found any notice/documentation about now having to add @objc before required methods of protocols when implementing them if there is a mix of required and optional methods. In addition, the bigger problem is the optional chaining not working when calling optional methods on an object of a protocol type.


What am I missing?

Accepted Answer

You would need to put @objc in front of the optional methods too, in order for them to be accessible from the @objc protocol.

Thanks for the info and that works. Just curious, did something change (I am using Swift only) between 1.1 and 1.2 as with 1.1, the @objc wasn't required to get the optional chaining to work or to get to compile (other than before the actual protocol definition)?

In a thread on the previous developer forums, an Apple employee stated that the use of @objc for protocols with optional requirements was due to a limitation in the current Swift runtime, which was planned to be addressed in a later version.


So until Swift supports optional protocol requirements and protocol conformance checking natively, it's using the support in the objc runtime as a workaround.


I think there have been various changes in determining what is exposed to objc by default, and 1.1 must have been more lenient about methods from non-objc classes.

Just curious, why can protocols provided by Apple define optional functions without @objc ?

For example, UIImagePickerControllerDelegate has two optional functions, but there are no @objc.

I tried to omit "@objc" by making my own protocol inherit from NSObjectProtocol, but I couldn't.

Apple's frameworks are still written in objective-C, so even if the version of the header generated for Swift isn't explicitly marked as @objc, the code behind it is running on the objective-C runtime anyway.


Adding @objc to your Swift code tells the compiler to do whatever it needs to do to make your Swift code compatible with objective-C, so that your Swift protocol can use the objective-C runtime to make optional methods work. Anything written in obj-c is already compatible.

Swift 1.2 protocols and optional functions
 
 
Q