Swift Protocol Function Overloading - ios

Is it possible to overload a protocol function and have the correct definition be called when dealing directly with the protocol type?
Here's some code to illustrate the issue
protocol SomeProtocol {
func doSomething<T>(obj: T)
}
class SomeClass : SomeProtocol {
func doSomething<T>(obj: T) {
print("Generic Method")
}
func doSomething(obj: String) {
print(obj)
}
}
let testClass = SomeClass()
testClass.doSomething("I will use the string specific method")
(testClass as SomeProtocol).doSomething("But I will use the generic method")
Edit: To clarify, the code works. I want to know why both calls do not use the string specific method.
Double Edit: Removed the intermediary dispatch class for a simpler example
Is this a bug, current limitation, or intended functionality? If this is intended, can someone please explain why?
Swift 2.0, Xcode 7.0
Answer
You can't overload a protocol function and expect the correct definition to be called. This is because the definition to call is picked at compile time. Since the compiler doesn't know the concrete type, it chooses the only definition known at compile time, doSomething<T>.

I tested your code here http://swiftstub.com/ and it worked fine.
First it prints "I will use the specific method" and then "Generic Method":
I will use the specific methodGeneric Method

Related

Why is the #objc tag needed to use a selector?

I am trying to use a selector to see if a certain protocol can perform an action. When I try it like this:
protocol Test {
func hello()
func goodBye(a: String)
}
class Tester: NSObject, Test {
override init() {}
func hello() { }
func goodBye(a: String) { }
}
let a: Test = Tester()
let result = a.responds(to: Selector("goodByeWithA:"))
In this case, result evaluates to false.
But if I add the #objc tag to the protocol, it evaluates as true.
#objc protocol Test {
func hello()
func goodBye(a: String)
}
Why is this?
On a side note, I know that it is now recommended to use the #selector syntax and to move away from using strings, but for various reasons, I have to use a string in this case.
EDIT: This only started happening once I migrated my project to Swift 4.2
By default Swift generates code that is only available to other Swift code, but if you need to interact with the Objective-C runtime – all of UIKit, for example – you need to tell Swift what to do.
That’s where the #objc attribute comes in: when you apply it to a class or method it instructs Swift to make those things available to Objective-C as well as Swift code. So, any time you want to call a method from a UIBarButtonItem or a Timer, you’ll need to mark that method using #objc so it’s exposed – both of those, and many others, are Objective-C code.
Don’t worry: if you forget to add #objc when it’s needed, your code simply won’t compile – it’s not something you can forget by accident and introduce a bug.

Inconsistency in Swift optional protocol behaviour

Coming from .Net, I am trying to learn Swift3/iOS and got puzzled by the following apparent inconsistent behaviour of optional protocol members. I suspect its something got to do with the juggling between objc/swift words, but what am I missing here actually?
// In playground, given below:
#objc protocol SomePtotocol {
#objc optional func someMethod()
}
class SomeDelegate: NSObject, SomePtotocol {
}
class SomeController: NSObject {
var delegate: SomePtotocol = SomeDelegate()
}
// This works and compiles without error
let controller = SomeController()
controller.delegate.someMethod?() // No error, typed as '(() -> ())?'
// But this fails to even compile ??
let delegate = SomeDelegate()
delegate.someMethod?() // Error: 'SomeDelegate' has no member 'someMethod'
I would expect either both to fail or both pass, so if someone could please enlighten me on this anomaly.
The difference between the two blocks of code is the type of the variable involved.
In the first block, delegate is explicitly typed as SomePtotocol, and this protocol defines the someMethod method, so your statement is valid.
In the second block, delegate is implicitly typed as SomeDelegate and although this class conforms to SomePtotocol, it doesn't implement the optional method someMethod, so you get an error.
If you change your second block to
let delegate: SomePtotocol = SomeDelegate()
delegate.someMethod?()
which is equivalent to the first block, then there is no error.

How to discriminate same method selector on Swift 3.0? [duplicate]

[NOTE This question was originally formulated under Swift 2.2. It has been revised for Swift 4, involving two important language changes: the first method parameter external is no longer automatically suppressed, and a selector must be explicitly exposed to Objective-C.]
Let's say I have these two methods in my class:
#objc func test() {}
#objc func test(_ sender:AnyObject?) {}
Now I want to use Swift 2.2's new #selector syntax to make a selector corresponding to the first of these methods, func test(). How do I do it? When I try this:
let selector = #selector(test) // error
... I get an error, "Ambiguous use of test()." But if I say this:
let selector = #selector(test(_:)) // ok, but...
... the error goes away, but I'm now referring to the wrong method, the one with a parameter. I want to refer to the one without any parameter. How do I do it?
[Note: the example is not artificial. NSObject has both Objective-C copy and copy: instance methods, Swift copy() and copy(sender:AnyObject?); so the problem can easily arise in real life.]
[NOTE This answer was originally formulated under Swift 2.2. It has been revised for Swift 4, involving two important language changes: the first method parameter external is no longer automatically suppressed, and a selector must be explicitly exposed to Objective-C.]
You can work around this problem by casting your function reference to the correct method signature:
let selector = #selector(test as () -> Void)
(However, in my opinion, you should not have to do this. I regard this situation as a bug, revealing that Swift's syntax for referring to functions is inadequate. I filed a bug report, but to no avail.)
Just to summarize the new #selector syntax:
The purpose of this syntax is to prevent the all-too-common runtime crashes (typically "unrecognized selector") that can arise when supplying a selector as a literal string. #selector() takes a function reference, and the compiler will check that the function really exists and will resolve the reference to an Objective-C selector for you. Thus, you can't readily make any mistake.
(EDIT: Okay, yes you can. You can be a complete lunkhead and set the target to an instance that doesn't implement the action message specified by the #selector. The compiler won't stop you and you'll crash just like in the good old days. Sigh...)
A function reference can appear in any of three forms:
The bare name of the function. This is sufficient if the function is unambiguous. Thus, for example:
#objc func test(_ sender:AnyObject?) {}
func makeSelector() {
let selector = #selector(test)
}
There is only one test method, so this #selector refers to it even though it takes a parameter and the #selector doesn't mention the parameter. The resolved Objective-C selector, behind the scenes, will still correctly be "test:" (with the colon, indicating a parameter).
The name of the function along with the rest of its signature. For example:
func test() {}
func test(_ sender:AnyObject?) {}
func makeSelector() {
let selector = #selector(test(_:))
}
We have two test methods, so we need to differentiate; the notation test(_:) resolves to the second one, the one with a parameter.
The name of the function with or without the rest of its signature, plus a cast to show the types of the parameters. Thus:
#objc func test(_ integer:Int) {}
#nonobjc func test(_ string:String) {}
func makeSelector() {
let selector1 = #selector(test as (Int) -> Void)
// or:
let selector2 = #selector(test(_:) as (Int) -> Void)
}
Here, we have overloaded test(_:). The overloading cannot be exposed to Objective-C, because Objective-C doesn't permit overloading, so only one of them is exposed, and we can form a selector only for the one that is exposed, because selectors are an Objective-C feature. But we must still disambiguate as far as Swift is concerned, and the cast does that.
(It is this linguistic feature that is used — misused, in my opinion — as the basis of the answer above.)
Also, you might have to help Swift resolve the function reference by telling it what class the function is in:
If the class is the same as this one, or up the superclass chain from this one, no further resolution is usually needed (as shown in the examples above); optionally, you can say self, with dot-notation (e.g. #selector(self.test), and in some situations you might have to do so.
Otherwise, you use either a reference to an instance for which the method is implemented, with dot-notation, as in this real-life example (self.mp is an MPMusicPlayerController):
let pause = UIBarButtonItem(barButtonSystemItem: .pause,
target: self.mp, action: #selector(self.mp.pause))
...or you can use the name of the class, with dot-notation:
class ClassA : NSObject {
#objc func test() {}
}
class ClassB {
func makeSelector() {
let selector = #selector(ClassA.test)
}
}
(This seems a curious notation, because it looks like you're saying test is a class method rather than an instance method, but it will be correctly resolved to a selector nonetheless, which is all that matters.)
I want to add a missing disambiguation: accessing an instance method from outside the class.
class Foo {
#objc func test() {}
#objc func test(_ sender: AnyObject?) {}
}
From the class' perspective the full signature of the test() method is (Foo) -> () -> Void, which you will need to specify in order to get the Selector.
#selector(Foo.test as (Foo) -> () -> Void)
#selector(Foo.test(_:))
Alternatively you can refer to an instance's Selectors as shown in the original answer.
let foo = Foo()
#selector(foo.test as () -> Void)
#selector(foo.test(_:))
In my case (Xcode 11.3.1) the error was only when using lldb while debugging. When running it works properly.

Check if optional protocol method is implemented in Swift?

I have a swift protocol:
#objc protocol SomeDelegate {
optional func myFunction()
}
I one of my classes I did:
weak var delegate: SomeDelegate?
Now I want to check if the delegate has myFunction implemented.
In objective-c I can do:
if ([delegate respondsToSelector:#selector(myFunction)]) {
...
}
But this is not available in Swift.
Edit: This is different from: What is the swift equivalent of respondsToSelector? I focus on class protocols not on classes.
How do I check if my delegate has an optional method implemented?
Per The Swift Programming Language:
You check for an implementation of an optional requirement by writing
a question mark after the name of the requirement when it is called,
such as someOptionalMethod?(someArgument). Optional property
requirements, and optional method requirements that return a value,
will always return an optional value of the appropriate type when they
are accessed or called, to reflect the fact that the optional
requirement may not have been implemented.
So the intention is not that you check whether the method is implemented, it's that you attempt to call it regardless and get an optional back.
You can do
if delegate?.myFunction != nil {
}
I've found it successful to add an extension to the protocol that defines basic default implementation and then any class implementing the protocol need only override the functions of interest.
public protocol PresenterDelegate : class {
func presenterDidRefreshCompleteLayout(presenter: Presenter)
func presenterShouldDoSomething(presenter: Presenter) -> Bool
}
then extend
extension PresenterDelegate {
public func presenterDidRefreshCompleteLayout(presenter: Presenter) {}
public func presenterShouldDoSomething(presenter: Presenter) -> Bool {
return true
}
}
Now any class needing to conform to the PresenterDelegate protocol has all functions already implemented, so it's now optional to override it's functionality.
I normally implement it like this:
self.delegate?.myFunction?()
if the delegate methods returns a value:
var result = defaultValue
if let delegateResult = self.delegate?.myFunction?() else {
result = delegateResult
}
//do something with result
Declaration
#objc public protocol nameOfDelegate: class {
#objc optional func delegateMethod(_ varA: int, didSelect item: Item)
}
Implimetation
if let delegate = nameOfDelegate {
delegate.delegateMethod?(1, didDeselect: node)
}
I know this question is 5 years old, but I would like to share what I found. My solution works as of 2021, XCode 11+, Swift 5.
Say I wanted to figure out whether the function sign follows the GIDSignInDelegate protocol and also know what all the optional functions for GIDSignInDelegate are.
I have to look at the source code of the GIDSignIn module, and this is how.
Click on jump to definition on the main module that is imported. It will lead to a file like this:
Copy the entire line, import GoogleSignIn.GIDSignIn and paste it in the ViewController or whatever .swift file (doesn't really matter).
Within the swift file, right click on the GIDSignIn part of the import line GoogleSignIn.GIDSignIn and jump to definition. This will lead you to the actual module with all the available functions (the functions not marked optional may be stubs, which are required functions in the delegate protocol):
From this file, I can see that there is a sign function that is a stub of GIDSignInDelegate and an optional sign function that is implemented as a method overload.
I used this for GIDSignInDelegate, but you can use the same method to figure out whether any function follows any delegate protocol.

Swift and method prototype - forward declaration

Exploring Swift headers I'm seeing this pattern used by Apple, in particular the init declaration of the following struct HAS NO IMPLEMENTATION.
Obviously the init() implementation is hidden somehow, since it's Apple stuff, but I was trying to understand how.
This is only an example, but it seems a common behavior in the headers
struct AutoreleasingUnsafePointer<T> : Equatable, LogicValue {
let value: Builtin.RawPointer
init(_ value: Builtin.RawPointer) // <---- how is it possible? where is the implementation?
func getLogicValue() -> Bool
/// Access the underlying raw memory, getting and
/// setting values.
var memory: T
}
I know that it is possible to declare a protocol plus a class extension, doing this it's possible to "hide" the implementation from the class declaration and moving it elsewhere
class TestClass :TestClassProtocol
{
// nothing here
}
protocol TestClassProtocol
{
func someMethod() // here is the method declaration
}
extension TestClass
{
func someMethod() // here is the method implementation
{
println("extended method")
}
}
But it's different from what I have seen in the Apple Headers, since the method "declaration" is inside the "class", not inside the "protocol". if I try to put the method declaration inside the class TestClass, however, I have two errors (function without body on the class, and invalid redeclaration on the extension)
In Objective C this was "implicit", since the method declaration was in the .h and the implementation in the .m
How to do the same in Swift?
I think the explanation is very simple. What you see in Xcode is not actually a valid Swift
code.
It's a result from an automatic conversion of an Obj-C header into Swift-like code but it's not compilable Swift.

Resources