Modular Project Structure with Swift Package Manager (SPM)
Every application consists of a couple of features. The application is composed of separate features that can be connected.
Let’s start with a minimalistic set of features.
In addition to these features, it also has supporting components like Network, Database, Logger, etc.
As a technical person, be it developer, or software engineer, we all think this in terms of Source Code.
# A project that has lots of files.
Every feature has been achieved with the help of a ton of code files, and feature code is communicating with other feature code.
Each of the features will have some or all kind of code files as, Storyboard, View Controller, View Model, Presenter, Model, Requester/Responder and many more based on our knowlege, choice of the architecture design pattern and the additional requirement the respective feature has.
The feature will be communicating with Network and Database. It will be using utilities like Logger, Analytics, etc.
Hope, we all are good with this set of features and high-level component identification.
# Let’s create a skeleton of the identified features, components, and high-level files that will help us to implement.
The overall project structure is as in the above-depicted picture. It looks neat and clean. However, there’s scope for improvement by having segregation of UI, Domain/Use cases, Business Logic, etc., files.
We all have heard the word called Monolithic. If now above picture is the best example of Monolithic.
In this context, Monolithic is the traditional unified model for the design of a software program, which means composed all in one piece.
# This is understood by most of us in the backend world and not in Mobile application development.
We will not discuss the Monolithic pros or cons from loose coupling, maintainability, performance, build time, etc. We will try to keep it simple.
## Few observations are based on the structure of the project.
- Only one target with the app source.
- Only one Storyboard by Design. (NOTE: We can have multiple)
- Only one Test Target by Design.
- As time passes, maintaining the project structure is cumbersome. In short, no one gone follow it.
How to overcome the problems by design?
Modular structure of iOS applications to enable self-governing, sovereign, scalability, optimize the build and independent test cycles, and ensure good practices in our project.
The core idea is to build the project by building independent features that are interconnected using clear and concise APIs use Protocol Oriented Programming.
What do we have to do?
- Know the autonomous features
- Know the vertical layers
- Know the common checkpoint
- Know integrated and aggregated systems
Why Modular Architecture?
- The monolithic project is Hardstone.
- Brings frustration when we are asked to work on those projects.
- The project tends to grow in size (large codebase/large team) as the day passes.
- It turns complex, tough to manage, hard to manage by resources.
- Divide and conquer
- Small modules
“Modularising an project consists on organising an source code and resources in multiple modules”
The beauty of modular architecture is that we can replace or add any one component (module) without affecting the rest of the system.
The modular architecture will support the scalability of large XCode codebases. To make, manage, build, maintain we need platform support, support of tools, and IDE support.
Modular architecture using Swift Package Manager
- It is the “official” package manager for Swift, so its support is about as solid as we can get.
- Swift Package Manager is a single cross-platform for building, running, testing, and packaging our swift code.
- Configuration of packages is written in Swift itself, making it easy to configure targets, declare products and manage package dependencies.
- Swift Package Manager organizes code into modules.
- Each module specifies a namespace and enforces access controls on which parts of that code can be used outside of the module.
Read to understand the µFeatures Architecture
Splitting projects into multiple small modules using local packages/dependencies.
Let’s say we have a project called ModularWithSPM. Make sure we add/create an explicit folder/directory called ‘Packages’.
NOTE: We can choose the name for this folder/directory.
- Create a New -> Package…
2. Navigate to the folder/directory ‘Packages’ or other which we have created.
3. Name the Package as ‘Common’
4. It will open a new Xcode instance with the Package -> Common as shown in follow.
5. Let’s add the supported platforms with supported targets.
NOTE: We all can read the Package.swift file and understand other configurations as per our own space.
NOTE: We can close the Xcode Instance of Common Package project.
6. Go to the ModularWithSPM project. Right-click on the Project file and click on Add Packages…
7. Click on Add Local
8. Add the Common package. (NOTE: We might need to navigate to the Package folder)
Select Common package and click on Add Package button.
9. Our Project Navigator will look like below.
10. Add the Common Package
Go To Targets -> Build Phases -> Link Binary With Libraries
Select Common -> Add Button
11. Now feel free to, Import Common Package and use it as we want.
Follow, Step 1 to Step 11 keep adding more packages into project.
i.e., Network, Favourites, Home, Logger, Login, Registration, and etc.
These are a handful of steps. This is a fairly basic yet powerful guide. At a very high level what we get to motivate all of us to begin with.
1. Modular project structure
2. Write the test cases to the individual to the package.
3. Have a feature-specific storyboard.
4. Have a feature-specific Assets Catalog.
5. Sharing the module
You can easily drag the Package into any other project right away.
Go To Targets -> Build Phases -> Link Binary With Libraries
import module and use it.
Once we understand the importance of modular design and start with a small step to begin with we can achieve greater heights for sure!!!
Don't limit modularity for project structure only. Understand the advantages of modular architecture and apply it for functional aspect too and see the benefits you get.
How to cache the local packages/dependencies?