Frameworks are great for modularizing your code, breaking down reusable components into a separate bundle of code.
For example, consider an app that provides the functionality of recording the screen. We move the methods and classes related to recording into a different framework, naming it RecordingKit. This framework is created with reusability in mind and can be used in other scenarios as well. Also, other engineers can work in it independently.
In this article, we’ll learn these steps:
- Create a framework
- Import it into a project
- Use it in a project
To follow along, you should have basic knowledge of the Swift language and experience working with Xcode.
Introduction
With the recent craze over the stock market and cryptocurrency, we want an investment tracker app. These will track the prices of stocks on a stock exchange and a few cryptocurrencies, respectively.
The app needs a settings screen, and to maintain consistency, we don’t want to duplicate the code. So, we’ll create a framework called SettingsKit to reuse across our app (or maybe more in the future).
Creating a new framework
Open Xcode and create a new project. Select Framework under the iOS section.
Fill in the template options as follows, then click Next:
- Product Name: SettingsKit
- Organization Identifier: The identifier you want to use for your framework. For example,
com.rudrankriyam.SettingsKit
- Language: Swift
- Uncheck the Include Tests option
Choose a directory to save the framework, and click Create.
Now, create a new SwiftUI view, and name it as SettingsRow.swift. This is a generic row with a name and image, with a disclosure indicator. Make sure to check the framework in the targets.
Copy the following code for SettingsRow
inside the file:
public struct SettingsRow: View { private var title: String private var image: String private var showDisclosure: Bool /// A generic settings row which can be customised according to your needs. /// - Parameters: /// - title: The title of the row. /// - image: The SF symbol for the row. /// - showDisclosure: Show disclosure icon for action or navigation. public init(_ title: String, image: String, showDisclosure: Bool = false) { self.image = image self.title = title self.showDisclosure = showDisclosure } public var body: some View { HStack(spacing: 8) { Image(systemName: image) .font(.headline) .frame(minWidth: 25, alignment: .leading) .accessibility(hidden: true) Text(title) Spacer() if showDisclosure { Image(systemName: "chevron.right") .accessibility(hidden: true) } } .padding(.vertical .foregroundColor(.accentColor) } }
This view can be used in places for showing the app version or copyright. In this case, we have the disclosure icon hidden by default. As we want to access the view outside the framework itself and use it in our own apps, we change the access level of the struct
as public
.
Another use case is an action to perform on a row. Create SettingsActionRow
file, and add the following:
public struct SettingsActionRow: View { private var image: String private var title: String private var action: () -> () /// A generic settings row which can be customised according to your needs. /// - Parameters: /// - title: The title of the row. /// - image: The SF symbol for the row. /// - action: The custom action that you want to perform on tapping the row. public init(_ title: String, image: String, action: @escaping () -> ()) { self.image = image self.title = title self.action = action } public var body: some View { Button(action: action) { SettingsRow(title, image: image, showDisclosure: true) } .buttonStyle(PlainButtonStyle()) } }
The client code provides it with an action; for example, reviewing the app on the store or opening the app’s social accounts.
To navigate to another view, we create another view called SettingsNavigationRow
:
public struct SettingsNavigationRow<Destination: View>: View { private var title: String private var image: String private var destination: Destination /// A generic settings row which can be customised according to your needs. /// - Parameters: /// - title: The title of the row. /// - image: The SF symbol for the row. /// - destination: The view to navigate to, after tapping the row. public init(_ title: String, image: String, destination: Destination) { self.image = image self.title = title self.destination = destination } public var body: some View { NavigationLink(destination: destination) { SettingsRow(title, image: image, showDisclosure: true) } .buttonStyle(PlainButtonStyle()) } }
After a few similar rows, we group them using a secondary background color, like in the iOS settings screen. Add the following modifier:
public extension View { func settingsBackground(cornerRadius: CGFloat = 16, innerPadding: CGFloat = 8, outerPadding: CGFloat = 16) -> some View { self .padding(.horizontal, 16) .padding(.vertical, innerPadding) .background(RoundedRectangle(cornerRadius: cornerRadius, style: .continuous) .fill(Color(.secondarySystemBackground))) .padding(outerPadding) } }
With this, we’ve created our first framework ready to be utilized in our apps!
Creating a new project
Open Xcode, select Create a new Xcode project, and select the App template under the iOS header.
Fill in the template options as follows, then click Next:
- Product Name: Stocktance
- Organization Name: Fill this in however you like
- Organization Identifier: The identifier you use for your apps
- Interface: SwiftUI
- Life Cycle: SwiftUI App
- Language: Swift
- Make sure you’ve unchecked the Use Core Data, Include Unit Tests, and UI Tests options
Choose a directory to save our project and click Create.
Now that we have our project ready, we import the framework into our app.
Importing the framework into the project
There are two ways to add the project to your app:
- Drag the framework into the project navigator, and then add the framework to the target
- Add the framework to the project, and then add the framework to the target
Both are of a similar type, so we’ll prefer the latter option. In the app, select the project from the project navigator, select the Stocktance target, and scroll to Frameworks, Libraries, and Embedded Content.
Click on the plus button, click Add Other… and select Add Files…
Navigate to the SettingsKit folder and select it. We’ve added the framework to the project. To add it to our target, click the plus button again, and you’ll find SettingsKit.framework on the top. Select it to add it to our target.
Now, we’ve successfully added the framework to our app! Time to use it!
Using the Framework in the Project
Create a new SwiftUI file called SettingsView
in Stocktance, and at the top of the file, import our framework:
import SettingsKit
Just like we import Apple’s SwiftUI framework to take advantage of all they have to offer, we import our framework to create the settings view.
Add the following to the SettingsView
:
struct SettingsView: View { let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String var body: some View { NavigationView { ScrollView { VStack { SettingsNavigationRow("Account", image: "person.crop.circle", destination: Text("Accounts Screen")) SettingsNavigationRow("Phone Numbers", image: "number.circle", destination: Text("Phone Screen")) SettingsNavigationRow("Notifications", image: "bell.circle", destination: Text("Notifications Screen")) } .settingsBackground() VStack { SettingsRow("App Version \(appVersion)", image: "doc.append") } .settingsBackground() } .navigationTitle("Settings") } } }
With few lines of code, thanks to the framework we created earlier, we created simple views for our settings screen. You can use this framework in any other app as well to maintain the consistency of your settings.
To add the SettingsView
in the app, copy the following in ContentView.swift:
struct Stock { var name: String var price: Double } extension Stock { static let testStocks = [Stock(name: "Banana", price: 125), Stock(name: "TapeBook", price: 320), Stock(name: "Ramalon", price: 3200)] } struct ContentView: View { var body: some View { NavigationView { List(Stock.testStocks, id: \.name, rowContent: WatchlistRow.init) .navigationTitle("Stocktance") .toolbar { NavigationLink(destination: SettingsView()) { Image(systemName: "gear") } } } .accentColor(.purple) } } struct WatchlistRow: View { var stock: Stock var body: some View { HStack { Text(stock.name) Spacer() Text("$" + String(format: "%.2f", stock.price)) .foregroundColor(.white) .padding(8) .background(RoundedRectangle(cornerRadius: 8).fill(Color(.systemGreen))) } .padding(.vertical) } }
Run the app to see your framework code in action!
Conclusion
As your app scales, it is an excellent plan to break the code into individual components and reusable chunks into frameworks. For example, you can have the networking layer as a framework, isolated from the main app. Or an AnalyticsKit for handling the analytics. If the provider changes, you only have to make changes in the framework, as the primary implementation is separated from the app.
For sharing your framework as an open-source library or sharing it internally with the team, you can use Swift Package Manager to manage the code’s distribution.
Get set up with LogRocket's modern error tracking in minutes:
- Visit https://logrocket.com/signup/ to get an app ID
- Install LogRocket via npm or script tag.
LogRocket.init()
must be called client-side, not server-side - (Optional) Install plugins for deeper integrations with your stack:
- Redux middleware
- NgRx middleware
- Vuex plugin
$ npm i --save logrocket
// Code:
import LogRocket from 'logrocket';
LogRocket.init('app/id');
Add to your HTML:
<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>
Thank you for sharing!