Getting Started with iOS App Modularization — Extracting Catalog

Arya Surya
Tokopedia Engineering
5 min readSep 16, 2022

This is the second part of the modularization tutorial. Refer to the list below for the complete parts :
Part 1: Introduction
Part 2: Extracting Catalog
Part 3: Final Extraction and Micro App

In this article, you will continue to extract the catalog feature.

Refer to the link below for the starting project template, or you can continue with your existing project if you come from the first part of the article.

1st Step : Creating The Catalog Framework

We will start this article by creating a new framework named Catalog. Choose File > New Project, choose Framework and remember to add it to your xcworkspace . Refer to the GIF below for a better understanding.

The next step is to move all the Catalog’s folder contents in the main framework into its framework now, refer to another GIF below for clarity.

The first thing to do upon framework creation is to add the framework’s dependency. In this case, you have to add the Shared framework as a dependency since Catalog relies on it heavily for the models and other assets. Click on the Catalog project, find the targets tab and scroll until the Framework, Libraries, and Embededded Content section. Click the plus icon and choose the Shared framework.

The next step is to set asset files in the new Catalog framework to point to its module, click the CatalogPageViewController.xib . Locate the File Owner option on the Placeholders section, then choose the custom class tab, and type Catalog at the Module text field, and the Inherit module from target will be automatically unchecked.

Try to run the app but expect the build to be failed because of the ACL and import-related issues.
One place that errored first is SceneDelegate.swift located in the main framework. Simply importing Catalog will resolve the issue, but before that, you have to add the Catalog framework as the main framework’s dependency.

Try to rebuild the app again, but the error will persist. This is caused by ACL since the CatalogPageViewController ‘s access control is internal. Add a public access control before the class definition and make all its exposed functions public, the resulting code will be like this.

Even after all these ACL modifications, our catalog feature still will not build because the file cannot identify the ProductDetailPageViewController .
One might argue that the solution to this is just to
1. Extract the product detail into its framework
2. Simply import the product detail module at our catalog framework

This approach will work but it will cause a dependency issue if later, the product detail page needs to use classes/models from the Catalog framework, you will end up having a circular dependency in your app.

GIF by https://giphy.com/justin/ at Giphy

2nd Step : Designing The App Router

A better solution to the previous one mentioned before is you can create an abstraction that acts as a route handler in the Shared framework, then when any features that want to navigate to another module can just use the abstraction without having to be tightly coupled to the destination framework.

Navigate to the Helper folder inside the Shared framework and add a new file named Router.swift

This will be a blueprint for our navigation capability. Now, head back to the CatalogPageViewController and replace the didSelectItemAt indexPath function extension to become like this

Try to run the app. Now, it will be successfully built but crashed as soon as it runs. The crash is caused by the CatalogPageViewController since it cannot find its XIB.
Navigate to CatalogPageViewController , locate its init function and change them.

Bundle(for: Self.self) is used to refer the bundle within the module itself, you can use this directly instead of Bundle(identifier:) since the CatalogPageViewController.xib lives inside its own framework.

Try to rebuild the app again, and this time the app will successfully run.

But one capability is missing from our app, if you try to click one of the product cards, it will do nothing instead of navigating to the respective product detail page, why is this happening?

Remember atCatalogPageViewController , you have changed the didSelectItemAt indexPath function definition to use Router instead of manually navigating. The navigation will not work yet because you have not created the definition for the route function, which explains the bug.

Navigate SceneDelegate.swift inside the main framework. Add the route definition at the very end of the scene function :

By doing this, the app will be able to perform navigation from any modules without ending up having a circular dependency or becoming tightly coupled with unnecessary modules.
Try to run the app, and the navigation will now work smoothly.

Up until this point, you have successfully extracted the Shared and Catalog frameworks, leaving only the Product Detail Page to be extracted.
In the next part, you will continue to extract the Product Detail Page into its framework and learn how to build one single framework instead of the entire app.

Now, let’s continue to the final part here

--

--

Arya Surya
Tokopedia Engineering

Mostly writes about frontend, including but not limited to iOS and web development. Go follow @agustinustheoo for other stuff