![Swift Package Manager](https://d382vuhe6yd0tq.cloudfront.net/wp-content/uploads/2023/10/Swift-Package-Manager.webp)
In the realm of iOS development, developers often encounter recurring code patterns. While efforts are made to streamline these patterns, imagine the convenience of encapsulating this recurring code within a file, making its functionalities accessible through simple function calls.
Historically, third-party libraries have been integrated into our projects using tools like CocoaPods, Carthage, and the Swift Package Manager (SPM). Presently, SPM stands out as the most reliable method, primarily because it’s Apple’s recommended approach for adding third-party libraries. This brings us to an intriguing idea: Why not package our reusable code as a Swift Package? This not only promotes code reusability but also simplifies its integration into various projects, saving significant setup time.
Setting Up the Swift Package Manager
1. Prerequisites: Before diving in, ensure you have a GitHub account, as the Swift Package will be maintained in a git repository.
2. Creating a Swift Package:
- Launch Xcode and navigate to the multiplatform section. Here, under frameworks, you’ll find the ‘Swift Package’ option.
- Select ‘Swift Package’ and proceed to fill in the necessary details, such as the file name and storage location.
3.Setting Up a GitHub Repository:
- Log in to your existing GitHub account and create a new repository.
- Once the repository is set up, you can either:
1. Directly move your SPM to the repository folder on your Mac.
2. Push the SPM to the repository using git commands.
4. Creating an Example for the Swift Package:
- It’s beneficial to include an example within the repository. This allows users to test the Swift Package seamlessly. Additionally, it provides the flexibility to edit and update both the repository and SPM simultaneously.
- Start by creating a standard iOS Xcode project. For illustration, let’s consider an iOS project named ‘Package Example’ and a Package/Repository named ‘BasicPackage’.
- Add the files for the iOS project inside an ‘Example’ folder in the repository.
With the foundational setup complete, we can now delve into the coding aspect.
Setting Up Package Dependencies and Implementing Basic Logic
The heart of the Swift Package Manager (SPM) is the Package.swift file. This is where all dependencies are declared, ensuring they’re installed when our Swift Package is utilized.
1. Initial View of Package.swift: Upon opening Package.swift, you’ll encounter its default structure. This is the foundation upon which we’ll build our package.
2. Declaring Platforms: It’s crucial to specify the platforms and their versions when setting up a package. This is especially important when certain dependencies have minimum OS version requirements. For our package, we’re setting macOS version 10.14 and iOS version 12 as the baseline. To achieve this, insert the following code beneath the package name in Package.swift:
3. Products Configuration: The products section defines the executables and libraries produced by the package. Modifying the name here will alter the name
of your library and its associated target:
4. Adding Dependencies: The dependencies array is where external dependencies for our package are declared. For our use case, we’re integrating Alamofire, Kingfisher, and SDWebImage. To include these, you’ll need their respective git links and desired versions.
Pro Tip: If you’re on the hunt for Swift Packages, the Swift Package Index is a valuable resource.
Within the dependencies section, use .package(url: String, Version) to declare each dependency. It’s recommended to set the version to the next major release to ensure compatibility.
5. In dependencies we have to add .package(url: String, Version) so we will add version to next major version.
These Dependencies are useless until and unless we add dependencies in target too.
So we will add dependencies inside target and name them as we need them inside the package.
6. To see these dependencies in action, press Command + S
Using Dependencies inside Package and make Functions
Now the next step is to use these dependencies and make our custom functions to perform action.
These functions will be public as these are to be used in project from Package.
Lets dive into the code and process to make a API call and other minor functions like opening a website in safari and loading and uploading image.
- We’ve seen a folder named Sources, That is the main function which has another folder with your package name, In my case BasicPackage.
- Inside this BasicPackage folder we will be having a main swift file where we will create functions and also the supporting files will be inside this folder.
- Whatever is created inside this file is accessible to the user only if it is marked as public. Public access identifier will let that function to be used and private or just func will not let user use this function.
- To create a API call lets first create a network manager where we will call API and handle response and return from main file (BasicPackage.swift).
- Create a Network manager swift file inside BasicPackage folder and import Alamofire.
- Use Alamofire and create a API request.
- Here we can see Reachability and Errors which is used.
Just to tell in brief Reachability is used to check whether the device is connect to internet or not and Errors is just a enum for error types. We’ll go though the files soon. - Here in makeAPICall function we are taking the mandatory parameters and setting default for those which is not necessary for a API call.
Lets now see Reachability and Errors file which is added in the same BasicPackage folder.
This is how my files look in project navigator.
The Error file consists of Error types only, where it has a variable that returns string.
Reachability is the class where we have a function to check network connectivity and will return Bool value about its connection status.
Using Reachability.isConnectedToNetwork before API call is done so that we don’t need to call API unnecessarily even when it is not connected to internet and can return error if its disconnected.
To send a custom error we have a case of apiError in Errors which sends custom error message.
- Now for the API calls we will use Network manager and call api from main file (BasicPackage.swift).
- This is how we have called API from BasicPackage.swift and made a function.
- Here in line 29, we have used NetworkManager and made a API call from Alamofire. To send the parameters for makeAPICall we’ve added parameters in top level function so it will be taken from user.
- We have a in line 22, It is used to get a generic type and used in line 25 and line 27.
- In line 25, We are sending a model which is codeable model to map the response.
- In line 27, We are using a closure to send the response and result of API call. We are sending the Model(Response) in success and String(Error) in failure.
- We have to use ‘where V: Decodable’ to set the .type and map the response in desired codeable model.
- Use switch inside the function to switch between result of api call from alamofire function from NetworkManager.
- Success will give JSON Data and failure will give error in Errors type.
Failure case:
Switching result will give 2 cases, Success and Failure. In failure we are simply sending a completion with nil and error.displayMessage. Here nil will be for result we get and error will be sent in string format.
Success case:
- We will decode the data which we get in JSON format from API to the desired codeable model.
- We are using a do, try, catch block as decoding can throw and error.
- In do block we are first printing the JSON data in string with readable format. For this I have created variable in extension of Data.
- Next we are decoding using JSONDecoder and sending it in completion with nil in error string and object in response.
- In catch block we are just sending custom error from Errors and sending nil for response.
This is how API is called and now we will see it working and giving result.
Using in an iOS project
Import the SPM like we usually do in iOS projects.
1. Open project -> File -> Add Packages and then paste the git link and add.
To use the SPM function we have to import BasicPackage in the file.
2. Start typing callApiWithSPM inside the function you want. You’ll get a autocomplete for our API function.
3.Here our api call from SPM is just from line 29 to 33 which can also be converted into one line if we remove formatting for better visibility.
4.Url(String) is taken from constant file and sent.
Body(Dictionary) is the request body.
Type(Codeable model) is the model in which we want the response to be mapped.
Method(String) is the http method, here we have a file from where it is getting “POST”.
5.The response will be in closure where we’ve named it “response”.
6.The error will be string and we are getting it in “error”.
7.Handling error and response is dependent on user.
Here we are using guard let to get error and show alert otherwise we have error in response body so checking that and handling it accordingly.
Conclusion
The Swift Package Manager (SPM) represents a significant stride in the evolution of iOS development. By streamlining the integration of reusable code and third-party libraries, it not only enhances the efficiency of development but also fosters a more collaborative and modular approach to building applications. As developers, embracing tools like SPM allows us to stay at the forefront of innovation, ensuring our projects are both scalable and maintainable. As the tech landscape continues to evolve, leveraging such powerful tools will be paramount in delivering robust and high-quality applications. Whether you’re a seasoned developer or just starting your journey, diving deep into SPM and its capabilities can undoubtedly elevate your coding prowess.