# Mastering Modularization: Building Swift Packages with Swift Package Manager

### Introduction

Hi folks, today we're diving into the importance of modularization in Swift application development. Later in this post, I'll walk you through creating your own package using Swift Package Manager and how to integrate it into other Xcode projects.

### Why Modularization Matters

Before we get started, let's explore the benefits of modularization. In today's fast-paced software development world, keeping a clean and efficient codebase is crucial. Modularization, a technique that divides your software into separate, manageable modules, is key to achieving this. Here are some of its benefits:

* **Improved Code Organization**: makes your code easier to manage and navigate.
    
* **Reusability**: allows you to reuse code across different projects, saving time and effort.
    
* **Scalability**: facilitates scaling projects by enabling independent development of modules.
    
* **Testing**: enables isolated testing of modules.
    
* **Collaboration**: allows multiple developers to work on different modules simultaneously.
    

Understanding these benefits highlights the value of modularization. Now, let's get to work.

### Creating a Swift Package

A network manager would be an ideal candidate for modularization since most Swift applications interact with the network to fetch and post data. So in this post, I'll show you how to create a reusable package to deal with network requests. However, I won't dive deeply into the implementation details of network manager, as that's not the main focus of this post. In general, I used **protocols** and **generics** to support various request and response types and the **Swift concurrency** framework for handling asynchronous tasks. I also stubbed network requests using **URLProtocol** to write unit tests for the network manager. If you are interested in the implementation details, I have posted the link to the package at the end of this post.

Let's start creating the package using Xcode. To do this, select **File -&gt; New -&gt; Package** from the Xcode menu.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721344094445/86c72484-5318-4009-9a3e-7b51325e048e.png align="center")

Next, let's name the Swift package as `NetworkManager`. Make sure to check the "Create Git Repository" option ✅ as shown in the screenshot below. Then, click "Create" 🚀.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721347414890/531a1fa4-0180-4bb7-8987-75403814d093.jpeg align="center")

Once done you'll notice the structure of the project is a bit different from a typical iOS project structure that you are used to. The main difference is that, instead of a `<appname>.xcodeproj` file, we have a `Package.swift` file.

Here is how the `Package.swift` file will look like:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721348011313/be2af14c-f7d1-439e-86f9-eae301fb9901.png align="center")

The `Package.swift` file is the manifest file used by Swift Package Manager (SPM). This file is essential for SPM to resolve dependencies, build the package, and integrate it into other Swift projects.

Here are the key configurations that can be used with the `Package.swift` file and their purposes:

* **Name**: The name of the package.
    
* **Platforms**: Specifies which platforms (like macOS, iOS, Linux) the package supports and any minimum version requirements.
    
* **Products**: Lists any products (libraries or executables) that should be built from the targets.
    
* **Dependencies**: Lists the dependencies required by the package. These can be other Swift packages or external libraries needed for the package to function.
    
* **Targets**: Specifies the targets (modules of Swift code) that are part of the package.
    

We can see `name`, `products` and `targets` configurations are already included by default. Other than that, we need to include `platform` dependencies for our package. Since the network manager will be used in both iOS and macOS projects, I am going to include both iOS and macOS platforms as shown below:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721349384278/f0725317-afd5-4743-aa14-746af90345e8.png align="center")

Note that our package does not depend on any third-party packages. So, we **won't** be adding any `dependencies` to the manifest file. However, let's say you want to use SnapKit as a dependency for your package, you can add the following section to the `dependencies` array in the manifest file:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721352047382/731fca7f-1a1b-43c5-a1ac-743d6a69f7e5.png align="center")

But make sure to include the dependencies before the targets, as the order does matter. The expected order in the manifest file is: ***name -&gt; platforms -&gt; products -&gt; dependencies -&gt; targets*.**

Next, we’ll include the implementation files for the network manager. All implementation files should go inside the `Sources` folder.

After that, there’s an important thing to keep in mind. That is **Access control**! By default, everything in Swift is `internal`, meaning that those functions and classes won’t be visible outside this module. Ensure that everything you need to expose outside this package is declared as `open` or `public`. For more information about access control, you can refer to Apple's [documentation](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/accesscontrol/#Access-Levels-for-Frameworks) here.

Once everything is in place, make sure the project builds successfully.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721353119789/85b4bcc7-b947-48c5-80dd-f0c949776af1.png align="center")

Next, let's go ahead and include the unit test files for the package. Unit test files should be created inside the `Tests` folder.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721353174722/709e92bc-ad38-435d-ab87-5286be84e421.png align="center")

To run the unit tests, you'll need to switch to the test scheme. If there is no test scheme available, you can create a new one as follows.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721353692315/b5b5f319-9014-4278-a437-ad6ba816f57e.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721353726666/9df9aa71-3bfc-4256-9832-99480b8033f9.png align="center")

Next, select the test scheme and press **⌘ + U**. Make sure all tests are passing ✅.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721354602045/725cc849-41ab-41e1-848d-4fe788a477dd.png align="center")

Finally, we need to add a `README` file for the package. This file will serve as the front page of our package on GitHub and will also be visible to users when importing the package through Swift Package Manager in Xcode. In the `README` file, you should include details on how to install and how to use the package in an iOS or macOS project 📚.

To create the README file:

1. **Right-click on the package name**.
    
2. **Click on New File** 📄.
    
3. **Rename the File**: Name it `README.md` ✏️.
    
4. **Include Package Details**: Add the necessary information about the package inside the README file 📝.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721429415590/bf6df090-ab5b-407e-b226-1916ae1e5d91.png align="center")

Now the setup is complete. Next, we need to push the package to GitHub. Before that, if you haven’t already configured Xcode to connect with your GitHub account yet, please follow this [link](https://www.swiftyplace.com/blog/xcode-github-access-token) and set it up.

Once your GitHub account is configured:

1. **Open Source Control Navigator**: Go to **Source Control Navigator -&gt; Repositories** in Xcode 🔍.
    
2. **Right-click on the Package Name**
    
3. **Click on New Remote**: Select `New "NetworkManager" Remote` to add your GitHub repository 🌐.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721429979188/a234ddcd-0094-45f5-ab95-a5778e7d2d61.png align="center")

Then you'll get a prompt to create the GitHub repository. You can change the default package name if needed and set the visibility to **Public** 🌍. Then, click **Create** 🚀.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721430456741/d15582d7-b51a-42b9-ab4c-e9087e953db1.png align="center")

This will push your package to GitHub, making it available for others to use and collaborate on 🚀.

Now we need to commit our changes to the GitHub repository we just created. To do this:

1. **Press ⌘ + ⌥ + C.**
    
2. **Stage the Changes**
    
3. **Add a Commit Message**: Enter a descriptive commit message 📝.
    
4. **Click Commit** ✅.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721430801473/6efdcb89-23bf-49ff-9efe-95bd1110858f.png align="center")

Before pushing your changes to GitHub, we need to **version** our package. Versioning is important when importing the package into other projects and also it helps us to maintain different versions of the package 📦.

To create a version:

1. **Open Source Control Navigator**: Go to **Source Control Navigator -&gt; Repositories** in Xcode 🔍.
    
2. **Right-click on the Package Name**.
    
3. **Click on Tag "main"**.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721431466811/f902bc42-14be-4b6d-a2ba-26f6222108b6.png align="center")

In the popup, enter a version number and a message, then click `Create` 🎯.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721431526815/e634b85c-4711-469f-8492-78897ab1f59c.png align="center")

This will tag your current state as a version, making it easier to manage and track different versions of your package.

Now, let’s push our changes to GitHub. Make sure to check ✅ the `Include tags` option before pushing.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721431717005/38e49aca-2d3a-4f19-b911-b79b232e6294.png align="center")

Now, head over to GitHub and verify everything is in order. Inside the newly created `NetworkManager` repository, you should see:

* The updated README with the package description 📄.
    
* One tag, which is listed under the **Releases** section 🏷️.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721432141986/d1648860-c703-4693-960c-fcc1791a2e69.png align="center")

Now it's time to test our newly created package🤗. Switch back to Xcode and create a new iOS project📱.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721432802985/badd383b-d694-44f0-ae35-79b3ad92698c.png align="center")

Let’s give a name to the new project and save it 📝. To import our package, we need the URL of the `NetworkManager` repo. Switch back to GitHub and go to your repository, click the green **Code** button, and copy the URL 🌐.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721433569004/4f0c1db8-93ce-444c-b152-58979c1d59a6.png align="center")

Then, switch back to Xcode, go to the `Project Navigator`, and select `Package Dependencies` as shown in the screenshot.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721433679260/4e1831fe-1d7e-47bd-ab91-cc1115e0d9ee.png align="center")

Click the ➕ button. In the popup, paste the copied URL into the search box 🔍. You should then see our newly created package.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721434738501/6a9ca916-44f7-4d7c-aa5e-73e2035a14a9.png align="center")

Go ahead and click `Add Package`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721434781572/d7a14406-851b-48ce-8378-552eb0514a3b.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721434811868/cefebf7e-bf35-43f0-85da-a57e08ea4933.png align="center")

Once that is done, open the `ViewController.swift` file and you should be able to import the `NetworkManager` package as shown below📦. Thats it! 🥳.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1721434881539/f8ee8503-a6bd-4261-bfea-97150d6d42d2.png align="center")

### Conclusion

And that’s a wrap! 🎉 We've successfully created, uploaded, and integrated a Swift package into a new project. This process involved setting up the package, managing its versions, and finally importing it into an Xcode project for use. By now, you should have a solid understanding of how to modularize your code using Swift packages, making your projects cleaner and more manageable.

I hope you find this guide helpful and informative. Here is the [link](https://github.com/ganukeperera/NetworkManager) to the GitHub Repo I have already created. If you have any questions or need further assistance, feel free to reach out. Happy coding! 🌟

Until next time... Ciao! 👋
