iOS View Code: A Solution To Keep the Encapsulation of the Pipeline

Declaring the data inside some computed properties

Pedro Alvarez
Better Programming

--

Image from alphacoders

In the last series, I presented the first solution to organize your pipeline with three steps. At the same time, you will never be confused about which step should come first. Indeed it is a good coding practice. However, there is an issue regarding encapsulation. Since the methods for view coding stand internal or public if the protocol is from a different module or maybe the superclass, there is always a chance to call some of these methods from outside, which implies a lack of encapsulation. Thinking of SOLID, there should never be a way to trigger direct UI changes outside the scope.

View Code implemented as a protocol

Fortunately, there is an easy turnaround for this, and we still rely on a superclass to define some overridable behaviors.

Create a Base Class for Your Views

First, let's record how we used to create our pipeline. We had three methods: one for building the hierarchy, the second for constraints, and the third for additional configuration. We want to define or view data in separate places and trigger the pipeline once we initialize our view. Let's create a new base class where we shall centralize all of our tasks in a single initializer:

Now we have a setupView function to trigger all the setup in the initializer. But the problem is, buildHierarchy and setupConstraints steps are particular to its subclasses, how may the superclass know about them? We should save the data regarding hierarchy and constraints in computed variables that the subclasses may extend.

How are we supposed to save data regarding hierarchy in the superclass since its defined by a sequence of addSubview and addArrangedSubview steps? Well, we shall introduce a new data model describing which views belong to which.

Create a Data Model for Describing the Hierarchy

Create a new class for our hierarchy model:

This data type describes which views are attached to a specific one. It also provides a method for establishing the hierarchy based on the parent views and subviews. If it's a stack view, it should call addArrangedSubview for each subview. Otherwise, add with addSubview . This makeHierarchy method should be called for each hierarchy relation that is described in our computed property in the subclass of BaseView. With that said, let's declare the following:

Now our hierarchy will be established once our view is initialized.

Constraints

We know that our UIView classes are provided with a computed property for constraints, which is an array of NSLayoutConstraint. Let's activate all of them in our setupView method:

We need to provide an array of constraints for each subclass to be activated by its super init .

Additional Configuration

We have placed our pipeline for hierarchy and auto layout in the default initializer for each view. Now, we need a way of establishing the configuration. Since it's very abstract, I haven't figured out how to save the additional configuration in a computed property. We could create a private method for each view or maybe find another way to apply that.

But what is important is that our pipeline is secure and now encapsulated since the trigger for it is kept inside our base view init and we can only see the data regarding hierarchy and constraints in our computed properties.

Conclusion

In this article, we found a turnaround for the problem of exposing the view code methods in a custom view. Instead of declaring it within some internal methods that could be called by any other place in the same module, we are just declaring the data inside some computed properties, and we only trigger the pipeline from the base class initializer.

I hope you found this solution useful and enjoyed ;).

--

--

iOS | Android Developer - WWDC19 scholarship winner- Blockchain enthusiast