Delegation: An example in code

Suppose that we have decided to create a class to encapsulate all of the logic for a special rating picker control. We'd like to offer:
+ the ability to customise the picker by allowing the user of our control to specify a preferred rating symbol. (customisation) + a feedback loop to notify the user of our control when certain events have occurred (communication)

Delegation would be a great tool to provide both customisation and communication between classes, so let's get started.

Step 1. Create the protocol

protocol RatingPickerDelegate {  
    func preferredRatingSymbol(picker: RatingPicker) -> UIImage?
    func didSelectRating(picker: RatingPicker, rating: Int)
    func didCancel(picker: RatingPicker)
}

preferredRatingSymbol: lets the delegate set a custom rating symbol. didSelectRating:rating: and didCancel: allow the delegate to react to user events such as selecting a rating or clearing one.

It's always nice for the delegate to have access to the public methods of the instance calling its methods, so the RatingPicker(or UITableView or UIScrollView or whatever) is often passed along as an argument.

Step 2. Create the delegator

A delegator class typically defines a variable property with the word "delegate" somewhere in the name. I personally prefer just delegate unless the delegator class expects to have multiple delegates each serving a part of class functionality. The type of the variable property is the key to it all. The variable will be of type whatever-you-named-your-delegate-protocol. E.g. if you named your protocol MySpecialDelegate then the variable type of the delegate property would be MySpecialDelegate.

With our protocol defined, our delegator RatingPicker can now set itself up to use that protocol.

class RatingPicker {  
    var delegate: RatingPickerDelegate?

    func setup() {
        let preferredRatingSymbol = 
                delegate?.preferredRatingSymbol(self)
        // set up the picker with the preferred rating symbol 
        // if specified by the delegate
    }

    func selectRating(selectedRating: Int) {
        delegate?.didSelectRating(self, rating: selectedRating)
        // other logic related to selecting a rating
    }

    func cancel() {
        delegate?.didCancel(self)
        // other logic related to canceling
    }
}

The delegate property is strongly typed to be a RatingPickerDelegate. Since it is declared as an optional here, it's not absolutely essential for RatingPicker to have a delegate set to work properly. If it were essential, we'd have to write a custom init: method and assign the delegate during initialisation.

Step 3. Choose a delegate

Choosing the delegate class is the final decision to make. It's not uncommon for a View Controller to double up as a delegate but I like to keep things simple and separate and usually create a separate handler class. You might prefer a different style.

class RatingPickerHandler: RatingPickerDelegate {  
    func preferredRatingSymbol(picker: RatingPicker) -> UIImage?
    {
        return UIImage(contentsOfFile:"Star.png")
    }

    func didSelectRating(picker: RatingPicker, rating: Int) 
    {
        // do something in response to a rating being selected
    }

    func didCancel() 
    {
        // do something in response to the rating picker canceling
    }
}

Step 4. The delegate meets the delegator

So now we have:
+ a protocol which specifies behaviour that a delegate must support + a delegator class which holds reference to a RatingPickerDelegate as a property + a delegate class which has adopted the RatingPickerDelegate protocol and is ready to go

There's still one key thing missing. We haven't connected the delegator and the delegate yet. Programmatically speaking, the delegate property of RatingPicker hasn't been set to be an instance of the delegate class RatingPickerHandler. How do we do that? It is actually very simple.

    // create an instance of RatingPicker
    var newRatingPicker = RatingPicker()

    // create an instance of RatingPickerHandler
    var ratingPickerHandler = RatingPickerHandler()

    // assign the delegate to the delegator
    newRatingPicker.delegate = ratingPickerHandler

Now obviously in practice, we'll never ever write the delegate assignment code in this fashion but the general mechanics of it will remain the same. You'll often find yourself assigning delegate properties in the prepareForSegue: method of the view controller. An oversimplified example,

func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject)  
{
    let destination = segue.destinationViewController
    destination.delegate = ratingPickerHandler
}

Aaaannndd... tada... you know how to delegation now.

Animesh.

Read more posts by this author.

United Kingdom

Subscribe to Animesh.

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!