Mobile analytic clients by Plugins design

Steve Dao
NE Digital
Published in
5 min readDec 20, 2020

--

Analytics is the systematic computational analysis of data or statistics - Wikipedia

Analytics is one of “must-have” core components not only for mobile applications but other platforms. Nowadays, there’re a lot of analytic clients such as: Google Analytics, AppFlyer, MixPanel, … Each of them has pros & cons, therefore we may need to use more than 1 client in our application. This article is going to propose a Plugins design to construct a flexible, maintainable & scaleable architecture for analytic clients, the code will be written in Swift but only on interface approach, can be ported & referred to other programming languages or platforms.

Abstraction

We don’t know how many analytic clients would be used in our application, instead of implementing a concrete instance for each, an abstraction is a better approach. Our application doesn’t need to know about the client, it just need to send the analytics, that’s all! In super-app or multiple modules project, abstraction is required for dependency injection as well to reuse the clients instead of instantiate redundancies.

Abstract analytic clients

The challenge of this approach is its name: “Abstraction”. Each analytic client provides a lot of feature, some clients provide specific features that others do not. We have to create an interface to cover all features we may don’t even know in the future when we add a new one, this would be a big challenge. Depends on your project & situation, try to define a best interface to fit your need. This is an example of mine with a simple method to send an analytic event:

We can also abstract the analytic event, it would be easier to let application create a new event instead of passing all parameters to our method above:

The options property gives application a chance to deal with concrete clients. It would be an agreement between application & concrete client to follow, for example:

Now, our abstraction is ready to be adopted by real clients, this is an example:

This is an example of event will be sent from Application to clients:

In this example, the event will only be sent to clients except MixPanel. It means if we inject the Mixpanel client to AppContainer then this event will be ignored! Depends on your needs, we can add more logics to the event’s options to achieve more functionalities

For dependency injection, we can create many concrete client but only one would be used to register & resolve. Since our app has been deployed to AppStore, we cannot change our code to update our clients. One use case in this scenario also, if we want to send an event to multiple clients for experiment, how can we disable one of them remotely? These will be resolved on next section.

Plugins design

To send an event to multiple clients, just make a loop through items?

Yes, this is an easiest solution we can do but not really a good idea for maintainable & scalable. We have to find a way to group all clients into a place and implement some logics such as remote config there. Uber team has published a good article about the plugins design. It is a recommendation for you to read that first, this article is inspired by its theory.

All components are doing the same logic will be consider as plugins:

Each plugin will be identified as a unique String, it would be easier for us to configure remotely with an array of String. The isApplicable property gives plugin a chance to decide after remote config. For example if a plugin is available on Remote config but not ready by itself in Application then it should not be use.

All plugins will be attached into a PluginPoint:

Application will decide how many available plugins can be used then the createApplicablePlugins will ask plugins by itself about their availability through their isApplicable property and return the ready-to-use plugins. This method get an array of String that can be fetched from Remote Config also.

Now our analytic clients need to adopt PluginType, we can create a new interface that combine current AnalyticType & PluginType. This is an example of a client:

In this example, Google analytic client will only be used for user login events even it is enable it in Remote config, it is a 2-way of decision & let application to be more flexible. Now it’s time to group all analytic clients into a PluginPoint:

Now, each time we’re going to send an event, let’s get the latest ready clients then dispatch the event. We can create a new “placeholder” AnalyticClientType:

This “placeholder” will act as a normal concrete client then can be injectable via AppContainer. We can also hide all implementations of Plugins design here, Application & its modules don’t need to know that. In this example, we can see the RemoteConfigClient, we also tried to abstract the remote config clients, the remote client can be Firebase Remote Config, we’re using Split in current project.

Conclusion

In my current project, I have refactored our current analytic clients into plugins design with a feature flag (from Split also). It means if our Plugins design get any issues then we can back to legacy codes safely. Feature flag is needed for any refactoring, please keep this in mind!

Now all analytic clients in FairPrice iOS app are running under Plugins design. We can manage all analytic clients such as: adding a new one or disable them via Remote config. All our feature modules will resolve the shared “placeholder” client to use, no need to instantiate by themself anymore.

FairPrice iOS App

Plugins design is a good idea for super app or multi modules architecture. Again, it is a recommendation for you to read an article from Uber team to understand more about this design, they mostly talked about the theory, this article let you understand more by an actual use-case & code implementations.

Thanks for reading & happy plugging-in 🤗

--

--