iOS App As a Microservice. Modularize Your App With Tuist

This is the second article in a series on modular app architecture. In this post, I will cover implementation details using Tuist

Alex Dremov
Level Up Coding

--

Photo by Vlado Paunovic / Unsplash

This is the second article in a series on modular app architecture. In this post, I will cover implementation details using Tuist. It is an excellent command line tool that helps you generate, maintain and interact with Xcode projects.

💥 I covered the core ideas of modular architecture in the previous post.
Check it out if you haven’t yet!

What’s next?

In the next and last post in this series, I will cover implementation tips with SwiftUI. Subscribe so you don’t miss it

UPD: live now!

Why Tuist?

It encourages you to further code modularization as it provides an elegant way to create separate Xcode projects for different modules, making tight coupling or implicit dependencies less viable

Also, it’s great for teamwork. Have you tried to commit an Xcode project to a VCS like GitHub?

It’s a mess

Diff of the modified Xcode project is not human-readable. It’s simply impossible to trace changes or review a PR. What if you could define the Xcode project in a simple config file? Tuist does that. Moreover, tuist config files are written in Swift.

Our goal

We want to divide our project into separate Xcode projects according to the architecture I proposed in the previous article.

To reiterate, our app will consist of a combination of modules and for every module or feature, we will create a new Tuist project.

💡 Remember that each feature should not depend on other features’ implementation. Only interfaces should be public

So, for each feature, we will create several targets corresponding to the feature interface, implementation, and testing or mocking targets if required.

Defining project

💥 Sources for this post are published on GitHub. So, before reading this article you can see how elegant describing a project could be when using Tuist

Structure

Tuist project is a simple folder with config files describing your workspace structure

💥 Use tuist edit to edit your Tuist files. This will generate and open a temporary Xcode project for you

But as I said early, each module should have at least an implementation and interface target

💡 There could be modules that contain common tools and that are not dependent on any other module. Then, it might have implementation only

So, let’s modify the structure according to that

Before defining modules, we need to define where Tuist should search for these modules. This can be done in Workspace.swift file

Project file

Tuist defines the Xcode project with a simple Swift file.

But this post is not just a review of Tuist

Let’s define a project, knowing that we need to have an interface and implementation targets. Also, let’s create an enum for feature names so that we don’t have to use strings and remember all namings

💡 As config is defined in Swift, you can use the power of suggestions and auto-completion in Xcode while defining your project structure.

For example, Xcode will suggest other modules’ names when using enums

With several simple helpers, we could define project structure with Swift’s beauty:

Features are going to be separate frameworks.

💡 All swift files that help to describe tuist configs should be placed in the ProjectDescriptionHelpers folder

Then, we can define what feature is

Finally, we combine modules in an app target. It’s defined in the same way

That’s it.

Now we can create different features and state dependencies between them. After that, we simply use tuist generate command and it generates Xcode workspace and Xcode projects for us.

Tuist-generated workspace
Tuist-generated workspace

Great!

Now we have our project bootstrapped, and it is fully defined in nice Swift files with a clean structure and explicit dependencies. You can add all .xcodeproj and .xcworkspace to gitignore and forget about a mess in GitHub repositories.

💥 Some details are not covered for the brevity of this post. The full example is published on GitHub and do not hesitate to ask me about anything in the comments!

Creating an app with Tuist

I already showed how to define project structure in the examples above. Let’s get even more specific and write a simple app that will show a random value in a range.

RandomProvider defines protocol for generating random number and several implementations for it

RandomScreen defines several UI screens to display random number and re-generate it. Notice that it depends only on RandomProviderInterface and not on RandomProvider which is implementation

Common is a module that provides common tools. Actually, it is used only by App module, but I wanted to show that many modules can depend on it

ExampleApp is an app module that combines other modules and builds the final app

This is the only module that can depend on other modules’ implementation. Moreover, it chooses which implementation to use depending on the scenario. In the example app, NumberProvider implementation is changed in runtime

Final notes

So, in this post we constructed microfeatures or modular app using Tuist. In example project I added useful tools like

  • Additions to default Info.plist
  • Template for creating a new feature that can be invoked by
    tuist scaffold framework --name ModuleName. This will create new module folder, Project.swift file
  • Building for release mode. You can invoke generation with environment variable and this will make all modules static. Using static frameworks improves app speed and good for producion.
    TUIST_BUILD_TYPE_RELEASE=TRUE tuist generate --no-cache

Also, If you have not read my article on general overview of modular architecture, check it out!

Do not hesitate to ask anything in the comments

This post was originally published on alexdremov.meCheck out my blog!

References

--

--