Segues, Delegates and Protocols, oh my!
In the last year, I have been dedicating a large amount of my time to developing mobile apps with React Native (RN). I love the flexibility of React Native, the speed of development and the fact that it is just Javascript. With tools like RN and Code Push from Microsoft, the iteration cycle is fast and you can be super productive. But there are certain things that RN can’t do and I knew there would come a point where I would need to get my hands dirty and write some native code. Now I am sure I could cobble something together from Stack Overflow and other tutorials. But I wanted more than that. I want to really understand what I was writing and why. So I decided to dedicate a month of my free time to learning the iOS ecosystem and the recently released Swift 4. I find learning new languages fun so really looked forward to the challenge.
I intend to write a number of blog posts about the things I have learned, particularly the areas that I needed to solidify in my mind.
A working example of the code for this tutorial can be found here.
This post is about passing data between multiple screens in your apps.
It is unlikely that your app is only gonna one screen and you are probably gonna want to pass data between these screens. In iOS terms, these screens are linked by segues. The definition of segue is: 'an uninterrupted transition from one piece of music or film scene to another.' This definition is quite fitting as it is the process of transitioning from one screen to another.
Passing data forward is easy. You can get a reference to the next screen from the newly activated segue. With this reference, you can add to properties of the class that has been instantiated when the segue was created.
In the code snippet below, we have a function that is triggered on button press. When creating a segue we must give it an identifier. In the example below, we are triggering a performSegue function call on a segue with the identifier goToSecondScreen. We are able to react to the performSegue function and do things before the segue is completed (like send over data). In the second function, we are overriding the preparefunction. This function returns the segue that we are accessing. We are testing the identifier is the one we want to act on (as there may be many segues on a single screen) and getting that reference to the next screen and adding data from this screen to the properties that are attached to the class of the second screen.
So here we are creating a constant called destinationVC and attaching a reference to the segue.destination. The as! SecondController informs the compiler what sort of View Controller it is. We are adding the data from the input field on the first screen to the property textPassedOver
Then once the segue has completed and you are on the next screen of your app we are updating the `secondLabel.text` with the data assigned to the `textPassedOver` property.
Now then, what if we want to pass data back from this second screen to the first. Your first thought would be to follow the same process as before, attaching the data to a property on `segue.destination` but the problem you have is that this creates a new instance of the **FirstController**. The data is sent back but not to the original screen. This is obviously problematic for a multitude of reasons. This is where Delegates and Protocols come in. It is easier to understand how Delegates and Protocols work by detailing the steps to get the data passing back.
Wikipedias definition of the delegate pattern is:
Delegation is a way to make composition as powerful for reuse as inheritance [Lie86, JZ91]. In delegation, two objects are involved in handling a request: a receiving object delegates operations to its delegate. This is analogous to subclasses deferring requests to parent classes. But with inheritance, an inherited operation can always refer to the receiving object through the this member variable in C++ and self in Smalltalk. To achieve the same effect with delegation, the receiver passes itself to the delegate to let the delegated operation refer to the receiver. Grady Booch
Using the delegate pattern is the go to way when transferring data between screens in your swift apps and this is what the rest of the blog post is going to be detailing.
The delegate pattern
The first thing to do is create the protocol function. A protocol function is a contract stipulating what the delegate must adhere to. It is like creating a contract between the class and its delegate. This function does not care about the implementation of the function., just that is exists.
The protocol is created at the same level as the `class`. Usually above the class declaration.
Now that we have our delegate protocol and so our 'contract' we need to conform to that protocol in the controller we want to have as the delegate.
This is done by adding the protocol to the class declaration.
At this point Xcode is probably screaming at you. Because we have not fulfilled the contract and implemented the delegate method(s) defined in the protocol. We are calling the delegate method and using the data passed back to update the UI on the first screen.
Back in the SecondController we must create the delegate property. The property is optional (hence the `?`) as there might not be a declared delegate
Now we must instruct our code that FirstController wants to be the delegate (and be notified of new data). We do this by getting the destination of the segue and assigning ourselves as the delegate. When using a segue to go from firstViewControlller to secondViewController it creates a new instance of the secondViewController.
The final step is to trigger the delegate method and passing the data back to the declared delegate.
delegate?changeLabelBack(labelText: inputText) basically says that if the delegate is not nil then call the method. We are then dismissing the controller, essentially removing it from the stack and returning us back to the first screen again.
I hope this all makes sense. It is quite a confusing topic but one that is fundamental to producing apps with multiple screens. As mentioned above, a simple working example repo can be found here.