Adding custom behavior to our UIViews is extremely easy and a thing that iOS developers regularly do. Yet there are some cases where this is not such an effortless task. Lets imagine that we have 2 button classes – Bouncy and Shiny. If we want a button to have one of the functionalities we make it a subclass of that particular class. But what will happen if we want a button to be both shiny and bouncy? Or we have to decide at runtime what type the button should be? One of the best ways to handle those situations is through composition. There are a lot of ways to implement it and the approach I think is most suitable for UIViews is to use an Entity Component System. Before we get to the source lets describe the requirements. So ideally what are the things we would like to be done for us and how do we want to interact with this framework?
- Pass the view to a component factory and receive the behavior.
- No need to inherit from a base class.
- Components should be initialized at the right time. For example – before view is displayed or if we need to know the view’s size after -(void)layoutSubviews.
- No additional calls should be needed from the main execution to function correctly.
- The components should follow the view’s lifetime, and get unregistered and deallocated from the system when the view is no longer needed.
What’s the Setup?
I think the easiest way to understand(and explain) it is to go through the classes in reverse. In this order we can go from simple to complex and not vice-versa.
The component class is nothing but a model. It holds the needed data/properties and doesn’t contain any methods in itself.
Now the system’s responsibility is to initialize the component and provide the behavior. As we all know UIViews can be a little tricky and its now always the case where we can setup everything directly into the constructor method. This is why all systems inherit from the Hooking protocol and by defining methods from it they specify when in the rendering process the code should be executed.
This is a static class that checks what methods of the Hooking protocol are implemented by a system and injects(thanks to Aspects) these methods to be executed for the view at the intended time. Through Hook we can ensure we have everything needed to init our components gracefully or unregister them when the view is about to get removed from the hierarchy.
The entity system’s responsibility is to tie each component to its corresponding system. In more general implementations an entity is a number and it can get render component, etc. However for this particular case I chose the view itself to be the entity.
You can find the repo on github, here. Its a solution that I still haven’t used in production but nonetheless excited to put in future use. The project is small so other than the explanation above I doubt anyone would need more to understand what’s going on and roll out they’re own version if needed. If you have any suggestions, questions or anything else I’d gladly discuss them in the comment section.