Show more

inverted bicycle
Interestingly, if you drove this in Australia it would be the right way around.

As part of the TextNow iOS team, I am responsible for ensuring the core parts of our products are well-architected, and align with design patterns. That’s where the Inversion of Control principle comes in.

TextNow is a free app, available on many platforms, that offers free calls within the US and Canada, along with very inexpensive international calling rates. To recoup the cost of free calls, we monetize TextNow by showing ads to our users.

Inversion of Control (referred to as IoC hereafter) can be defined as:

Inverting the control of the program where, instead of higher level components controlling lower level components to do certain tasks, a framework takes over the control and performs the tasks.

A good example is how UIKit manages the UIViews and renders them on the screen. All we do in our program is indicate what properties the view should have, and then rest is taken care by the framework. UIKit as a framework can change internally how it manages or renders UIViews and our program wouldn’t change a bit. That decoupling is an essential trait for IoC, as it allows each component to be extended independently of the other.

IoC is not to be confused with the dependency inversion principle, where the higher-level and lower-level components are decoupled using abstraction layers. With dependency inversion, control is still retained by the higher level components.

Moving reusable code into a common library does not change the controlling aspect of the components. Any change of control in the reusable low-level component would necessitate changing all high-level components as well.

If the library exhibits its own controlling nature, then it ceases to be a simple library and becomes a framework, which is exactly what IoC encourages.

IoC Example with Code

The below code shows tight control of tasks B, C and D by task A and Z:

performTaskA () {
    performTaskB();
    performTaskC();
    performTaskD();
    taskADone();
}

performTaskZ () {
    performTaskB()
    performTaskD()
    taskZDone();
}

The below code shows control being delegated to another component to perform tasks:

performTaskA() {
    taskPerformer.needTasksPerformed ([B, C, D], &taskADone());
}
performTaskZ() {
    taskPerformer.needTasksPerformed ([B, D], &taskZDone());
}
Class TaskPerformerFramework {
    needTasksPerformed (list, blockToCall) {
        for each in list {
            each.performTask();
        }
        blockToCall ();
    }
}

In the above simple example, IoC has elevated the lower-level component to the same level as the higher-level component. So there is no more “lower” or “higher”; instead, it’s just components interacting with each other. The control flows seamlessly between the components.

Controlling vs. Autonomy
Controlling vs. Autonomy

Current Ad Architecture at TextNow

The View Controller
The View Controller
ViewController controls all aspect of ads (creation, refreshing, view stack, tracking, etc.)

The current implementation of ad logic is deeply tied with the ViewController logic.

Here are the responsibilities of the ViewController in the old architecture :

  • It controls the position of the ad view
  • It controls the placeholder ad views to swap ads in from different vendors
  • It controls the refreshing logic of the ads
  • It controls the callbacks from ad SDKs for success and failure conditions of loading ads
  • It controls the ad view stack to place ad removal buttons so the user can opt out of seeing ads
  • It controls tracking of impressions, clicks, etc.

Since we show ads in many different places in the app, the above mechanism is repeated in multiple places. On top of that, we have different monetization strategies and ad providers depending on the type of ad being shown. For example: the ad that shows up in the conversation list, a list ad, will have different refresh rates, ad providers, rotation timing, etc, compared to a banner ad being shown at the bottom of the screen. That means more custom logic in each ViewController to control the ads.

New Ad Architecture at TextNow

The new ad architecture
The new ad architecture
In the new architecture the ViewController just decides where the ad should be placed, and only deals with the positioning of that view. The rest of the work is delegated to the Ad Framework.

With a framework controlling all of the advertising business logic, there is a clear separation of duties; the ViewController is responsible for creating the view and positioning it, and the Ad Framework is responsible for managing the ads on that view.

In this new architecture, the ViewController is responsible for one thing: controlling the position of the ad view. Now, here are the responsibilities of the Ad Framework in the same new architecture:

  • Controlling the placeholder ad views to swap Ads from different vendors
  • Controlling the refreshing logic of the ads
  • Controlling callbacks from ad SDKs for success and failure of loading ads
  • Controlling the ad view stack to place Ad removal buttons so the user can opt out of seeing ads
  • Controlling tracking of impressions, clicks etc. for each ad shown

Testability

Since the Ad Framework runs independently from the ViewController logic, we can send it a UIView and watch the Ad Framework populate the ads. Some of the things we can test are:

  • Ad types being filled in
  • Timing the interval of ad refreshing
  • Impression tracking
  • View stack, view positions etc.

We also use dependency injection within the Ad Framework to make it more testable than its old legacy counterpart, which was tightly coupled to the ViewController.

We also use Swift Protocols heavily to remove the concept of a vendor when it comes to serving Ads. Rather, ad vendors are baked into the concrete implementations which conform to a common protocol that the Ad Framework uses. This allows doing mock-ups with any conforming concrete class an easy task, thus improving testability.

Decoupling

Since the Ad Framework is now its own entity, we can change the ad experience independently of the ViewController logic. Real-time switches can be made to alter the entire ad experience and run A/B experiments to find the most profitable sequence of ads.

What We Learned

By using IoC, we elevated the ad component to the same level as the ViewController. And, by doing so, we opened up some new doors for us here at TextNow, and helped improve our product.

For a more in-depth look, I recommend checking out Martin Fowler’s blog on IoC.


At TextNow we are solving interesting problems like this everyday. If you like a good challenge, check out some of our Engineering job openings.

Similar posts

No Comments

Leave a Reply

Your email address will not be published. Required fields are marked with *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.