Create Reusable Swift Package Modules with Storyboards
Swift Package is a great tool to help us create reusable components that can be used in different apps. Swift Packages now are more powerful than before and can handle variety of resources including Storyboards!
In this quick tutorial, we’ll learn how to create a Swift Package that includes view controllers with storyboards. The goal is to make it act as a standalone module component, so later we can integrate it in any iOS app.
Let's imagine we’re a bakery store and already have an app on the App Store. We’re working on a new different app to replace the current, but in the meanwhile, we have a new cake and coffee sections that we’d like to introduce in our app. Should we wait till the new app finishes, or we build the feature twice?
Here comes the power of Swift Packages. We can build the feature including its UI elements as a Swift Package module then use it everywhere.
We’ll start with creating a new Swift Package. Go to Xcode -> File -> New -> Swift Package… and name our package CakeSP
Our initial Swift Package project will look like this:
We won’t change the Package.swift manifest file in this tutorial. Our goal is to create what’s like a small app with some storyboards. All our work and files should go inside Sources -> CakeSP folder.
For organization purposes let’s create a new folder called ViewController inside the CakeSP:
In the ViewController folder, let’s create two view controllers with their storyboards, CakeVC and CoffeeVC:
After adding a ViewController view to the storyboard, make sure to check Is Initial View Controller and to specify the class in the Identity inspectory:
We want to fill the CakeVC and CoffeeVC with some content. Let’s create an asset folder to store our images. Right click on CakeSP folder -> New File -> Asset Catalog:
Let’s drop some cake and coffee images in there:
In the storyboard, we can create a quick design by adding a Label and some image views that are set using our assets:
Do the same for the CoffeeVC storyboard as well:
Don’t forget to make sure that “Is Initial View Controller” is checked, otherwise we won’t be able to reference them.
Our view controllers need to be public so they’re accessed from outside our package. Change the CakeVC and CoffeeVC to public as well as the methods inside them:
import UIKitpublic class CakeVC: UIViewController{ public override func viewDidLoad() { }}
Every storyboard file has an initial view controller as we’ve set it. To get that initial view controller, we use instantiateInitialViewController() method from the UIStoryboard object. For clarity, let’s declare a static public property that will return the initial view controller:
public class CakeVC: UIViewController{ public static let storyboardVC = UIStoryboard(name: “CakeVC”, bundle: Bundle.module).instantiateInitialViewController()! public override func viewDidLoad() { }}
It’s crucial to use Bundle.module as it represents our Swift Package, the containing module.
We do the same with the CoffeeVC class, adding public and storyboard access declarations.
We’re done with Swift Package part and ready to use it in our apps.
We can upload the Swift Package to Github, then add it from there as a dependency, or we can just copy it to our projects.
First, copy the package to the desktop, drag the root package while clicking “alt” key. Make sure you see a green “+” sign, then drop it. The green “+” indicates that i’ll be copied.
Now, it’s ready to be dropped in our app’s workspace.
Let’s go to our BakeryStore app and add this package as a new feature. All there is to it is to drop the Swift Package into the root of the project:
Then, we need to go to our app’s Targets -> “Frameworks, Libraries, and Embedded Content” and click “+” to add our CakeSP Swift Package to the app. We do this step for every target where we want to use CakeSP, such as, an App Clip target:
Then, to use it, we go to the the view controller in our app and import CakeSP package:
import UIKit
import CakeSPclass ViewController: UIViewController { override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}}
Let’s say our app’s view controller looks like this:
When we press Cake, we should navigate to the Swift Package’s CakeVC, and when we press Coffee, we should navigate to the Swift Package’s CoffeeVC.
Here is the action for each button:
@IBAction func openCakeVC(){
let vc = CakeVC.storyboardVC
present(vc, animated: true, completion: nil)
}@IBAction func openCoffeeVC(){
let vc = CoffeeVC.storyboardVC
present(vc, animated: true, completion: nil)
}
We have access to CakeVC and CoffeeVC because they’re declared as public.
Using the classes names, we can access their view controllers by using the static property storyboardVC. It knows to look for it in the package’s storyboards because we used Bundle.module when we declaring storyboardVC. The Bundle.module represents the containing package, which is the CakeSP package.
Linking the actions to our buttons, we get this result:
It’s as simple as that.
Swift Packages as we see are a great technology to create standalone components or mini apps that can be reused in any context.
I hope that was helpful!