Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What would be better strategy for IBActions in protocols in Swift?

I am creating a Swift project and I want to define a specific protocol that enforces other components to implement a animate method:

protocol AnimatableBehavior {
    @IBAction func animate()
}

The problem is I want this method to be an IBAction, but I get this error from XCode:

Only instance methods can be declared 'IBAction'

My question is, how would you implement such a thing?

I have considered:

  1. Remove @IBAction, but then I need to remember adding it in every class that implements. Not very elegant and error prone.
  2. Create a base class instead of protocol, but then I am enforcing all components to subclass my base class instead of their own choice ones, so it is not a valid option.

Any other ideas?


EDIT: Response to comments below.

The idea of the IBAction on the protocol is because in the project there will be many different devs implementing small UI components, all of which have the animate method. The components can be added programatically or by Interface Builder and it is very convenient that they are always IBAction because I plan to compose them from IB files to simplify the View Controllers to the maximum extent (and this is clearly a View only task).

Therefore, the solution proposed below of adding a method in the controller that just calls the animate of the component is not good because it is redundant code and makes your Controller more dependent on your View.

The idea of letting the dev to remember adding the IBAction keyword on the method is workable, but as I said it is error prone (and by that I mean that there will be some forgetting about it), and I want to make sure that this is always accessible from IB. It also adds extra cognitive load, because I will need to document this lack of IBAction on the protocol and request the implementor to add it manually.

I know is not the common way of working in iOS and UIKit, but that was why I posted the question, maybe someone has an alternative idea.

like image 669
Angel G. Olloqui Avatar asked Apr 11 '15 09:04

Angel G. Olloqui


People also ask

Can we make protocol only class specific struct can not adopt it?

The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. But there would be a time when you want to restrict protocols to be adopted by a specific class. In Swift 5, you can do just that.

What is difference between protocol and delegate in Swift?

Protocol is a set of methods (either optional or required) that would be implemented by the class which conforms to that protocol. While, delegate is the reference to that class which conforms to that protocol and will adhere to implement methods defined in protocol.

How would you explain protocols to a new Swift developer?

Protocols provide a blueprint for Methods, properties and other requirements functionality. It is just described as a methods or properties skeleton instead of implementation. Methods and properties implementation can further be done by defining classes, functions and enumerations.

Why protocols are needed in Swift?

Protocols are used to define a “blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality.” Swift checks for protocol conformity issues at compile-time, allowing developers to discover some fatal bugs in the code even before running the program.


1 Answers

It doesn't make any sense to have an @IBAction in a protocol. @IBAction is nothing more than a keyword for Interface Builder to have a hook when you're control+dragging from Interface Builder to your actual source code.

This is just a simple misunderstanding of what @IBAction actually is and does.

  1. A method does not have to be marked as @IBAction in order for it to be the target of a UI element's actions. You programmatically hook up any method to any action using the addTarget set of methods that UI elements have. The method does not have to be marked as an @IBAction to do this.

  2. Regardless of whether or not a protocol defines a method as @IBAction, the class conforming to the protocol can add it (and still be conforming to the protocol.

    protocol FooProtocol {
        func doSomething()
    }
    
    class ViewControllerA: UIViewController, FooProtocol {
        @IBAction func doSomething() {
            // do something
        }
    } 
    
    class ViewControllerB: UIViewController, FooProtocol {
        func doSomething() {
            // do something
        }
    }
    

    Both of these view controller subclasses conform to the protocol, and having @IBAction there is ONLY necessary if you intend to hook up an action from interface builder!


Ultimately, whatever you're trying to do, if you think an @IBAction is necessary in your protocol, I think you're taking the wrong approach to something. It's hard to say what the right approach would be without knowing more details about what you're actually doing, but it never makes sense for @IBAction to belong in a protocol.

To me, it seems like the methods your protocol enforces shouldn't at all be tied to @IBAction methods. Instead, whatever user interaction should trigger the animation, should in turn call the animate method. For example, if we weren't talking about the protocol, my recommendation would be this sort of set up:

class ViewController: UIViewController {
    @IBAction func buttonThatStartsAnimation {
        self.animate()
    }

    func animate {
        // code that does all the animation
    }
}

So, with the protocol, we should take the same seperation of duties between the method that's actually initiating the animation code (which in the case of protocols, this is obviously some other outside class), and the animate method should only ever handle doing the relevant animations.

Importantly, just as a general rule, you shouldn't be directly referring to your @IBAction methods or your @IBOutlet variables directly from outside the class which defines them.

like image 128
nhgrif Avatar answered Sep 28 '22 06:09

nhgrif