About two years ago, someone asked me a good question: “Why do we need delegates for
UIViewControllers ?” He thought that Swift made things easier, but the whole thing with delegates seems very complicated. Why is it just not possible to send messages or initialization between classes?
When I first studied iOS, I recognized that it took me months to understand what happened to the delegation. I found a lot of incomprehensible code and some explanations. When I worked on this, the result was not enough. In most cases, tutorials referred to information on how to use the standard Apple delegate, but did not show how to create your own response. These responses are necessary for a full understanding of the delegates.
I decided it was time to update the article and include two examples that developers might encounter: iOS and watchOS versions. Together with the maturation of watchOS in watchOS 3, I think many developers will begin to look towards developing applications for watches and there may be confronted with incomprehensible things.
')
What is a class?
Let's start from the beginning, so that everyone understands the problem. As long as we use classes in object-oriented programming, it’s worth a good understanding of what they are.
A class is a collection of data that we call
properties (properties) and actions
methods for
properties .
Properties and
methods can be
public (public) or
private (private).
Public method see and use classes other than the defining one.
Private means that properties or methods are visible and used inside the defining class, while other classes are not able to see or use them. In Swift,
private makes
properties and
methods private. Calculating
properties is another way to make properties private. In addition, there is a standard state in Swift that makes a
method or
class public only for the current target.
Many developers do not like playing with their code, and this is also a good programming experience. In general, it is worth striving to leave public the necessary minimum for other classes. Preserving
properties and
methods private and hiding classes is called
encapsulation .
Encapsulation allows you to build code, as if we were building a block house. As with ordinary bricks there are several uses, and the class has several
methods used. After that, they can attach many other bricks.
What is Model - View - Controller or MVC?
A frequent MVC term among developers, this is not an exclusive term for Xcode projects. This is a programming sequence, a good organization of any program or application in a graphic environment and in the whole environment that interacts with the user. MVC shares the main parts of the application. First, it separates data and user interaction and adds an intermediary between them. Why is this so important? You can write and publish an application for the iPhone, then decide that the iPad version is a good idea, and then decide to add a version for the clock. With MVC, you change only one part completely - View and maybe a little
Controller . Code containing data never changes between versions. It will save a lot of time and effort.
What is a Model?
There are parts of the program that work with the data that we want to process. The pizza ordering system has data on any order, which may contain links to more detailed data about each customer and pizza. This is our
Model in the pizza ordering system: a collection of all the data used in the ordering system. It should not contact with the user, does not ask for anything and does not display. This is just data. Here is an example of a simple model:
class switchState{ var state:Bool func textState()->String{ if state { return "On" } else { return "Off" } func overLoadSwitch(users:Int,load:Int){ let totalLoad = users * load if totalLoad > 100 { switch = false } }
This model is a state switch. This is data. The model has two
methods : textState () - describes the state of the switch, as
String , overLoadSwitch () turns off the switch if the number of users * load is more than 100. There are many more methods that I have to add to describe the switch, but any methods change or describe only data. There is no user input from here.
Model can perform calculations, but again, there is no user interaction.
What is a View?


In
View , all user interactions occur. Most people in Xcode use
Interface Builder as a canvas to use
storyboard or.
xib to build a
View . A developer can programmatically create a
View class that contains various controls. As the
Model never interacts with the user,
View never interacts directly with the data.
View does nothing, it just is. He can respond to the user's touch, for example, by transmitting a signal to a method, changing the color, if the button is pressed or scrolls the list, but that's all he does.
View contains many properties and methods and tells us about the state of
View . We can change the behavior of the interface or its appearance through methods or properties.
View can send the
Controller that there have been changes to the
View , for example, a button is pressed or a character is entered. But he can do nothing with it, except to transmit information.
What is a controller?
The heart of MVC combines
View and
Model . Called
Controller or
ViewController , it coordinates what happens in the data or
View . If the user clicks the button, the
Controller responds to this event. If this response means sending a
Model message, the
ViewController does it. If the response requires the output of data from the
Model , the
Controller does this too. In Xcode, @IBOutlet and @IBAction combine files containing views in Interface Builder and
ViewController .
The key to MVC is communication. To be precise, its disadvantage. MVC takes encapsulation very seriously.
View and
Model never communicate directly with each other.
Controller can send messages
View and
Controller .
View and
Controller can perform internal actions in response to messages, like calling a method, or they can return the value of
Controller 's.
The controller never makes changes directly to the
Model or the
View .
So,
View ,
Model and
Controller do not change the properties of each other.
View and
Model can not communicate with each other at all.
View can tell the
Controller that there is a change in the
Model .
The controller can send messages in the form of a method call to
View ,
Model, and receive responses through this method.
How MVC binds to other MVC
Everything that we have just discussed concerns only one scene in a rather large application. Suppose I have a watchOS storyboard like this:
There is a Switch button that loads another
View with a switch. When I decide how the switch should be located, I'll click
Done .
Easy way
In Xcode, we have segues. Segways are convenient to point from one
ViewController to another. When segway makes things complicated for us easier and we move from one
Controller to another, segway tells the system to open a specific
ViewController , and then opens
View and
Model .
Model and
View in the new MVC settings are different from the caller. Apple has included the prepare (for segue :) method for iOS, which gives us a chance to set the values in the new
ViewController and, accordingly, in the new
ViewController ’s
View and
Model .
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "switch"{ let vc = segue.destination as! SwitchViewController vc.switchState = false }
With the advent of WatchOS, a slightly different approach emerged. Instead of accessing the full
Model , watchOS sends new controllers a single value, called
context . So how is it like
Any? , you can pass it any values. Most often, developers pass
dictionaries of values to other controllers via the contextForSegue method.
override func contextForSegue(withIdentifier segueIdentifier: String) -> Any? { return self }
For identical applications for the iPhone and the clock, I can press the Switch button. It starts the switch interface and passes the value to the switch
false .
Problem path
We can turn the switch on and off quite easily, but when we press Done to send it back to the original controller, the problem then appears. By MVC rules, we need a method that returns a value. Where in the called
instance can we return to its class that caused it?
With the encapsulated class we can not. In this situation, it is not possible to send the revised
Model back to the original controller without break encapsulation or MVC. The new
ViewController knows nothing about the class that caused it. Looks like we're stuck. If we try to create a reference directly to the calling controller, we will call the
reference loop , which will kill the memory. Simply put, we cannot send data back.
This problem delegates and protocols solve being a little agile. Imagine another class that is actually a skeleton of a class. This class contains only methods. It declares certain methods in this class, but never uses them. In Swift, they are called protocols. We create a protocol containing one method. This method is what you do when you end up with a switch and want to return to the calling controller. It has several parameters, data for transmission back to the calling controller. It might look like this:
protocol SwitchDelegate { func didFinishSwitch(switchState:Bool) }
I passed back the switch value in this case.
In the controller with the switch, we create an
instance of this protocol and call it a delegate.
delegate : SwitchDelegate! = nil
Since we have a property of type SwitchDelegate, we can use methods of type SwitchDelegate, for example, the didFinishSwitch method. We can bind the execution of this method to the Done button:
@IBAction func doneButtonPressed(sender:UIButton!){ delegate.didFinishSwitch(switchState: switchState) dismiss(animated: true, completion: nil) }
or for WatchOS:
@IBAction func submitSwitchStatus() { delegate.didFinishSwitch(switchState: switchState) pop() }
Once you do this, you will get a compilation error, since the protocol method does not exist in the class. In the code, to adapt the class on the example of OrderPizzaViewController, we use the method:
func didFinishSwitch(switchState: Bool) { if switchState { textState = "Switch is On" } else { textState = "Switch is Off" } }
We return the data back and what we need with them. In this case, the text displayed in the
Label will be assigned.
One more step. During the return to the designated controller, I said that the delegate is a
protocol instanc , but I did not say where the delegate was. In prepare (for Segue :), I added an additional line vc.delegate = self, telling the protocol of your controller:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "switch"{ let vc = segue.destination as! SwitchViewController vc.switchState = false vc.delegate = self }
In WatchOS, things are a little different. I have only one passed
context , switchState and delegate. For several values, developers usually use
dictionary like this:
override func contextForSegue(withIdentifier segueIdentifier: String) -> Any? { let context:[String:Any] = ["switchState":false,"delegate":self] return context }
The awake method has code to retrieve
dictionary and assign values. When we click Done on the clock or in the application, the method starts, it knows what is in the calling controller and will be called where we added it to the original class.
data is a parameter that the program could easily transfer to the controller and to the
Model . Delegates and protocols are pretty tricky, but it works and is one of the most important techniques when working with
ViewControllers .
The main question was why Swift did not make it all easier. Since I showed everything here in the context of Swift, it does not matter which object-oriented language you use. Delegation is part of the MVC sequence, which is an important programming skill.
Translation of
this article
here .