Photo by Max Nelson on Unsplash
In this post I’ll cover a few frameworks that help with some common tasks throughout the development lifecycle. You can skip to a specific area or just follow along!
Before We Begin
Careful consideration should be taken when deciding whether or not to bring a 3rd party framework into your project. Here is a short list of questions you should ask yourself when evaluating a framework:
- How much will this add to my app size?
- How much support will I get if I run into issues?
- Does the framework do much more than I need?
- Can I implement this myself?
- And many more…
What I have found is that you should always let what is best for the business guide your technical decisions. It’s a win in my book if you make more sales sooner because you were able to ship faster because you used a 3rd party framework. You can revisit the matter when you have more time or resources.
I have found myself reaching for a small set of frameworks over and over again, especially since we are a resource-strapped team. We have to do a lot with very little so we try to leverage the good work of the community when possible and when it makes sense to.
Although some of these frameworks are not Swift specific I will only focus on Swift. And without further ado, here is my list.
Networking
Most apps today need to communicate with some kind of backend (rest, graphql). iOS has had the URLSession API since iOS 7 and depending on your use-case could be all you need. But sometimes you need additional functionality that isn’t included with the builtin APIs.
Alamofire
Alamofire is packed with features. It provides an elegant and intuitive API for making networking requests, both for downloading and uploading. It also provides handy debugging utilities. It is built on top of the URLSession API which means that new features Apple introduces are also available in Alamofire.
Here’s a snippet of what a request and response to a json API might look like:
AF.request(URL(string: "https://example.com/json/api")!)
.validate(200..<299)
.responseDecodable { response in
switch response.result {
case .success(let result):
print("My API result: \(result)")
case .failure(let error):
print("An error occurred: \(error)")
}
}
Using generics you can wrap this call in a helper method that automatically maps the response to your Codable
conforming type.
func send<T: Codable>(completion: @escaping (Result<T, Error>) -> Void) {
AF.request(URL(string: "https://example.com/json/api")!)
.validate(200..<299)
.responseDecodable { (response: DataResponse<T>) in
switch response.result {
case .success(let result):
completion(.success(result))
case .failure(let error):
completion(.failure(error))
}
}
}
You can read much more about this functionality in their docs. Be sure to check out their advanced topics section to learn about the Router
design pattern. It’s a nifty way to organize your networking code in a scaleable and testable manner.
Kingfisher
A common task in iOS development is to load images from a networked resource. Although all of the APIs are available to do this yourself, you’d end up writing quite a bit of boilerplate code. But then maybe you also want more advanced (but common) features, like caching or image processing.
This is where Kingfisher steps in and shines. A simple, intuitive, and powerful library for fetching, caching and processing images. It has builtin extensions to simplify working with UIImageView
and UIButton
.
From their docs:
The simplest use-case is setting an image to an image view with the UIImageView
extension:
let url = URL(string: "https://example.com/image.png")
imageView.kf.setImage(with: url)
Kingfisher will download the image from url, send it to both memory cache and disk cache, and display it in imageView. When you set with the same URL later, the image will be retrieved from cache and shown immediately.
You can customize the size of the memory and disk caches and set TTLs for both. But that is hardly scratching the surface of what Kingfisher can do.
UI
Apple has given developers valuable tools for developing powerful UIs like Interface Builder, AutoLayout, UITableView and UICollectionView. These are powerful building blocks for creating beautiful, interactive UIs and it is worth learning each of these tools, especially if you are just starting out*.
Here I present a couple libraries that enhance the builtin APIs or ease the use of them.
*On the horizon is SwiftUI
. Announced at WWDC 2019, SwiftUI
introduces a declarative approach to UI development. It’s a huge shift from the historically imperative nature of iOS development. Unfortunately, it depends on iOS 13 so unless you are only targeting newer devices you might still need to wait some time before adopting it.
IGListKit
IGListKit is a framework developed by Instagram and used by their production app. It’s a data-driven UICollectionView
framework for building fast and flexible lists.
IGListKit achieves its efficiency by using a diffing algorithm internally to only apply necessary changes. This means that you no longer call reloadData()
and instead let the framework handle updates for you. Moreover, things that can be tricky to get right, like removing rows, adding rows in bulk with animations, or reordering rows, become a lot easier.
If you’ve ever worked with UICollectionView
or UITableView
you know that there is quite a bit of boilerplate to deal with, wiring up your delegate, implementing your datasource, and handling cell sizing, etc.
This is greatly reduced in IGListKit. Instead of declaring delegates you instead conform to protocols. And even here the framework helps you out by raising compiler errors if you don’t implement a required method, reducing the chances of errors.
You can add the framework to an existing project and start adopting for new views, or convert old views as you work on them. It supports working with programmatic views or with storyboards and nibs.
SnapKit
SnapKit is a DSL to make Auto Layout easy on iOS and OS X.
Auto Layout is a powerful tool, but I think we have all experienced those red and yellow flags in Interface Builder showing that one of our constraint has an issue. Or worse, we get a seemingly endless log stream in the console at runtime.
Auto Layout can also be a bit verbose if you have a complex UI and can also be error prone if you forget to set translatesAutoresizingMaskIntoConstraints
to false.
Here is a very trivial example of a UIView
with a width of 42 points and a height of 42 points centered within its parent view.
class ViewController: UIViewController {
lazy var myView = UIView()
override func loadView() {
super.loadView()
myView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(myView)
self.box.backgroundColor = .green
NSLayoutConstraint.activate([ myView.widthAnchor.constraint(equalToConstant: 42), myView.heightAnchor.constraint(equalToConstant: 42), myView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), myView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor), ]) }
}
And here is the same example in SnapKit.
class ViewController: UIViewController {
lazy var myView = UIView()
override func loadView() {
super.loadView()
self.view.addSubview(myView)
myView.backgroundColor = .green
myView.snp.makeConstraints { (make) -> Void in make.width.height.equalTo(42) make.center.equalTo(self.view) } }
}
Even this trivial example one can begin to see the benefits of using SnapKit. No more forgetting to activate constraints. Use a chainable, intuitive API to build up complex constraints. And much more.
Debugging
Xcode is a powerful IDE and has many builtin functionality for debugging your app. Beyond that, Apple has provided a whole suite of tools for profiling and diagnosing performance issues in Instruments.
Sometimes though you want to inspect your app after it has gone to TestFlight and under certain scenarios, like a specific user being logged in, or with certain UI enabled or disabled. This is where Flex can come in handy.
FLEX
FLEX (Flipboard Explorer) is a set of in-app debugging and exploration tools for iOS development. When presented, FLEX shows a toolbar that lives in a window above your application. From this toolbar, you can view and modify nearly every piece of state in your running application.
With FLEX you can:
- Inspect and modify views in the hierarchy.
- See the properties and ivars on any object.
- Dynamically modify many properties and ivars.
- Dynamically call instance and class methods.
- Observe detailed network request history with timing, headers, and full responses.
- Add your own simulator keyboard shortcuts.
- View system log messages (e.g. from NSLog).
- And more…
This framework is feature packed and once integrated into your app easy to use. A great companion framework when you’re iterating quickly.
In Conclusion
I’ve listed a few frameworks that I like to use, some more than others depending on the use-case. As I stated before, you should weigh the pros and cons of introducing a framework into your project and conclude whether it makes sense to do so.
I prefer to implement as much code on my own as possible but sometimes the business dictates otherwise. The nice thing of the frameworks I have mentioned here is that they build on top of the existing ecosystem and don’t obviate it.