---
title: "iOS App As a Microservice. Modularize Your App With Tuist"
slug: "ios-app-as-a-microservice-modularize-your-app-with-tuist"
date: "2022-10-07T10:14:04.000Z"
description: "This is the second article in a series on modular app architecture. In this post, I will cover implementation details using Tuist"
---

# iOS App As a Microservice. Modularize Your App With Tuist

**Tuist** 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!

[

iOS App As a Microservice. Build Robust App Architecture

What will you choose: MVVM, MVC, VIPER? Those all are local and problem-specific architectures. But how to structure your app on a larger scale to make it scalable and well-organized?

![](https://alexdremov.me/assets/icons/apple-touch-icon.png)Alex DremovAlex Dremov

![](https://images.unsplash.com/photo-1532622785990-d2c36a76f5a6?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8c2VhcmNofDV8fHN0cnVjdHVyZXxlbnwwfHx8fDE2NjMyMzA3ODU&ixlib=rb-1.2.1&q=80&w=2000)

](https://alexdremov.me/ios-app-as-a-microservice-build-robust-app-architecture/)

## 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:** now available

[

iOS App As a Microservice. Using SwiftUI in Modular App

The modular architecture is excellent. But how to implement it effectively with SwiftUI? From its core, SwiftUI is state-driven, and it can be tricky to modularize an app and define exact responsibility borders.

![](https://alexdremov.me/assets/icons/apple-touch-icon.png)Alex DremovAlex Dremov

![](https://images.unsplash.com/photo-1581291518633-83b4ebd1d83e?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fGludGVyZmFjZXxlbnwwfHx8fDE2NjYxMjA1NzM&ixlib=rb-1.2.1&q=80&w=2000)

](https://alexdremov.me/ios-app-as-a-microservice-using-swiftui-in-modular-app/#whats-the-problem)

## 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

[

GitHub - AlexRoar/TuistExample: Using Tuist for modular app architecture

Using Tuist for modular app architecture. Contribute to AlexRoar/TuistExample development by creating an account on GitHub.

![](https://github.com/fluidicon.png)GitHubAlexRoar

![](https://opengraph.githubassets.com/504ecc1c580a5e07e18a7a1546831b4328d13bfcbbd90a7c6cc0b0d35be53e18/AlexRoar/TuistExample)

](https://github.com/AlexRoar/TuistExample)

### Structure

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

```
Your project root
├── Workspace.swift
├── Tuist
│   ├── Config.swift
│   ├── Dependencies.swift
│   └── ProjectDescriptionHelpers
│       └── <tuist helpers>
└── modules
    ├── Foo
    │   ├── Project.swift
    │   └── <module code, folders>
    ├── Biz
    │   ├── Project.swift
    │   └── <module code, folders>
    └── ...
```

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

```
Your project root
├── Workspace.swift
├── Tuist
│   ├── Config.swift
│   ├── Dependencies.swift
│   └── ProjectDescriptionHelpers
│       └── <tuist helpers>
└── modules
    ├── Foo
    │   └── Project.swift
    │       ├── interface
    │       │   └── <interface files>
    │       └── src
    │           └── <implementation files>
    ├── Biz
    │   └── Project.swift
    │       ├── interface
    │       │   └── <interface files>
    │       └── src
    │           └── <implementation files>
    └── ...
```

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

```swift
import ProjectDescription

let workspace = Workspace(
    name: "ExampleWorkspace",
    projects: [
        "modules/*"
    ]
)
```

Subscribe and don't miss posts!

### Project file

Tuist defines the Xcode project with a simple Swift file.

```swift
// Project.swift
import ProjectDescription
import ProjectDescriptionHelpers

let project = Project(
  name: "ProjectName",
  targets: [
  	...
  ]
)
```

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:

```swift
import ProjectDescription
import ProjectDescriptionHelpers

let project = Project(
    name: Feature.Foo.rawValue,
    targets: [
        .feature(
            implementation: .Foo,
            dependencies: [
                .feature(interface: .Biz),
                .external(.AsyncAlgorithms)
            ]
        ),
        .feature(
            interface: .Foo,
            dependencies: [
                .feature(interface: .Biz)
            ]
        )
    ]
)
```

Features are going to be separate frameworks.

💡

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

```swift
public extension Target {
    static func makeFramework(
        name: String,
        sources: ProjectDescription.SourceFilesList,
        dependencies: [ProjectDescription.TargetDependency] = [],
        resources: ProjectDescription.ResourceFileElements? = []
    ) -> Target {
        Target(
            name: name,
            platform: .iOS,
            product: defaultPackageType,
            bundleId: makeBundleID(with: name + ".framework"),
            sources: sources,
            resources: resources,
            dependencies: dependencies
        )
    }
}
```

Then, we can define what feature is

```swift
public extension Target {
    static func feature(
        interface featureName: Feature,
        dependencies: [ProjectDescription.TargetDependency] = [],
        resources: ProjectDescription.ResourceFileElements? = []
    ) -> Target {
        .makeFramework(
            name: featureName.rawValue + "Interface",
            sources: [ "interface/**" ],
            dependencies: dependencies,
            resources: resources
        )
    }
    
    static func feature(
        interface featureName: Feature,
        dependencies: [ProjectDescription.TargetDependency] = [],
        resources: ProjectDescription.ResourceFileElements? = []
    ) -> Target {
        .makeFramework(
            name: featureName.rawValue,
            sources: [ "src/**" ],
            dependencies: dependencies,
            resources: resources
        )
    }
}
```

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

```swift
public extension Target {
    static func makeApp(
        name: String,
        sources: ProjectDescription.SourceFilesList,
        dependencies: [ProjectDescription.TargetDependency]
    ) -> Target {
        Target(
            name: name,
            platform: .iOS,
            product: .app,
            bundleId: makeBundleID(with: "app"),
            deploymentTarget: .iOS(targetVersion: "16.0", devices: .iphone),
            sources: sources,
            dependencies: dependencies
        )
    }
}

let project = Project(
    name: "ExampleApp",
    targets: [
        .makeApp(
            name: "ExampleApp",
            sources: [
                "src/**"
            ],
            dependencies: [
                .common,
                .feature(implementation: .Foo),
                .feature(interface: .Foo),

                .feature(implementation: .Biz),
                .feature(interface: .Biz),

                .external(.FoggyColors)
            ]
        )
    ]
)
```

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](https://alexdremov.me/content/images/2022/10/Screenshot-2022-10-07-at-00.43.44-min.png)

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!

[

GitHub - AlexRoar/TuistExample: Using Tuist for modular app architecture

Using Tuist for modular app architecture. Contribute to AlexRoar/TuistExample development by creating an account on GitHub.

![](https://github.com/fluidicon.png)GitHubAlexRoar

![](https://opengraph.githubassets.com/504ecc1c580a5e07e18a7a1546831b4328d13bfcbbd90a7c6cc0b0d35be53e18/AlexRoar/TuistExample)

](https://github.com/AlexRoar/TuistExample)

## 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.

![](https://alexdremov.me/content/images/2022/10/graph.svg)

App Architecture

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

```swift
// Interface
public protocol NumberProvider {
    var number: Int { get }
}

// Implementation
public struct NumberProviderZero: NumberProvider {
    public let number = 0
    
    public init() {
        
    }
}

public struct NumberProviderRandom: NumberProvider {
    private let range: ClosedRange<Int>
    
    public var number: Int {
        Int.random(in: range)
    }
    
    public init(range: ClosedRange<Int>) {
        self.range = range
    }
}
```

**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 the implementation

```swift
public struct RandomScreenSimple: RandomScreen {
    let randomProvider: NumberProvider
    
    @State var number: Int = 0
    
    public init(randomProvider: NumberProvider) {
        self.randomProvider = randomProvider
    }
    
    public var body: some View {
        VStack {
            Text("\(number)")
            Button("generate") {
                number = randomProvider.number
            }
        }.onAppear {
            number = randomProvider.number
        }
        .animation(.default, value: number)
    }
}
```

**Common** is a module that provides common tools. Actually, it is used only by the 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

0:00

/

1×

Example app

## Final notes

So, in this post, we constructed a modular app using Tuist. In the 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 a new module folder, Project.swift file
*   Building for release mode. You can invoke generation with an environment variable and this will make all modules static. Using static frameworks improves app speed and is good for production.  
    `TUIST_BUILD_TYPE_RELEASE=TRUE tuist generate --no-cache`

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

[

iOS App As a Microservice. Build Robust App Architecture

What will you choose: MVVM, MVC, VIPER? Those all are local and problem-specific architectures. But how to structure your app on a larger scale to make it scalable and well-organized?

![](https://alexdremov.me/assets/icons/apple-touch-icon.png)Alex DremovAlex Dremov

![](https://images.unsplash.com/photo-1532622785990-d2c36a76f5a6?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8c2VhcmNofDV8fHN0cnVjdHVyZXxlbnwwfHx8fDE2NjMyMzA3ODU&ixlib=rb-1.2.1&q=80&w=2000)

](https://alexdremov.me/ios-app-as-a-microservice-build-robust-app-architecture/)

Do not hesitate to ask anything in the comments

## References

[

Xcode on steroids | Tuist

Tuist is a tool that helps developers manage large Xcode projects by leveraging project generation. Moreover, it provides some tools to automate most common tasks, allowing developers to focus on building apps.

![](https://tuist.io/icons/icon-512x512.png?v=afd926b5da3ecabd886495871849f751)Tuist - Xcode on steroids

![](https://tuist.io/squared-logo.png)

](https://tuist.io)