In the last post we changed the communication between our classes to be based on the Key-Value Observation. As we could see KVO is not the best solution for our problem, but sometimes can be helpful, if we need to observe one particular object property changes.
In this post I will modify the communication to have the same characteristic as the delegation software design pattern. Let see what changes are required in the class diagram in order to achieve our target:
In the UML diagram above you see two «protocol», which is pretty much same as the «interface» in other Object Oriented Programming languages (if they support this particular principle). The protocol or the interface sometimes called as a contract, and all of the classes implement the protocol or interface will have as a “contractual obligation” to implement all of the required properties and methods. I would more think about the protocols as a template, where all of the required parts needs to be filled out. If we are referencing to a protocol in Objective-C, the form is used, where ProtocolWantToConform is the name of the protocol.
About the Delegation pattern
The delegation pattern originally was created for allowing something similar to multiple inheritance, which is not available in most of the programming languages. In our Apple software ecosystem the pattern is more about acting in behalf of another object. Usually delegates are for accomplish a well designed (and limited) functionality, helping the developers to have a nicely decoupled software. The best examples for the delegation patterns are the UITableViewDelegate and UITableViewDataSource protocols. With a careful software design you can use separate classes and code to provide the behavioural part (from the delegate) and the data source for your table view. The benefit to do so is that you can later easily change any of them, without making significant changes in the table view controller itself.
About the protocol files
Ok, I have already tried to explain what is a protocol, let see how can you create a protocol file. Don’t forget the protocol should stand only as a blueprint for the classes want to conform or implement to the protocol. Having said that we don’t need to implement any details in the methods, only the method signatures and optionally we can also specify if the method is optional.
Creating a protocol file is a bit tricky in Xcode, but you can used to it quite easily: Go to File -> New-> File, and on the screen select the Objective-C file (marked with a small m), as you can see on the screenshot below.
Click on Next and select “Protocol” as File Type, and give it a reasonable and meaningful name.
Eventually you will end up only one file, with the .h extension, and a quite simple @protocol NameOfProtocol @end structure in the file. Note, that is the parent or super protocol!
Protocol files for our solution
As we can see on the UML diagram, there are 2 protocol files used: PMODataHolder and PMODownloaderFromURL and in our specific case we need to establish a to way communication, or at least cross reference between those entities via the delegate and the receiver properties. Keep in mind, that we need the receiver property to hold the reference of the image holder object in the delegate protocol in order to make a callback on it with the downloaded data.
I think it is a well commented file. There are a few thing though which I would like to emphasise.
Please note the forward declaration of the cross referenced protocol for the delegate class, which was required by Xcode, to build the solution. Without this I had a nasty “No type or protocol named” error.
The second thing I want to emphasise is about the delegate property declaration. As you can see the delegate will be strong attribute. The other thing you should note, that the delegate type is id, and needs to implement the . This kind of declaration can make your code loosely coupled, since this declaration won’t tell you what kind of object we want to reference here, only we require that this class need to implement certain type of protocol. I think this is one of the important techniques which helps you to apply the principle from the GoF design patterns book, “Program to interfaces, not implementation”.
The only change here about the cross declaration is the weak attribute of the receiver property. This is because I would like to avoid from retain circles. Using the weak pointer means if the receiver object for some reason has been already deallocated (for example the download process takes long time, and the user has been already moved to another screen), the receiver object will be successfully freed from the memory by the ARC.
Changes in our files
As it was described in our new UML diagram, the PMOPictureController need to implement the PMODataHolder protocol. At
//1 the protocol header file imported, and at
//2 it is declared, that this class needs to conform the said protocol.
Take a look at the @synthesize at
//1. If you remember I created a property in the protocol, but I also told, that the protocol file is just a blueprint, the developer’s work is to build a class from this blueprint. Unfortunately the properties, declared in the protocol won’t be automatically synthesised for us, that’s why we need to implement them: by synthesising them. In very simple terms this line creates a private variable in the class, called _delegate, which will be bound to the self.delegate property. It also creates the default accessors for the self.delegate. Make note about the initializer changes: it will set up the PMODownloader as a delegate immediately after the default initialization.
//2 we just simply pass the task to our delegate, this is the part where actually the pattern implemented, ie. asking another class to do something in behalf our controller.
//3PMODataHolder protocol, including this method. I kept the other part of the class unchanged, so we still notified in any download problems, and the controller also notifies the objects, which are observing the
image Key-Value change.
So, let’s take a look on our downloader class, how is the protocol will be implemented, and what has been changed:
//1 I import the protocol file, so in the line at
//2 I can use it, with the class declaration, that this particular class will implement the protocol. As you can see the forward declaration for
- (void)downloadDataFromURL:(nonnull NSURL *)url; has been removed, since now it is declared in the protocol.
//1 I use the same technique to synthesise the
_receiver property, as it was used in the
Maybe you have already noticed that the code is getting more clear, and it is definitely seen in the method at
//2, where all of the other calls just simply replaced by a callback to the receiver, and passing the downloaded raw data.
I also removed the other, now useless code, but I kept the one for notifying about the download failure.
Updated test cases
As I changed the strategy, how to carry out the download, actually hiding the downloader class behind the delegation pattern, our tests need to be updated as well.
As you can see, now our download test is based on a KVO on the PMOPictureController image property.
I removed the successful download test, since this part of the code should work via the delegation pattern.
I hope you could earn some knowledge from this post. Delegation is quite powerful, in terms of segregation of concerns, which leads us to clean(er) code. It needs some preliminary planning and design, by using “Program for interfaces, not implementation.” principle.
For that particular case, i.e. asynchronously downloading an image, delegation is not the best solution still. The last post of this series I will introduce the block based solution.