Getting Started with iOS App Modularization — Extracting Catalog
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.
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 ofBundle(identifier:)
since theCatalogPageViewController.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