Five ways to improve code with type aliases

Swift grants developers the ability to shadow certain types with an alternative name using the typealias keyword. We can use this feature to create tuples and closures that look like types, or we can use them to provide alternate names to existing objects.

Today, I will show you five different applications of typealias that can dramatically improve your code when applied correctly. Let's dive right in, shall we?

1. Improving readability with type aliases

Perhaps this is the most obvious yet also somewhat underused way to use a typealias. When you have a type in your code that is very long or deeply nested, you could end up with rather long type declarations, the following code snippet is an example of a rather long type name:

UICollectionViewDiffableDataSource<CardSection.Diffable, Card.Diffable>

I have made the previous declaration nice and long on purpose, but it's not far from code that I have actually written. You can make the type declaration above much shorter with a simple typealias:

typealias CardDataSource = UICollectionViewDiffableDataSource<CardSection.Diffable, Card.Diffable>

After defining this in your code, the Swift compiler will now know that whenever you write CardDataSource, you really mean UICollectionViewDiffableDataSource<CardSection.Diffable, Card.Diffable> instead. This is really neat because instead of writing the same long, hard to read type declaration over and over, you can instead use the shorter, more descriptive type alias you created.

The same idea can be applied to functions that take a completion closure:

func fetchCards(_ completion: @escaping (Result<[CardSection], Error>) -> Void) {
  // implementation
}

While the code above isn't horrible, we can make it a bit more readable with a typealias:

typealias FetchCardsHandler = (Result<[CardSection], Error>) -> Void

func fetchCards(_ completion: @escaping FetchCardsHandler) {
  // implementation
}

Reading this alternative function that uses a typealias is easier on the eyes since there is much less information to unpack in the function declaration.

Before we move on to the second tip, please note that you shouldn't go and create type aliases for every long type or closure in your code. Type aliases improve readability but in some cases, they also hurt discoverability. In the case of the diffable data source typealias, this isn't much of a problem. In the closure case, this can be a bit more problematic. As with any technique, always consider your options carefully.

2. Communicate intention

A well-placed type alias can help developers determine what valid inputs for a certain method are, or what the inputs are used for. A good example is the TimeInterval alias that's used on iOS. Whenever you see that a certain method or property has the type TimeInterval you immediately get a sense of what kind of inputs you can use, and what will be done with them. Internally, however, a TimeInterval is nothing more than a typealias.

Another example of a type alias that communicates an intention to the user is a special ID type that's used inside of a struct:

typealias ID = Int

struct Card {
  let id: ID
  // more properties and code
}

struct CardSection {
  let id: ID
  // more properties and code
}

In the preceding snippet, you can see that the Card and CardSection objects both have an id property who's type is ID. While this doesn't look that impressive, it's pretty neat. We can now enforce that Card and CardSection use the same type as their ID by ensuring that they both use the same aliased type. In this case, both identifiers have to be integers. This can provide a lot of meaning to other developers reading this code and when used carefully can really make a big difference in your code.

3. Combining protocols

This is possibly one of the more interesting and obscure ways to use a typealias that I actually use regularly myself. I even wrote an article about it a while ago so I won't go in-depth too much. In Swift, you can use a typealias to create a placeholder for something that conforms to one or more protocols. For example, you can do the following:

typealias CommonDataSource = UICollectionViewDataSource & UITableViewDataSource

The result of the code above is that any object that claims to be a CommonDataSource must conform to both UICollectionViewDataSource and UITableViewDataSource. Since this example is cool but not very useful, let me give you another brief example:

public typealias Codable = Decodable & Encodable

Does the above look familiar? The idea of it probably does! If you declare a type to be Codable, it's both Encodable and Decodable. The way this is defined in Swift is through a typealias that looks exactly like the one in this code snippet.

4. Naming tuples

A language feature in Swift that's not used or needed a lot is the ability to define tuples. A tuple is a set of values that are grouped together. For example:

func divide(_ lhs: Int, _ rhs: Int) -> (result: Int, remainder: Int) {
  return (lhs/rhs, lhs % rhs)
}

The code above returns a pair of values as a result. Since this in itself doesn't always mean much, we can use a typealias to give a name to this tuple to make its intent clear:

typealias DivisionResultAndRemainder = (result: Int, remainder: Int)

func divide(_ lhs: Int, _ rhs: Int) -> DivisionResultAndRemainder {
  return (lhs/rhs, lhs % rhs)
}

Users of this code now understand what the returned tuple is, and how they can use it.

An interesting side effect of using tuples and type aliases like this is that you could even swap out the DivisionResultAndRemainder tuple for a struct with the same properties without making any changes to callers of divide(_:_:) which is pretty neat.

5. Defining a protocol's associated type

The fifth and last tip is a more advanced one that is used a lot in the Swift source code. Sometimes a protocol has an associated type:

protocol Sequence {
  associatedtype Element
  associatedtype Iterator: IteratorProtocol where Iterator.Element == Element

  // more code...
}

Note:
If you're not familiar with associated types or if you want to learn more, I recommend that you read the following posts on this topic:

The Swift compiler is usually pretty good at inferring what the concrete types of these associated types should be. However, this can be rather confusing, especially if the associated type is used in more than one place. The most common use of type aliases in the Swift standard library is (by far) the explicit definition of associated types:

extension ClosedRange: Sequence where Bound: Strideable, Bound.Stride: SignedInteger {
  public typealias Element = Bound
  public typealias Iterator = IndexingIterator<ClosedRange<Bound>>
}

The code above is directly from the Swift standard library. It shows how ClosedRange conforms to Sequence under certain conditions, and in the extension body, the Element and Iterator associated types from Sequence are defined. What's really interesting is that all Sequence functionality can now be assigned to ClosedRange through extensions on Sequence rather than explicitly defining them on ClosedRange.

This a great example of how protocol-oriented programming works in Swift, and it's made possible by typealias! Pretty cool, right?

In Summary

In today's quick tip you learned about five different use cases for the typealias keyword in Swift. Some of these use cases can significantly improve your code's readability while others have a more minor impact. You even learned that typealias is used to power large parts of the Swift standard library and that without typealias, protocol-oriented programming probably wouldn't look like it does today.

If you have more cool uses of typealias to improve your code, if you have questions or feedback, don't hesitate to send me a Tweet.

Reversing an Array in Swift

You can reverse an Array, and any other Collection in Swift using the reverse method. For example

var input = [1, 2, 3]
print(input) // [1, 2, 3]
input.reverse()
print(input) // [3, 2, 1]

The code above takes an array (input) and reverses it in-place using the reverse() method. This only works if your array is mutable.

If you want to reverse an immutable array that's defined as let, or if you don't want to alter the original input you can use reversed() instead of reverse():

var input = [1, 2, 3]
print(input) // [1, 2, 3]
var reversed = input.reversed()
print(input) // [1, 2, 3]
print(reversed) // ReversedCollection<Array<Int>>(_base: [1, 2, 3])

The reversed() method returns an instance of ReversedCollection. It can be used much like a regular Collection and you can convert it to a regular Array as follows:

let reversedArray = Array(reversed)
print(reversedArray) // [3, 2, 1]

It's typically advised to avoid making this final conversion because ReversedCollection doesn't compute the reversed array unless absolutely needed. Instead, it can iterate over the input array from the end to the start which is more efficient than reversing the entire array all at once. So only convert the result of reversed() to an Array explicitly if you run into problems otherwise which, in my experience, is almost never.

Year in review: 2019

It's the end of the year, and that means that we should all take a little bit of time to reflect on the past year. Some people like to reflect on their past year in a very statistical manner, they set measurable goals and they decide whether they met their goals at the end of the year. Personally, I don't really set goals per year. I simply strive to do the best I can every year. In this post, I would like to reflect on how 2019 has been for me, and I will share some of my plans for 2020 with you.

Recapping 2019

Right of the bat, 2019 was an interesting year for me. In January, I first attended dotSwift in Paris which was an amazing experience. I went to the city a day early with my girlfriend, Dorien, and we caught a concert by one of my favorite bands of all time called Architects. Paris is a beautiful city and since the conference only lasts half a day, we had plenty of time to walk around the city. After the conference, I met up with Dorien and we ran into several other people who we ended up having an amazing dinner with.

Shortly after attending dotSwift I went to Romania to speak about designing frameworks that work on multiple platforms at MobOS. The conference was amazing and I got to meet several amazing people, including Benedikt Terchete, Dave Verwer, Paul Hudson and others. When the conference was over, we went to a salt mine which was a beautiful experience and tons of fun!

A couple of weeks after delivering my framework design talk at AppDevCon in my (almost) hometown Amsterdam. At the pre-conference dinner, I met Kaya Thomas, which was really cool.

A couple of days after I spoke at AppDevCon, I got on my first ever intercontinental plane ride to Tokyo because I was invited to speak at try! Swift in Tokyo where I finally met my friend Jon-Tait after we'd been hanging out online for years! I also met a new friend at try! Swift, Nic Laughter. And of course, I met many other wonderful people too, like Natasha, Mayuko and Kristina. If you ever want to visit a conference that will blow your mind, I can recommend try! Swift Tokyo. The city is beautiful and the conference is put together so well. It's truly awesome.

After returning from Tokyo, I went to New York for the first time in my life for a couple of days in April. I went there to work for a couple of days, so, unfortunately, I didn't see much of the city. And I left my wallet on the airplane when I arrived so that wasn't great. Luckily, I came back to the United States a month later for WWDC so I got a retry in experiencing what the US is like, with the added benefit of it being at WWDC.

If you ever get a chance to visit San Jose during WWDC week, I recommend you do. I had a ticket for the conference this year which made the experience even better but I'm sure I would've had an amazing time without a ticket too. There are so many amazing people in the area during the week of WWDC, and the parties and gatherings of people are so much fun. The best part is that AltConf is right next door, so you can even attend a conference while you're there. Speaking of AltConf, I snuck out of WWDC for a moment to attend a talk my friend Nic gave called The Hidden Potential of People with ASD. I really enjoyed and you should definitely check it out if you want to learn more about autism in the workplace.

About a month after returning from WWDC I was lucky enough to attend Swift Island which, according to the Hacking with Swift community awards was the best conference of the year this year! So I was truly lucky indeed. I had a great time there and if you're ever looking for a conference that has a vacation-vibe going on, make sure to go to Swift Island.

After Swift Island, I was a guest on the iPhreaks podcast where we discussed Core Data. After that, I didn't do much until September. In September I started blogging again and I wrote a couple of posts about testing. Coincidentally, I had the opportunity to speak about testing at MobiConf in Krakow, Poland in October. Going to Poland was tons of fun, especially when I found out my friend Antoine was speaking too! At MobiConf I met several old friends, but I also made new friends and wanted to mention Aleksandra, Vadim and John in particular. Aleksandra and Vadmin, it was awesome to spend a couple of hours exploring Krakow with you before I went to the airport. And John, thanks for showing me and the gang around in Krakow's food and cocktail scene. It was a blast!

Days after I got home from Modbiconf I was invited to speak at GDG Coimbra in Coimbra, Portugal. This was another fun conference where even though I was pretty much the only iOS developer, I met awesome people. I wanted to give a special shoutout to Ivan, your talk was super funny and it was fun sitting with you at the speaker's dinner. It was also a ton of fun to meet Eugenio and I met up with Fernando who I had first met at MobiConf. Small world, right?

In December I didn't do any speaking. Instead, I worked on a project called Advent of Swift where I published one article on this blog every day. The project was a ton of fun and it brought me some very cool side-effects. For example, I was a guest on both Contravariance and Swift by Sundell. And I was chosen as the winner of the Rising Star category in the Hacking with Swift community awards. To wrap up the month of December I launched the Advent of Swift bundle which contains all Advent of Swift articles and several sample projects. It's available on Gumroad for just $1,99.

Wow. Summing my year up like this really confirms what I have been saying all along. It's been a wild year! Not just for the iOS community with SwiftUI, Combine, Xcode 11 and iOS 13, also for me personally! And a lot of this wouldn't be possible without the iOS community, all the amazing conference organizers and the amazing speakers that travel the world to talk at all the conferences.

In addition to all these people, it's also not possible without you specifically. Yes, you, the person reading this. If you wouldn't visit my website and read my articles, the last three months of this year wouldn't have been as exciting as they were, and all I can do is thank you from the bottom of my heart.

Looking ahead: 2020

No year in review is complete without at least mentioning the year after. I already mentioned that I don't do goals for the new year. But I can tell you that I plan to speak at conferences again in 2020, and I have already submitted several papers to CFPs. I can also tell you that I'm going to continue to bring you content in 2020 and for the foreseeable future I'm going to keep going with my two posts a week schedule. I enjoy that schedule and I hope you do too. And of course, I'm already thinking about what kinds of cool extra projects I can do for my blog in 2020.

I hope to see you again in 2020, and I wish you all the best and a Happy New Year!

Getting ready to publish your app on the App Store

You've done all the work to build your app, your UI looks amazing, animations are smooth and you're ready to put your app in the hands of other people. Maybe you're even ready to start offering your app on the App Store! This is a huge achievement if you are currently at this stage in your development cycle, I would like to congratulate you. Being ready to ship your app is a huge accomplishment, especially if it's your first App. I still remember the excitement when I submitted my first app to the App Review team in App Store Connect (when it was still called iTunes Connect). And I was even more excited when the app got approved and I could tell my friends to search for it in the App Store, and they would find it and download it. That truly was a magical moment for me.

Today, I would like to share some tips that will help you get ready for a smooth launch of your app. I am by no means a marketing expert, so I won't give you advice on how you can promote or market your app. Instead, I will provide you with a list of things you'll need to do from the moment you decide you're ready to push your app up to App Store Connect and submit it to the App Store. By the end of this post, you will know how to create your app in App Store Connect, how to submit your app for beta testing and even how to submit your app for App Store review.

The list below is presented in an order that I find comfortable, but of course, you're free to follow the steps in any order that fits your workflow.

Review the guidelines

Ideally, you execute this step early in the development process of your app. If you're not familiar with Apple's App Store Review Guidelines, you can find them here. This document contains loads of information that you need to know to make sure your app is appropriate for the App Store. For example, the guidelines state that your app must contain functionality that makes it interesting or more than just a website packaged in an app. Unfortunately, reviewing the guidelines is not a guarantee that your app will be accepted in the App Store. The review process is conducted by humans that might interpret the guidelines slightly different than you did. Regardless of this fact, it's still a good idea to familiarize yourself with the guidelines because it prevents a lot of disappointment down the line.

Create your app in App Store Connect

Before you can submit your app to the store, you must register it on Apple's App Store Connect platform. App Store Connect is the central place where you manage your entire app's store presence. To register your app, log in and on your dashboard click the My Apps icon. Next, click the + icon in the top left corner of the screen to add a new app. Fill out the form that pops up:

Register App Pop Up

Your app's bundle identifier might not be present in the list of bundle ids that are presented in the drop-down menu for the Bundle id field. If this is the case, click the Certificates, Identifiers & Profiles link that is shown below the drop-down and fill out your app's information to register your bundle id in the developer portal:

Registering an app in the developer portal

Once you've registered your app, you are taken to your app's management portal. Here you can manage your app's App Store information, pricing and the builds that you're running in Testflight or submit a new build for review.

Note that you can localize the information that's shown under the Localizable Information header using the drop-down menu on the right side of the window. When you registered your app, version 1.0 was created for your app in the sidebar on the left. This is where you can add screenshots, videos and release notes for your app's version. This is also where you select the build of your app that you want to submit to the App Store. But before you can do this, you need to archive your app in Xcode and upload it to App Store Connect.

Archive your App in Xcode and upload it to App Store Connect

Once your app is ready to be deployed to App Store Connect, you need to archive it. To do this, select the Generic iOS Device as the target to build your app to, and select the Product -> Archive option from the Xcode menu bar:

Archive app

Archiving your app might take a little while because Xcode will build your app using your Release configuration, and using settings that will allow your app to run on all iOS devices. When Xcode is done archiving your app, the Organizer window will open. From there you can select the archive that Xcode just created for you, and you can upload it to App Store Connect using the Distribute App option. Typically, you can keep al default options and click Next for every step. Once you've gone through all the steps, Xcode will upload your app to App Store Connect where your binary will be processed. This might take a while and you should receive an email from Apple when your binary has completed processing.

Deploy your app to TestFlight

Once your build has processed, you need to prepare it for Testflight. Fill out all the required information under the Test Information section on the Testflight page for your app. You also need to provide "compliance information". Click on the warning sign that should be visible on your processed build and provide the requested information. Once you've done this you can immediately send your Testflight app to your App Store Connect team members using the App Store Connect Users section in the sidebar.

To send your app to external testers, go to the Add External Testers page in the sidebar. You will first be asked to create a group for your testers. Provide a name and continue. Next, go to the Builds section on the external testers page and add the build that you just uploaded.

Uploaded app build in AppStoreConnect

Follow the steps in the pop up that appears after you've clicked the Add build to start testing button. Make sure to fill out the Test Information page with the key parts of your app that you want your testers to pay attention to. After doing this, click Submit for Review. This will send your app to Apple for a very brief beta-phase review. Getting approved for Testflight does not guarantee that your app will be approved for the App Store. In the meantime, you can begin adding testers using the Testers section. Once your build has been approved by Apple, a public link will be visible on the Testers page that you can share to allow people to obtain access to your Testflight app.

Create a video of your app's key features

The App Store allows you to include a video that promotes your app on your App Store page. While this is not required, it's certainly recommended. Your promo video should be short, to the point, and it should make people want to use your app. It's important to ensure that your video doesn't rely too much on Audio because not everybody that uses the App Store will be in a position to listen to your promo video's audio. Again, a promo video is not required but it's most certainly recommended.

Create screenshots for your app

While a promotional video is optional, screenshots are required. When supplying screenshots for iPhone, you need to upload screenshots for, at least, the 6.5" Display form factor and the 5.5" Display form factor. This boils down to the largest phones with, and without a home button. Smaller form factors will use the larger screenshots on the App Store page. Of course, you're free to upload screenshots for every display size individually if you want to. For iPad, you need to include screenshots for the 12.9" 2nd- and 3rd generation iPad pros. Again, this boils down to devices with and without a home button. The larger form factor screenshots will be used for the smaller displays on the iPad in the same way this is done for the iPhone.

It's possible to localize your screenshots similar to how you can localize your app's information on the App Information page.

When you need to take screenshots for several devices, using many different localizations, I can recommend that you look into using Fastlane. Fastlane includes a tool called Snapshot that allows you to automate the process of taking screenshots using UI Tests which is really convenient. I won't go into detail about setting this up since that topic should be an entire post of its own.

Prepare your app's metadata in App Store Connect

Once you're happy with the results of your Testflight beta, everything is prepared and uploaded and you're feeling confident to send your app to the review team you should give your app's metadata one last look. Make sure your app name is correct, add a privacy URL and a catchy subtitle and pick the primary and secondary category that your app should be listed in. Additionally, on the version-specific page, make sure that you include a nice promo test and an accurate description of your app. For your keywords, it's important to try and use as many relevant keywords as possible. Don't include your app name in the keywords because your app will already be indexed using your app name.

Also, make sure to select the correct build under the Build section, include a nice App Store icon (this should be your app icon exported at 1024x1024 pixels) and add any other relevant information.

Once everything is set, it's time to take the final step; Submitting to the App Store

Submit your app for review

When you submit your app for review, you can still change some of your app's metadata but keep in mind that a lot of the information about your app cannot change anymore. Also, make sure to check the appropriate way to release your app.

Selecting a release method in AppStoreConnect

If everything is in a good place, and your happy with your app, your App Store description and other metadata and you've selected an appropriate way for your app to be rolled out to users once it's approved, click the big Submit for Review button on your app's version page.

Rejoice!

After submitting your app, the waiting game begins. Apple will typically review your app within a couple of days but it might also take a few hours or a week. The key is patience. If your app has been in review for over a week, it might help to re-submit your app. But often, waiting a bit longer is the better strategy. If Apple rejects your app, the rejection notice will typically include a little bit of information about the reason. Often they will include the App Store guideline that they consider you to break, and you should correct this. If you don't agree with the verdict, you can always appeal or request a call with Apple. Keep in mind that Apple is not your enemy here, they will happily work with developers to help them make the required adjustments and get you in the store. Their main priority is to make sure everything that's published in the Store is up to their standards. Unfortunately, this is a process performed by humans and some reviewers appear to be more strict with certain guidelines than others. There's nothing you can do about that other than trying to work with your reviewer to resolve any issues.

Once your app is approved, it's time to celebrate! Tell all your friends and most importantly, send me a Tweet! I want to see your finished projects in all their glory.

Dependency injection with Storyboards and Xcode 11

For years I have had a bit of a love and hate relationship with Storyboards. I love how easy they make it for me to set up my AutoLayout constraints, but they can quickly grow unwieldy and for large projects with multiple developers Storyboards are hard to use because of merge conflicts that occur when multiple developers update the UI. For personal projects, however, my Storyboards tend to be small enough to manage. And since I'm the only developer working on them I never have merge conflicts. Yet still, I've never been completely happy with them.

The main reason for that is that Storyboards used to make it impossible to use dependency injection. So what you end up with is a view controller with tons of optional properties that you have to set in prepare(for:sender:) when a segue is about to be performed. That all changed with Xcode 11 and iOS 13.

In today's article, I will show you how you can use the new @IBSegueAction attribute and the Instantion segue action to add proper dependency injection to your Storyboard based view controllers.

Understanding the problem that @IBSegueAction solves

The following code should look familiar to anybody who's used storyboards before and had to pass data from one view controller to the next:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  switch segue.identifier {
  case "ShowDetailPage":
    guard let indexPath = collectionView.indexPathsForSelectedItems?.first,
      let detailViewController = segue.destination as? DetailViewController else {
      break
    }

    detailViewController.item = viewModel.item(for: indexPath)
  default:
    break
  }
}

The problem with this code is that it's fiddly and the DetailViewController needs to have multiple optional properties while they really shouldn't be. In the example above, the DetailViewController needs an item to display. If no item exists, it makes no sense to display the view controller. In fact, we really want to enforce this on a compiler level; we shouldn't be able to create an instance of DetailViewController if we don't have a selected item.

Unfortunately, the only way to enforce something like this at the compiler level has always been to ditch storyboards and to manually instantiate your view controllers. This is far from ideal and is the main reason that I've been avoiding Storyboards in my projects. Luckily, Xcode 11 and iOS 13 introduced a new feature that resolves this problem. It's called @IBSegueAction.

Using @IBSegueAction in an app

When you use @IBSegueAction, your Storyboard can defer the creation of a destination view controller to you entirely. This means that you are able to create an instance of the destination view controller and inject any required dependencies directly. In other words, you regain full control over how an object is initialized and what dependencies it has. Let's look at an example:

@IBSegueAction
func createDetailViewController(coder: NSCoder, sender: Any?, segueIdentifier: String?) -> DetailViewController? {
  guard let indexPath = collectionView.indexPathsForSelectedItems?.first else {
    return nil
  }

  return DetailViewController(coder: coder, item: viewModel.item(for: indexPath))
}

Every segue action you define can be named however you please. I chose createDetailViewController for my action name, but you can pick any name you want. It also must accept three parameters: coder: NSCoder, sender: Any? and segueIdentifier: String?. The return type of the action is always an Optional<UIViewController> or UIViewController?. You should substitute UIViewController with your own view controller subclass.

In the body of your action @IBSegueAction method, you create the view controller that should be used as the destination of the segue that the action belongs to. Since Storyboards use the required initializer init?(coder: NSCoder), the coder must be passed along to the target view controller's initializer.

Let's have a look at DetailViewController now to see what it's initializer looks like:

class DetailViewController: UIViewController {
  let item: DetailItem

  required init?(coder: NSCoder, item: DetailItem) {
    self.item = item
    super.init(coder: coder)
  }

  required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
}

The definition for DetailViewController now has an initializer that accepts the DetailItem. And because we can assign the item property during initialization, it doesn't have to be optional. And because we don't want anybody to initialize the DetailViewController without a DetailItem, the default initializer contains a fatalError. This will enforce that we can only initialize this view controller with the init?(coder:item:) initializer.

Since @IBSegueAction works slightly different for regular segues than it does for relationship segues I will explain each separately in the next subsections.

Using @IBSegueAction with a regular segue

When you're using a regular segue in your Storyboard, for example when you navigate from a table view to a detail page, you can create your Segue like you normally would by ctrl+dragging from your table view cell to the destination view controller. After doing this, click the segue and give it a Segue Identifier. So far, nothing special. If you use Storyboards regularly you have done this many times before.

Next, open the Connections Inspector and behold. There's a new option:

new Segue option

The Connections Inspector now contains an Instantiation connection for segues. Drag from the circle to the view controller that implements the @IBSegueAction for the target view controller that your segue points to. When you use it to navigate to a detail view controller, you will typically want your list view controller to be the target. When you release your drag action on the correct view controller, you will see the option to pick an appropriate @IBSegueAction method. You can use the same method for multiple segues, and you can have multiple @IBSegueAction methods for one view controller.

After connecting the Instantiation action, there's nothing else for you to do. If you run your app, your segue action will now be called whenever the segue it's attached to is called.

Using @IBSegueAction with a relationship segue

While regular master -> detail segues work pretty reliably, there seems to be something strange going on for relationship segues. A relationship segue is a connection between a navigation controller and its root view controller. Or the connection between a tab bar controller and its view controllers.

When you execute a segue where you present a navigation controller with a root controller modally, it's possible to use an @IBSegueAction to configure the navigation controller's root controller. The way this works is a little bit counter-intuitive though. Imagine an app where you have the ability to add items to a list:

Screenshot with Add button

If you tap the add button in the app from the screenshot above, a modal view controller is presented. The view controller is contained in a navigation controller so we can add a Save and Cancel button in the view controller's navigation item.

When you want to use an @IBSegueAction for the view controller, you should add the action to the list view controller. When you connect the Instantiation action, Interface Builder will automatically move the connection from your list view controller to the navigation controller. Don't be fooled however, at runtime, the system will look for the @IBSegueAction on the list view controller. This is even the case if you add the @IBSegueAction to a navigation controller subclass. No matter what you do, the system will look for the segue action on the source of the presentation. This has definitely cost me an hour or two to figure out so keep this in mind.

In Summary

Today, you've learned that with Xcode 11 and iOS 13 you can finally implement proper dependency injection when using Storyboards. You saw what the problem is with the old state of affairs, and how the new @IBSegueAction makes everything better. You also saw how to set up segue actions in Interface Builder connecting the Instantiation action to your @IBSegueAction methods. And to wrap it up, you learned about a strange little quirk that you might encounter with modal presentations and relationship segues.

I'm certainly going to give storyboards another chance in the coming months because I think this new feature looks very promising. What are your thoughts? Does this convince you to give Storyboards another try? Let me know on Twitter.

Using compositional collection view layouts in iOS 13

Ever since iOS 6, developers have been able to use collection views and to build interesting custom layouts by subclassing the UICollectionViewLayout or UICollectionViewFlowLayout classes. I even wrote an article about building custom collection view layouts a while ago. Today, I would like to introduce you to a new way of defining collection view layouts called compositional layouts. We're going to build the two layouts shown in the following image:

Designs for the collections

I will first show you how to build a simple grid layout, and from there we'll continue working our way towards the end result.

Building a grid layout

A very common use of collection views is to display a grid layout. It's so common that Apple has included a grid layout called UICollectionViewFlowLayout with iOS since they introduced UICollectionView in iOS 6. Since it's such a familiar and relatively simple layout, I want to explore the basics of UICollectionViewCompositionalLayout with you by using it to build a grid. I'm going to assume that you have already got a collection view setup, possibly with a diffable data source. If you want to follow along with the code samples in this post, I would recommend that you always put your compositional layout code in a method, as I will soon demonstrate. You can then assign the layout to your collection view by writing the following in your viewDidLoad() method:

collectionView.collectionViewLayout = createCompositionalLayout()

Before I explain the anatomy of a compositional layout in detail, let's dive right in with an example:

func createCompositionalLayout() -> UICollectionViewCompositionalLayout {
  let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5),
                                       heightDimension: .fractionalHeight(1.0))
  let item = NSCollectionLayoutItem(layoutSize: itemSize)

  let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                        heightDimension: .fractionalWidth(0.5))
  let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])

  let section = NSCollectionLayoutSection(group: group)

  let layout = UICollectionViewCompositionalLayout(section: section)
  return layout
}

If you examine the preceding code upward from the return statement, you will notice that the UICollectionViewCompositionalLayout initializer takes an instance of NSCollectionLayoutSection. In other words, a layout is built using sections. These sections are built using NSCollectionLayoutGroup instances. A single section can contain multiple groups, and as we'll see in the final section of this article, a section can also contain several different groups. Groups are considered to be the biggest workhorse of a compositional layout. You will typically do most configuration work on groups.

Notice that the example code uses NSCollectionLayoutGroup.horizontal to create the layout group. By making the layout group horizontal, all items that are added to the group will be positioned horizontally. This means that the group will position items on the horizontal axis until it has filled up its width, and then it starts positioning items on the next "line" until that contains as many items as can be fit in the group's width and so forth. We also pass the group one or more item configurations. In addition to items, a layout group also receives a size. In the example code, we use a fractional size. You might notice that I've used fractionalWidth for both the heightDimension and the widthDimension. The reason for this is that I want the grid to contain square items. To achieve this, I defined a group that takes the full width and is as tall as half of the screen width. If you now look at the declaration of the NSCollectionLayoutItem that is passed to the layout group, you'll see that an item has a fractional height of 1, and a fractional width of 0.5.

In other words, the layout group defined in the code above will fill the full width of its container and its height will be half the width of its container. The items inside of the group will all take up half the width of the container, and its full height, which makes the cells in the grid squares. The following image shows items, groups, and sections in an example grid layout.

Layout components

In addition to a fractional width and height, you can also assign an absolute size using .absolute(240) which would make an item exactly 240 points wide or tall, or you can use .estimated(240) to let the system decide the optimal height for an item based on its contents.

If you would assign the layout that's generated by the createCompositionalLayout() method I just showed you to a collection view's collectionViewLayout property, you would get a very similar looking grid as a result. The items are a bit cramped though, so let's explore some of the spacing options we have.

Adjusting the spacing between items, groups, and sections

There are two possible ways to give your collection view cells some room to breathe:

  • Assign edge insets on the items, groups or sections.
  • Assign a spacing between individual items and groups.

When you assign edge inserts to an item, its size doesn't change. Examine the following image:

Individual item with insets

The red box around the gray square is the item's bounding box. It takes up the space that's defined by the item's NSCollectionLayoutSize. The insets are applied inward from that bounding box, and the cell is eventually rendered in the remaining space; the gray area in the above image. You can assign edge insets on an item using the following code:

item.contentInsets = NSDirectionalEdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8)

Try adding this line of code to the grid layout you've defined earlier and notice how it gives a nice spacing between cells.

It's also possible to apply insets to groups. The insets that you apply to groups follow the exact same rules as insets that are applied to items. The following image illustrates this nicely:

Group with insets

The gray squares are the rendered cells, the red lines are the bounding boxes for the items, and the blue lines indicate the bounding box for the layout group. And like items, this blue bounding box will have the size that you specify for the group itself, and the insets are applied inwards.

Lastly, you can apply insets to sections. Sections follow the same rules as groups and items, and their insets stack on top of the insets defined on items and groups:

Section with insets

The only difference is that sections always take up the full width of the viewport, and their height is always determined dynamically.

In addition to insets, you can specify how spacing should be applied between items and groups. Spacing is always applied to the bounding box of one element to the next as shown in the following screenshot by the red spacing indicator. Items are shown on the left, groups are shown on the right.

Spacing example

You can apply item and group spacing using the following code:

// item spacing
group.interItemSpacing = .fixed(15)

// group spacing
section.interGroupSpacing = 15

Note that we define item spacing on the group that contains the items, and group spacing on the section that contains the groups. And also note that the item spacing is defined as .fixed(15) in this case. This means that groups will always position their items with 15 points between them. You can also use .flexible(<spacing>) to allow the group to determine the best spacing between items based on its size, and the size of the items that it's positioning. The group will use the value of its flexible spacing as the minimum spacing between items; your items might be positioned further apart from each other than you specified, but never closer to each other.

I highly recommend using the simple grid layout I shared at the start of this section to explore the inset- and spacing options I've described in this section. A grid layout is simple and predictable which makes it a perfect candidate for exploration of options.

Adding section headers to a layout

At the beginning of this article, I told you that I would show you how to build two different layouts. One of these two layouts contains section headers. So let's see how you can add section headers to your compositional collection view layout. If you're following along with the code I'm presenting in this article, you can use the following code snippet in the context of your grid layout, or any other compositional layout you might already have:

let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44))
let headerElement = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: "header", alignment: .top)
section.boundarySupplementaryItems = [headerElement]

The first line of the preceding snippet should look familiar to you. The size of a collection view header is defined in exactly the same way as the size for items and groups. The header is defined as an instance of NSCollectionLayoutBoundarySupplementaryItem. Headers and footers are both different types of supplementary views, so they are defined in the exact same way. Depending on the value you pass to the alignment argument in the NSCollectionLayoutBoundarySupplementaryItem initializer, the supplementary view will act as a header, footer or just a decorative view. Also, note that there is an elementKind argument. This argument is a string identifier, similar to a cell reuse identifier that you use in your collection view data source when it's asked to provide a supplementary view.

Note:
Since this post is not about setting up a collection view data source, I'm not going to explain section headers in depth. In short, you need to implement the collectionView(_:viewForSupplementaryElementOfKind:at:) method from the UICollectionViewDataSource protocol. You also need to register a UICollectionViewCell subclass on your collection view using the collection view's register(_:forSupplementaryViewOfKind:withReuseIdentifier:) method. This method is very similar to registering a collection view cell in code, except you also supply the element kind string identifier.

Amazingly, the code above is all you need to add section headers to your layout. The following image shows our progress in implementing the layout from the beginning of this article:

Grid with a section header

Building a layout that scrolls on both axis

Now that we have a basic grid with section headers to work off, let's implement the first collection view layout by changing just a couple of lines of code.

The first change to make is to reconfigure the groups in the grid layout a little bit. We'll make items inside of groups take up the entire space of the group, and we'll update the group so it takes up roughly 90% of the available with, and the group's height will be relative to the available width to make the group size (and item size) a size that is based on an aspect ratio:

let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)

let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.9), heightDimension: .fractionalWidth(0.5))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])

If you compare the code above to the grid code we had before, you'll notice that you only changed a couple of numbers. If you run your app with these changes, you should now have a layout with a section header where each cell takes up almost all width, and they are all stacked vertically as shown in the following image.

Full width items with header

Let's make some more changes to make the items in a section appear next to each other so we can scroll through each section on the horizontal axis:

let section = NSCollectionLayoutSection(group: group)
section.orthogonalScrollingBehavior = .continuous
section.interGroupSpacing = 16
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)

The key line in the preceding snippet is section.orthogonalScrollingBehavior = .continuous. By assigning a value to the orthogonalScrollingBehavior property on a section, you flip everything on its head. The section will now place groups next to each other rather than on top of each other. And since every group in our layout contains a single item, we end up with a carousel that matches the design from the beginning of this post. Amazing, isn't it?

The orthogonal scrolling option that I chose in the preceding example is .continuous. This option will make each section scroll like a plain scroll view. The user drags their finger over the screen and the section scrolls. Other values for this option are .continuousGroupLeadingBoundary, .paging, .groupPaging, .groupPagingCentered and more. Some of these options implement a paging behavior, and others manipulate how a section responds when it's scrolled in more subtle ways. I can absolutely recommend that you take the examples from this article and play around with the different behaviors because they are tons of fun to mess around with.

That wraps up the first collection view layout I wanted to show you! We began with a simple grid, and by changing a couple of numbers, adding a header and setting a property on our sections, we now have a layout that can scroll horizontally and vertically. It's truly amazing how little code we needed, especially when compared with the code we would have to write without compositional layouts.

Creating an advanced grid

At the start of this article, I showed you a second grid layout. This layout is interesting because in order to build it we must use nested groups. When using a compositional layout, you can pas more than one item to an NSCollectionLayoutGroup's subitems array. When you do this, the group will use all items in order for the layout. So when you pass two items, the group will use the first item for the first cell it contains, the second item for the second cell and then the first item again for the third cell and so forth.

In our case, we want to create one regular item for the full-width cell, and then a nested group that contains two half-sized items for the two smaller cells that are positioned below the wide cell. The following code can be used to create the layout we're looking for:

func createCompositionalLayout() -> UICollectionViewCompositionalLayout {
  let inset: CGFloat = 8

  // Large item on top
  let topItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalWidth(9/16))
  let topItem = NSCollectionLayoutItem(layoutSize: topItemSize)
  topItem.contentInsets = NSDirectionalEdgeInsets(top: inset, leading: inset, bottom: inset, trailing: inset)

  // Bottom item
  let bottomItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), heightDimension: .fractionalHeight(1.0))
  let bottomItem = NSCollectionLayoutItem(layoutSize: bottomItemSize)
  bottomItem.contentInsets = NSDirectionalEdgeInsets(top: inset, leading: inset, bottom: inset, trailing: inset)

  // Group for bottom item, it repeats the bottom item twice
  let bottomGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalWidth(0.5))
  let bottomGroup = NSCollectionLayoutGroup.horizontal(layoutSize: bottomGroupSize, subitem: bottomItem, count: 2)

  // Combine the top item and bottom group
  let fullGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalWidth(9/16 + 0.5))
  let nestedGroup = NSCollectionLayoutGroup.vertical(layoutSize: fullGroupSize, subitems: [topItem, bottomGroup])

  let section = NSCollectionLayoutSection(group: nestedGroup)

  let layout = UICollectionViewCompositionalLayout(section: section)

  return layout
}

I added some comments to the code. All of this should look familiar to you, it's mostly just a different way of composing the elements you have learned about in this article. This familiarity is exactly what makes compositional layouts so powerful. It doesn't matter whether you want to build something simple like a grid, or something more complex like we just did, you always use the exact same components composed in a different way. Cool, right?

In summary

Today's article introduced the new UICollectionViewCompositionalLayout class. You learned that this class allows you to specify complicated collection view layouts using four simple building blocks; items, groups, sections and the layout itself. With these blocks you can build layouts that would be a nightmare to implement using the good old UICollectionViewFlowLayout and the code to do this is simple and concise.

You saw how to build a collection view layout that positions its sections vertically, and the items within the section can be scrolled through horizontally. You learned that this is called orthogonal scrolling, and it can be enabled with just a single line of code. You also saw how you can nest groups to build a somewhat irregular looking layout that's very interesting visually.

If you have any questions about this article, or if you have built something really cool that with a compositional layout make sure to let me know. I love to hear from you.

Modern table views with diffable data sources

At WWDC 2019 Apple announced a couple of really cool features for table views and collection views. One of these cool features comes in the form of UITableViewDiffableDataSource and its counterpart UICollectionViewDiffableDataSource. These new diffable data source classes allow us to define data sources for collection- and table views in terms of snapshots that represent the current state of the underlying models. The diffable data source will then compare the new snapshot to the old snapshot and it will automatically apply any insertions, deletions, and reordering of its contents.

In today's article, I will show you how to use UITableViewDiffableDataSource to drive your table views. Since the table view data source is pretty much the same as the collection view version apart from some class names, I will focus only on the table view variant. The following topics are covered in this article:

  • Understanding how a diffable data source is defined.
  • Using a diffable data source in your apps.
  • Some best-practices to consider when using a diffable data source.

By the end of this article, you will know exactly how to use diffable data sources and what their caveats are.

Understanding how a diffable data source is defined

A diffable data source is an object that replaces your table view's current UITableViewDataSource object. This means that it will supply your table view with the number of sections and items it needs to render, and it supplies your table view with the cells it needs to display. To do all this the diffable data source requires a snapshot of your model data. This snapshot contains the sections and items that are used to render your page. Apple refers to these sections and items as identifiers. The reason for this is that these identifiers must hashable, and the diffable data source uses the hash values for all identifiers to determine what's changed. Let's look at this a little bit more in-depth by exploring the type signatures of both the data source and the snapshot.

First, let's explore the UITableViewDataSource signature:

class UITableViewDiffableDataSource<SectionIdentifierType, ItemIdentifierType> : NSObject 
  where SectionIdentifierType : Hashable, ItemIdentifierType : Hashable

That's quite the mouthful! The UITableViewDiffableDataSource class has two generic types, one for the section identifier and one for the item. Both are constrained so that whatever type fills the generic type must conform to Hashable. If you're not familiar with generics, check out this post I wrote as an introduction to generics in Swift.

It's interesting that Apple has decided to call the data source's generic parameters SectionIdentifierType and ItemIdentifierType. If your data model conforms to hashable, you can use it as the SectionIdentifierType or as the ItemIdentifierType. But, as the name of the generic suggests that might not be the greatest idea. I will explain why in the best practices section. For now, what matters is that you understand that both identifiers must conform to Hashable and that the data source will use hash values to determine changes in your data set.

Now let's look at the second key player in using a diffable data source; the snapshot:

struct NSDiffableDataSourceSnapshot<SectionIdentifierType, ItemIdentifierType> 
  where SectionIdentifierType : Hashable, ItemIdentifierType : Hashable

The snapshot object is a struct rather than a class, and it has the same generic parameters as the diffable data source it's applied to. This means that you can't apply a snapshot with a set of identifiers to a data source with different identifiers and your code will fail to compile.

Now that you know how a diffable data source is defined, and have a rough idea of how it works, let's get more practical and see how you can use a diffable data source in your apps.

Using a diffable data source in your apps

In this section, I will use a very simple data model where my section identifiers are integers, and my model contains only a title property. In reality, your models will be much complicated than what I'm using here. However, part of the beauty of diffable data sources is that this doesn't matter. Whether your model is simple or more complex, the principles all remain the same.

Setting up the basics

To create a new diffable data source, you create an instance of UITableViewDiffableDataSource as follows:

let datasource = UITableViewDiffableDataSource<Int, MyModel>(tableView: tableView) { tableView, indexPath, itemIdentifier in
  let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath)

  // configure cell

  return cell
}

In the preceding example, we use Int and MyModel as the identifiers for the sections and items respectively. We pass the table view that the diffable data source instance should be applied to the initializer of UITableViewDiffableDataSource and we also pass it a closure.

This closure is the cell provider. It's called whenever the table view needs a new cell to render on screen. In many ways, this closure is a replacement for the tableView(_:cellForRowAtIndexPath:) method you might be used to implementing currently. The main difference is that the item identifier that corresponds to the index path for the table view is passed along to this closure. So in the preceding example, you would receive an instance of MyModel as the third parameter for the cell provider closure. This means that if your identifier type contains all properties that are needed to render your cell, you don't have to fetch this object from your underlying data storage anymore.

Let's create a new snapshot that can be applied to our diffable data source:

var snapshot = NSDiffableDataSourceSnapshot<Int, MyModel>()
snapshot.appendSections(storage.sections)

for section in storage.sections {
  snapshot.appendItems(storage.modelsForSection(section), toSection: section)
}

datasource.apply(snapshot)

Note:
The preceding code uses a storage object. It's not a built-in type or anything but rather a placeholder that I made up to use in this example. I'm sure you can derive what this storage object's section property and the modelsForSection(_:) method look like. The main point is that you understand how the snapshot is configured.

The snapshot is created as a var because it's a struct and we wouldn't be able to modify it if we had declared it as a let. First, we call appendSections(_:) on the snapshot. This method takes all section identifiers you want to have present in your table view. Then, we loop through all sections and call appendItems(_:toSection:) on the snapshot. This will associate an array of item identifiers with the section identifier that's passed to this method.

Once all items and sections are added to snapshot, it is passed to the data source by calling apply.

Note that your data source will not update the table view unless you explicitly create a snapshot and call apply to update the table view. If you have already applied a snapshot and want to perform a simple add or remove operation of an item or section, you can get the data source's existing snapshot, modify it, and apply it as follows:

var currentSnapshot = datasource.snapshot()
currentSnapshot.deleteItems([itemToDelete])
datasource.apply(currentSnapshot)

The preceding code takes a snapshot of the data source, deletes an item from it and then applies the snapshot to the data source. NSDiffableDataSourceSnapshot has methods to delete items, sections and even to wipe the entire snapshot clean. If you want to update your data source, it's up to you to decide whether you want to create a new snapshot from scratch or to update the current snapshot.

With a setup like this, you can already provide a table view with data. Let's see how you can add support for section headers and other table view features you might have implemented in the past. For example, cell deletion.

Adding section headers and interactions

Out of the box, the diffable data source is pretty plain. But since it conforms to UITableViewDataSource it can do anything you might be already doing in your current UITableViewDataSource implementation. All you need to do is define your own UITableViewDiffableDataSource and override the methods of features you want to implement. For example, to add section headers you can override tableView(_:titleForHeaderInSection:):

class MyDataSource: UITableViewDiffableDataSource<Int, MyModel> {
  override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return "This is section: \(section)"
  }
}

When you subclass UITableViewDiffableDataSource, you immediately fill in the generic parameters for the section- and item identifiers. In this case, Int and MyModel. Note that the section argument in tableView(_:titleForHeaderInSection:) is not a SectionIdentifierType, instead it's always the integer index of your section. Keep this in mind when you construct your section title. If you would run your app and use this data source subclass instead of the regular UITableViewDiffableDataSource class, you will find that you now have support for section headers.

If you want to add support for deleting items you need to override tableView(_:canEditRowAt:) and tableView(_:commit:forRowAt:) in your data source subclass:

override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
  return true
}

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
  if editingStyle == .delete {
    let model = storage.modelForIndexPath(indexPath)

    var snapshot = self.snapshot()
    snapshot.deleteItems([model])
    apply(snapshot)

    storage.deleteModel(model)
  }
}

Notice how the preceding code finds the model that needs to be deleted in the underlying storage and then updates the existing data source on the snapshot. Finally, the model is also deleted from the underlying storage to ensure that the deletion is persisted.

Best practices for using diffable data sources

During my time exploring diffable data sources, I have run into a problem where I couldn't get cells to reload themselves. Luckily Steve Breen was kind enough to respond to my call for help and I learned a couple of things from him. But even then I was having trouble. Eventually Chris Hinkle came through with an observation. Based on what I've learned I have found two best practices that I think are important to keep in mind.

Keep your identifiers simple and to the point

When you choose your identifiers for your diffable data source and snapshot, try to make sure they only include data that will be rendered or influences the rendering. When I was stuck with reloading my cells, I was using NSManaged object subclasses to drive my data source. This seems to work well enough because everything was there. Items were added, removed and reordered. However, for some reason, my data source never seemed to pick up changes to the properties of my managed objects. I eventually got around this by providing a struct that contained the data I wanted to render rather than the entire managed object. A nice way I've found to define these structs is as extensions on the models themselves:

extension MyModel {
  struct Diffable {
    let id: UUID

    let title: String
    let subtitle: String
    // other properties that will be rendered on the cell

    init(model: MyModel) {
      self.id = model.id
      self.title = model.title
      self.subtitle = model.subtitle
    }
  }
}

When using this approach, you'd replace the MyModel item identifier with MyModel.Diffable, and when you create your snapshot, you must convert all MyModel instances to MyModel.Diffable. Since the initializer for the MyModel.Diffable object takes an instance of MyModel this is fairly straightforward:

var snapshot = NSDiffableDataSourceSnapshot<Int, MyModel>()
snapshot.appendSections(storage.sections)

for section in storage.sections {
  let items = storage.modelsForSection(section).map(MyModel.Diffable.init)
  snapshot.appendItems(items, toSection: section)
}

datasource.apply(snapshot)

By mapping over the array of MyModel objects using the MyModel.Diffable.init, every model object will be passed to the MyModel.Diffable initializer and is converted to an instance MyModel.Diffable. Pretty nifty, right?

Notice that there is one property in my Diffable extension that shouldn't be rendered; the id property. I'll explain why in the next subsection.

Ensure that your identifiers can be uniquely identified

If you don't include any way of uniquely identifying your identifiers, it's really hard for the diffable data source to find out what changed. For example, look at the following code:

let itemsBefore = [{ "name": "Donny Wals" }, { "name": "Donny Wals" }]
let itemsAfter = [{ "name": "Donny Wals" }, { "name": "Donny Wals" }]

The preceding snippet shows two arrays that appear to be identical. And while they might look identical, each object is its own entity. And they might have some kind of underlying ordering and the objects might have swapped positions. It's impossible to tell, right?

The same is true for your diffable data source. If you don't provide it anything unique to identify objects by, it can get confused easily which is probably not what you want. If you include some kind of known unique identifier like a UUID or an identifier that's used in your back-end, it's much easier to keep track of changes to ordering and individual changes. If you're reading data from a server that you won't modify and you know it already has a sense of uniqueness, for example if every item points to a unique resource on the web, it might be redundant to add an extra identifier yourself.

In summary

I hope I have been able to not only introduce you to diffable data sources, but I hope I have also been able to teach you how you can use them in the real world. Several blog posts I have seen were written shortly after WWDC 2019 and cover the very basics, and unfortunately Apple's documentation this year hasn't been fantastic. I'm sure they will get around to updating it eventually. Luckily, Apple did include some sample code in the links for their Advances in UI Data Sources talk from WWDC 2019.

In today's post, I have shown you how a diffable data source is defined, and how it's used. You saw that a diffable data source has two generic parameters; one for sections and one for items, and you saw that a diffable data source requires a snapshot of your models to render data. I have also explained how you can subclass UITableViewDiffableDataSource to add support for features like section headers and swipe-to-delete. Lastly, I shared some best practices that you should keep in mind when using diffable data sources.

If there's anything unclear after reading this post, or if you have feedback for me, make sure to reach out on Twitter. I love hearing from you.

Fetching and displaying data from the network

One of the topics that I could write dozens of posts on is networking. Making calls to a remote API to retrieve or persist data is something that is a key feature in many apps that are currently in the App Store. Some apps make extensive use of the network while others only need the network to retrieve data periodically. No matter how extensively your app uses the network, the patterns for using the network in a clean and appropriate manner are often (roughly) the same. I have written several posts on networking in the past:

While these posts are great for advanced or experienced developers, there's one question that none of them really answers. And that question is "How do I grab data from the network, and show it in a table- or collection view?". In today's article I will cover the following topics to answer that exact question:

  • Writing a data model based on JSON data
  • Updating a table view asynchronously with data from the network

In this article, I will not dive into the topic of making a URL call or setting up a table view.

Writing a data model based on JSON data

When you work with data that's loaded from the network, this data is often presented to you in a format called JSON. JSON stands for Javascript Object Notation and it's a standardized way of representing objects in an easy to parse, lightweight data format. A JSON object must always contain a single top-level object. This usually will be either an array or a dictionary, but it's also fine for the top-level object to be a string, boolean or number. Let's look at an example:

// array as top-level object
[
  {
    "key": "value"
  },
  { 
    "key": "value"
  }
]

// dictionary as top level object
{
  "status": 200,
  "items": [
    { 
      "key": "value"
    }
  ]
}

Arrays in JSON are written using the familiar angle brackets ([]) as their start and end characters. Dictionaries are represented using curly braces ({}) as their start and end characters. A JSON object will always use arrays and dictionaries to group and present information. Arrays can contain dictionaries, numbers, strings, and booleans. Dictionaries in JSON will always use strings as their keys, and their values can be other dictionaries, numbers, strings, and booleans. These constraints are important to keep in mind because they will help you to read JSON, and to build your Swift models.

When you're building a Swift model for JSON data, you will usually create a struct that conforms to Decodable. Conforming an object to Decodable means that you can use a JSONDecoder to transform the JSON arrays and dictionaries to Swift structs and classes. Whenever you encounter an array in your JSON data, keep in mind that this must always be converted to a Swift array. So for the example of JSON with an array as its top-level object you saw earlier, you'd write the following Swift code:

let jsonData = """
[
  {
    "key": "value"
  },
  {
    "key": "value"
  }
]
""".data(using: .utf8)!

struct MyObject: Decodable {
  let key: String
}

do {
  let decoder = JSONDecoder()
  let decodedObject = try decoder.decode([MyObject].self, from: jsonData)
} catch {
  print("Could not decode JSON data", error)
}

Notice that we ask the JSONDecoder to decode an array of MyObject. The reason is that the JSON we're decoding is also an array of objects, and we map this object to MyObject in Swift.

Let's also look at the code needed to decode the dictionary top-level object you saw earlier:

let jsonData = """
{
  "status": 200,
  "items": [
    {
      "key": "value"
    }
  ]
}
""".data(using: .utf8)!

struct DictObject: Decodable {
  let status: Int
  let items: [MyObject]
}

do {
  let decoder = JSONDecoder()
  let decodedObject = try decoder.decode(DictObject.self, from: jsonData)
} catch {
  print("Could not decode JSON data", error)
}

Notice that the item property of the preceding code uses the MyObject struct defined in the previous example. We can decode nested JSON dictionaries like this in our Decodable structs as long as the Swift objects that you use also conform to Decodable. The previous example works because MyObject and DictObject both conform to Decodable. If MyObject would not conform to Decodable, the preceding example would not compile.

If you stick to the rules I've outlined in this section, you know everything you need to know to convert basic JSON data structures to Swift objects. There is more to learn on this topic but I will leave that for a later post. The JSON we'll work with in today's article follows the patterns and rules outlines so far so we're ready to move on for now. But before we do, here's a brief summary of the rules to keep in mind when working with JSON:

  • A JSON response will usually have an array or a dictionary as its top-level object.
  • JSON arrays must always be decoded into Swift arrays.
  • JSON objects can only contain strings, booleans, numbers, dictionaries, and arrays.
  • JSON dictionaries always use strings for keys.
  • When converting a JSON dictionary to a Swift object, keys are used as property names.
  • You can nest Swift objects to decode complex JSON objects as long as all nested objects conform to Decodable.

Updating a table view asynchronously with data from the network

When you have successfully built a networking layer, abstracted everything behind protocols and your models are all fleshed out, it's time to make your network call. I will use a ViewModel to contain my networking object. As always, you are free to use any architecture or pattern you prefer. I just happen to enjoy using view models because they neatly separate my business logic from my view controllers. The principles shown in this section can be applied to any two objects that operate in a similar relationship as the view model and view controller in the following examples do.

At this point, you may have written code that looks roughly as follows:

struct FeedViewModel {
  let networkLayer: Networking

  private var feed: Feed?

  var numberOfSections: Int { feed.sections.count }

  func loadFeed() {
    networkLayer.loadFeed { (result: Result<Feed, Error>) -> Void in 
      self.feed = try? result.get()
    }
  }

  func numberOfItemsInSection(_ section: Int) -> Int {
    return feed.numberOfItemsInSection(section)
  }

  func itemAtIndexPath(_ indexPath: IndexPath) -> FeedItem {
    return feed.item(indexPath.row, inSection: indexPath.section)
  }
}

class FeedViewController: UIViewController, UITableViewDataSource {
  let viewModel: FeedViewModel
  let tableView: UITableView

  override func viewDidLoad() {
    super.viewDidLoad()

    viewModel.loadFeed()
    tableView.reloadData()
  }
}

I have seen this type of code written by many developers, and the question that follows is usually "Why does this not work?". And while you might look at this and think "heh, of course, it doesn't.", I don't think it's unreasonable to expect this to work. All the parts are there, everything is called, what's wrong!?

The above code doesn't work because it's asynchronous. This means that loadFeed() completes executing before the network has finished its work. And as soon as loadFeed() is done executing, tableView.reloadData() is invoked. So the order of things happening in the above code is as follows:

  1. viewModel.loadFeed() is called.
  2. Network call begins.
  3. tableView.reloadData() is called.
  4. ...
  5. Network call completes and viewModel.feed is assigned.

We need some way to reload the table view when the network call is finished. My preferred way of doing this is by passing a completion closure to the view model's loadfeed() method. The closure will be called by the view model when the feed property is updated, and the new data is available:

struct FeedViewModel {
  // ...

  func loadFeed(_ completion: @escaping () -> Void) {
    networkLayer.loadFeed { (result: Result<Feed, Error>) -> Void in 
      self.feed = try? result.get()
      completion()
    }
  }

  // ...
}

class FeedViewController: UIViewController, UITableViewDataSource {
  let viewModel: FeedViewModel
  let tableView: UITableView

  override func viewDidLoad() {
    super.viewDidLoad()

    viewModel.loadFeed { [weak self] in
      DispatchQueue.main.async {
        self?.tableView.reloadData()
      }
    }
  }
}

By refactoring the code as shown, we reload the table view after the network call completes, and after viewModel.feed is assigned. Resulting in the following sequence of events:

  1. viewModel.loadFeed() is called.
  2. Network call begins.
  3. ...
  4. Network call completes and viewModel.feed is assigned.
  5. tableView.reloadData() is called.

This is exactly what we want!

As with most things in programming, there are other ways to achieve the above. For example, we could have chosen to implement the delegate pattern or to give the FeedViewModel a property that we can assign an onViewModelUpdated closure to. While these both are not bad strategies, they introduce a certain complexity that's not needed in my opinion. For this reason, I will not cover them in this article but I did want to mention them so you can research these options on your own.

Note:
If you're not sure what DispatchQueue.main or [weak self] are, you can read my articles on Appropriately using DispatchQueue.main and When to use weak self and why

In summary

In today's article, I hope to have filled the gap between building a good networking layer and updating your UI. I haven't covered how exactly you can display your data. I'm sure that you already know how to build a table- or collection view. You did learn a lot about how JSON data is structured and how you can convert JSON data to your Decodable Swift models. I even gave you a couple of rules to keep in mind whenever you work with JSON data.

After showing you how to work with JSON in Swift, you saw how you can wait for data to be loaded from the network before reloading your table view in a safe and predictable manner. If you have any questions or feedback about the contents of this article, don't hesitate to reach out on Twitter!

Architecting a robust networking layer with protocols

Both networking and protocols are topics that I could write dozens of posts on, and I would still have more ideas and examples left in my head. In today's article, I would like to combine the topics of networking and protocols and explain how you can design a robust networking layer for your app, that's 100% testable and mockable because everything down to the URLRequest is abstracted behind a protocol.

Defining our goals

Any time you're getting ready to write code, you should define your goals first. What are you writing? What problems are you trying to solve? This is especially true when you're getting ready to design APIs for objects that you're going to use all over your code. A good example of this is a networking layer.

Chances are that whatever solution you come up with for your networking layer will be used by many different objects in your code, and if you're part of a team the number of developers that will use your API will probably be roughly equal to the number of developers on your team.

In this article, we're designing a networking API. The goal here is to abstract the networking layer in such a way that we can easily use, reuse and arguably most importantly, test all code we write. I always like working from the outside in when I design code. In other words, I think about how I want to use a new API I'm building rather than thinking about how I want to implement it.

Using this approach, I might sometimes start with a very bare design. For example, I might start by writing the following code:

class FeedViewModel {
  let service: FeedProviding
  var feed: Feed?
  var onFeedUpdate: () -> Void = {}

  init(service: FeedProviding) {
    self.service = service
  }

  func fetch() {
    service.getFeed { result in
      do {
        self.feed = try result.get()
        self.onFeedUpdate()
      } catch {
        // handle error
      }

    }
  }
}

I want you to focus on the service that's used here. It's a simple service that has a getFeed(_:) method and calls a completion closure with a result of type Result<Feed, Error>. At this point, our goal is clear enough. We need to design an easy to use service that can be injected into a view model so we can fetch a Feed object.

Implementing the networking layer

Since we're working our way from the outside in, we're going to start working at the service level and work our way down to the networking layer from there. Every time we come up with a new object, we're going to define it as a protocol first. You'll see that doing this allows you to write code that's highly testable, flexible and focussed. Let's write a protocol definition for the FeedProviding service from the previous section:

protocol FeedProviding {
  func getFeed(_ completion: @escaping (Result<Feed, Error>) -> Void)
}

Simple enough, right? It's just a protocol that exposes the getFeed(_:) method that we saw earlier. But of course, that's not enough. Let's write a simple implementation of getFeed(_:):

extension FeedProviding {
  func getFeed(_ completion: @escaping (Result<Feed, Error>) -> Void) {
    network.fetch(.feed, completion: completion)
  }
}

Even though the API looks simple, there's a lot to unpack here!

The getFeed(_:) method is not defined on an object that conforms to FeedProviding. Instead, it's implemented as an extension of FeedProviding itself. In Swift, it's possible to write extensions for protocols to give them default behaviors and functionality. Since we don't have any reason to assume that we'll need multiple feed providers (other than a mock one in unit tests) it's a fine idea to implement this method in a protocol extension. Any objects that want conform to FeedProviding can implement their own getFeed(_:) method that will be called instead of the one defined in the protocol extension.

The getFeed(_:) implementation that's written here uses a network object. And that object had a fetch(_:completion:) method that we pass an enum value, or a static property of something. We don't know what it will be at this point. All that's decided is that it's something that will inform the network of the endpoint it has to fetch. The completion closure that's passed to the getFeed(_:) method is passed on to the fetch(_:completion:) method directly. This implies that once the network call succeeds, the Data from the response is decoded into a Feed object automatically.

You might be wondering why we should bother with this method and protocol at all. We might just as well either skip the service object and use a networking object directly in the view model. Or we could just call service.network.fetch(_:completion:) from the view model. The reason we need a service object in between the network and the view model is that we want the view model to be data source agnostic. What this means is that the view model shouldn't care where it gets data from, if the service decides that it will cache network responses, it should be able to do so transparently; the view model shouldn't be aware of this. Calling out to the service's networking object directly from the view model is not a great idea for similar reasons. It would also violate the Law of Demeter.

Further reading available:

Loose coupling and the law of Demeter

Back to business, based on the few lines of code in the extension we added to FeedProviding we now have three new goals:

  • The networking object should accept some kind of endpoint or request configuration object.
  • The networking object's fetch(_:completion:) should decode data into an appropriate model.
  • Any object that implements FeedProviding requires a networking object.

To meet the first two requirements in one go, we can define the following protocol:

protocol Networking {
  func fetch<T: Decodable>(_ endpoint: Endpoint, completion: @escaping (Result<T, Error>) -> Void)
}

By making fetch(_:completion:) generic over a Decodable object T, we achieve an extremely high level of flexibility. The service layer can define what the Networking object will decode its data into because Swift will infer T based on the completion closure that is passed to fetch(_:completion:). The fetch method also accepts an endpoint parameter that's of type Endpoint. This could either be a struct or an enum, depending on your needs. In this case, I'm going to go with an enum because at this point I'm pretty sure I'm not going to need any instances of Endpoint, I'm only going to need the endpoint identifiers in the form of enum cases.

Further reading available:

To implement the third requirement from the list above, all we need to do is add a network property to the FeedProviding protocol. The following code snippet shows all code we need to satisfy the requirements I listed:

protocol FeedProviding {
  var network: Networking { get }

  func getFeed(_ completion: @escaping (Result<Feed, Error>) -> Void)
}

enum Endpoint {
  case feed
}

protocol Networking {
  func fetch<T: Decodable>(_ endpoint: Endpoint, completion: @escaping (Result<T, Error>) -> Void)
}

I omitted the extension of FeedProviding from the code snippet above because it hasn't changed since the last time you saw it. The protocols we've defined so far looks pretty good, let's take a look at a sample implementation of fetch(_:completion) so we can see if there's any more work to be done:

extension Networking {
  func fetch<T: Decodable>(_ endpoint: Endpoint, completion: @escaping (Result<T, Error>) -> Void) {
    let urlRequest = endpoint.urlRequest

    URLSession.shared.dataTask(with: urlRequest) { data, response, error in
      do {
        if let error = error {
          completion(.failure(error))
          return
        }

        guard let data = data else {
          preconditionFailure("No error was received but we also don't have data...")
        }

        let decodedObject = try JSONDecoder().decode(T.self, from: data)

        completion(.success(decodedObject))
      } catch {
        completion(.failure(error))
      }
    }.resume()
  }
}

The first thing to notice here is that we ask the endpoint object for a URLRequest object. This is a good idea because by doing that, the endpoint can configure the request. For example, the endpoint is now free to decide whether any data or query parameters should be added to request and whether it should be a POST request rather than a GET request.

While it's a good idea, it seems to be an awful lot of implicit responsibility for a simple enum. We'll refactor the code in a minute so that the URLRequest configuration is abstracted behind a protocol and we don't rely on the enum anymore in the networking layer.

Other than that I'm pretty happy with the state of fetch(_:completion:). Because we made the decoding generic, we're free to decode the response from our requests into any Decodable object, and once we hide the request configuration behind a RequestProviding protocol we're free to configure requests however we please. Let's make some final modifications to the code. First, we'll add the RequestProviding protocol:

protocol RequestProviding {
  var urlRequest: URLRequest { get }
}

extension Endpoint: RequestProviding {
  var urlRequest: URLRequest {
    switch self {
    case .feed:
      guard let url = URL(string: "https://mydomain.com/feed") else {
        preconditionFailure("Invalid URL used to create URL instance")
      }

      return URLRequest(url: url)
    }
  }
}

By conforming the Endpoint enum to RequestProviding we still have the ability to define endpoints in terms of an enum, but we're free to configure our requests and endpoints however we please. Let's also update the Networking protocol and extension:

protocol Networking {
  func execute<T: Decodable>(_ requestProvider: RequestProviding, completion: @escaping (Result<T, Error>) -> Void)
}

extension Networking {
  func execute<T: Decodable>(_ requestProvider: RequestProviding, completion: @escaping (Result<T, Error>) -> Void) {
    let urlRequest = requestProvider.urlRequest

    // no changes here
  }
}

Note that I have renamed the fetch(_:completion:) method to execute(_:completion:). The reason for this is that we don't know whether the network call that's made is going to be a GET or POST. And since fetch implies a GET request I wanted to make sure the naming isn't ambiguous.

All that's left now is to update the getFeed(_:) method's implementation that we added to FeedProviding earlier:

extension FeedProviding {
  func getFeed(_ completion: @escaping (Result<Feed, Error>) -> Void) {
    network.execute(Endpoint.feed, completion: completion)
  }
}

This method now calls execute instead of fetch and we need to refer to the Endpoint enum explicitly since the type of the first argument that execute(_:completion:) expects is now RequestProviding instead of Endpoint.

And that's it! With a relatively small amount of code, we were able to build a simple yet flexible and robust networking layer that can be extended, modified and updated as your project and networking needs grow in the future.

In summary

In this article, I showed you how you can take an idea or feature, and design a solid API for it with protocols. We started off by writing the code that's closed to the edge of the feature; the view model. And from there, we worked our way deeper into the rabbit hole all the way down to making the URLRequest to an actual server. Along the way, we made some changes to our code to improve the overall design of the networking layer.

I personally think working from the outside in is a great way to make sure all of your APIs are easy to use, and it kind of forces you to write the code in a way where form follows function, and not the other way around.

If you're interested in learning more about protocols, networking and designing APIs I have a list of posts that I have published before that you might enjoy:

Each of the posts above uses protocols and/or networking to build features that are highly testable and as flexible as possible. I definitely recommend that you read all four posts because they provide valuable insights into writing good code.

If you have any questions or feedback for me, don't hesitate to reach out on Twitter.

Installing multiple Xcode versions with xcversion

As a developer that uses Xcode on a daily basis for multiple projects, you sometimes need to use different versions of Xcode depending on the project you’re working on. Or maybe you want to try out the latest Xcode beta, for example right after Apple announced it after WWDC. One way to manage is to go to the Apple developer portal, searching for the version you need and download it. You download the .xip file, expand it (eventually, it takes a while) and then you can finally open Xcode. But then you realize you also have to rename it before dragging it to your Applications folder and when you open Xcode you have to install all the required tooling. Not the best experience.

Luckily there is an alternative available; xcode-install or xcversion. This tool is a command-line interface that you can use to install, uninstall and manage multiple Xcode versions. Let’s look at a step by step guide to installing this tool, and using it.

Installing the xcversion command-line tool and exploring the available Xcode versions

gem install xcode-install

This command installs the command-line tool. Once it’s installed you can fetch the currently available list of Xcode versions as follows:

xcversion list

# a bunch of old versions
10.3 (installed)
11
11.1
11.2
11.2.1 (installed)
11.3 beta

Installing a new Xcode version

Note that this tool also detects what Xcode versions you have installed already. Looks like there’s a new beta available, so let’s install that:

xcversion install 11.3

This will download Xcode 11.3 Beta from the developer portal, extract the .xip archive, copy a renamed version to our Application directory and even install Xcode’s tooling for this version. Really nice.

Activating an installed Xcode version

Since I like living on the edge, let’s make 11.3 Beta the currently active/preferred Xcode version on my machine:

xcversion select --symlink 11.3

This will run the xcode-select utility to make sure that all of my command-line tools now use the Xcode 11.3 toolchain. It also creates a symlink called Xcode in the Applications directory so whenever you open Xcode without any version behind it, it will automatically open the currently active Xcode version.

Note
For the symlink to work properly, make sure you don’t already have an existing Xcode installation called "Xcode".

⚠️ Warning
When using a dependency manager like Carthage it’s extremely important that you use the select command to activate the Xcode version you want to use when pulling down dependencies and developing your app. Having a mismatch between the Xcode version you’re developing in and the Xcode version that’s active on the command line can lead to broken builds and non-functional dependencies.

Uninstalling a no longer needed Xcode version

In the list output from before, xcversion showed that I still had Xcode 10.3 installed. I don’t need that anymore so let’s uninstall that:

xcversion uninstall 10.3

This command works exactly as you would expect.

In summary

Juggling several Xcode versions on your machine can be frustrating. Using a tool like xcversion makes it much easier to manage your Xcode installations, and gives you a lot of control over what Xcode version should be your default.

It also has the added benefit of not automatically updating your Xcode, just like the App Store does. I have personally lost too many hours looking for problems only to find out that the App Store had updated my Xcode to one that came with a new Swift version without my knowledge. Having full control over my Xcode installations feels liberating.

If you have any questions, feedback or anything else for me make sure to reach out on Twitter