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

Loose coupling and the law of Demeter

When you're designing a new component for your codebase, you will usually only think of the component itself, and the objects that it interacts with directly. If you're designing a component that authenticates a user, you will typically only consider objects directly related to the authentication flow. You'll take into account that there's probably a network call, and maybe a central current user storage object. You don't want to spend time thinking about objects that are related to that network object since that's not something the component you're designing should care about. For example, if the network object caches responses from the server, or whether it uses some sort of configuration object. It's not relevant to the needs of the authentication flow. However, it's not uncommon to see code like the following in production:

struct Authenticator {
  let networkingObject: Networking

  func authenticate(using email: String, password: String, @escaping (Result<User, Error>) -> Void) {
    if networkingObject.configuration.requiresAuthentication {
      // authenticate
    }

    // proceed because we don't need to be authenticate
  }
}

Code like the preceding snippet is called tightly coupled; it relies heavily on another object's implementation. In today's article, I will explain why tightly coupled code is a problem for flexibility, readability, and maintainability. You will also learn about an interesting principle called the law of Demeter. Let's dive right in, shall we?

Understanding why tight coupling is problematic

In the introduction of this article I showed you a code snippet that contained the following line of code:

networkingObject.configuration.requiresAuthentication

At first glance, you'll probably think there is nothing wrong with this code. The Authenticator has access to a Networking object. And we know that the networking object has a configuration, so we can access that configuration, and we can check whether the network's configuration says that authentication is required. Based on that line of code, we can draw a couple of conclusions:

  • The Authenticator depends on a configuration value to determine whether authentication is required.
  • The network's configuration can not be private because that would prevent Authenticator from reading it.
  • The network's configuration can not be removed or changed significantly because that would break the Authenticator.

The three conclusions that I listed are all implicit. If you're a developer that's tasked with making changes to the Networking object, and you've never worked on the Authenticator, it's unlikely that you're aware of the Authenticator and its needs. And even if you would take a quick look at the Authenticator, it's not likely that you'll realize that changing the Networking configuration will break your Authenticator. Even the smallest changes to the Networking object can now impact Authenticator which is undesirable.

We call this kind of situation "tight coupling". The Networking object depends heavily on the Authenticator's implementation details. You might even say that we have an implicit dependency on a configuration in Authenticator because, to function properly, the requiresAuthentication property of a configuration object is used. Both implicit dependencies and tight coupling are problematic because it makes refactoring and changing code really complicated. Changing details in one object might cause other objects to break in unexpected ways. This can be frustrating and it often results in a significant increase in development time for new features, bug fixes and it makes solving tech debt incredibly hard.

Not only is code like this harder to maintain, but it's also in many ways very complicated to design code like this. To write code that is tightly coupled to other code, you need to know everything about all code in your codebase. You can only stitch together a chain of parameters like networkingObject.configuration.requiresAuthentication when you have deep knowledge of the system and all components it. However, in my experience, this kind of code is not written because the author made a conscious decision to do so. This kind of code is often written when there hasn't been a lot of thought about the design of the code before writing it and the path to achieving the desired outcome is paved while you're walking it. What I mean by this is that a developer who writes code that's tightly coupled often tackles problems as they present themselves rather than trying to anticipate the way code will work upfront.

There are many ways to avoid writing tightly coupled code that has implicit dependencies. Let's take a look at one of these approaches now.

Decoupling code using the law of Demeter

The law of Demeter, also known as the principle of least knowledge presents a set of rules that should be followed in any codebase. The short explanation of this rule is that objects are only allowed to access properties, functions, and parameters that are directly available to them. If you keep that in mind, do you see how the following code violates this rule?

self.networkingObject.configuration.requiresAuthentication

I added self here to make it clear how deep we have to go to read the requiresAuthentication property. It's okay for self to access networkingObject since it's a property that's owned by self, so that's all good. Since the networkingObject exposes certain properties and methods that self should interact with, it's also okay to access configuration as long as we need to. The biggest problem is with accessing requiresAuthentication. By accessing requiresAuthentication, self knows too much about the structure of the code. One way to sum up the rules of the law of Demeter that I like is provided on its wikipedia page:

  • Each unit should have only limited knowledge about other units: only units "closely" related to the current unit.
  • Each unit should only talk to its friends; don't talk to strangers.
  • Only talk to your immediate friends.

This list makes the idea very clear. Everything you access should be as close to the place you're accessing it from as possible.

So what if we still want to read requiresAuthentication by going through the Networking object? You might argue that the Networking object should be the source of truth for whether authentication is required, which is fine. The important part is that we depend on as little implementation details as possible. So if the Networking object's configuration property holds the requiresAuthentication property, we could refactor the code in Networking to the following:

struct Networking {
  private let configuration: Configuration

  var requiresAuthentication: Bool { configuration.requiresAuthentication }

  // The rest of the implementation
}

By refactoring the code, as shown above, the Authenticator can now use networking.requiresAuthentication rather than networkingObject.configuration.requiresAuthentication, and that's much better. We're dependant on the configuration anymore, so we're free to make that private, and change it as needed. The source of the requiresAuthentication property is now hidden, so we no longer violate the law of Demeter.

The big win here is that by only accessing properties that are "one level deep", we can now refactor our code more freely, and we only need to know about objects that we need to interact with directly. All dependencies are clear and there are no surprises hidden in our code anymore.

The downside is that this approach might require you to wrap a lot of code in very short methods or computed properties as we did in the refactored code snippet I showed you earlier. When you stick to the law of Demeter, it's not uncommon to have methods that look like the following example:

struct UserManager {
  // Other code

  func getAuthenticationStatus(completion: @escaping (Result<AuthenticationStatus, Error>) -> Void) {
    authenticator.getAuthenticationStatus(completion)
  }
}

It might seem tedious to write code like this at first. After all, it's much simpler to just write manager.authenticator.getAuthenticationStatus { _ in } than to create a new method that only calls a different method. While this can be tedious at first, you'll find that once you need to make changes to how the Authenticator determines whether somebody is authenticated, it's very convenient to be able to limit the work you're doing to a single place.

In summary

Today's article was something different than you might be used to from me. Instead of learning about something cool that exists on iOS or in Swift, you learned how loose coupling works in practice. I explained that loose coupling allows you to refactor with confidence and that it allows you to write code that is as independent of other objects as possible. You learned and saw that tight coupling can lead to incredibly complex code that is very hard to refactored.

I then explained to you that there is a rule about how much objects should know about each other and that this rule is called the law of Demeter, or principle of least knowledge. You saw that applying this law in your code leads to easy public interfaces and that it enforces loose coupling, which means that your code is much more flexible and simpler to reason about than code that doesn't adhere to the law of Demeter.

The law of Demeter is one of the few principles that I try to apply in every project, regardless of size, complexity or purpose. If you're not sure how to apply it in your project, have questions or if you have feedback, I love to hear from you on Twitter.

Sequencing tasks with DispatchGroup

When you're building apps, there are times when you need to perform certain tasks before executing the next task. Imagine a scenario where you need to make a couple of API calls to a webserver to retrieve information before you can begin processing the information that's fetched by all preceding API calls, so it can be used in your app. Usually, you want to perform this work as efficiently as possible. In the example I just outlined, this might mean that you want to fire off your API calls to retrieve information all at once, and begin processing immediately when all calls have finished.

In today's article, I will show you how to do this using a GCD (Grand Central Dispatch) feature called Dispatch Groups. I will first explain what we're going to build, and then I will show you how to achieve complete the task using the DispatchGroup class.

Understanding the problem that a Dispatch Group solves

In this article, I will show you how to build an advanced system that collects information from a couple of resources before it stitches that information together so it can be used by another operation. Examine the following graph of operations:

Operation Graph

The diagram above is a simplified version of a flow that I once had to build. A lot of complex caching was involved in the original problem but for the purposes of this article I distilled it down to a handful of tasks. When a user logs in, certain information is retrieved. To limit the number of calls made to the movie webserver, we first collect all the required information about the user. The user profile contains a reference to the user's all-time favorite movie. Every ticket that belongs to the user also has a reference to the movie that the ticket is for. The user's favorites are, you guessed it, also references to movies. So once we have all the references to movies, we can bundle them together and get all movie data in one API call rather than three.

To achieve this without Dispatch Groups you could wrap each individual operation in an object, and when one task object finishes you can check the status of all task objects to see if all tasks are now finished. If they are, you can trigger the next operation. This might work for a while, but it takes a lot of manual bookkeeping and race conditions between operations might make it seem like not all operations have finished while in reality, they finished at pretty much the exact same moment.

Dispatch Groups solve exactly this problem. With a Dispatch Group, you remove the burden of wrapping and monitoring every task you want to execute. Instead, you register enter and leave events on the Dispatch Group. You also tell the Dispatch Group what to do when all enter invocations have had a matching leave invocation. The idea is simple enough, right? Create a group, register a bunch of enter events, leave when a task completes and the group will automatically do what you need it to do when all work is done. Amazingly, that is exactly how DispatchGroup works!

Note:
If you've done work with operations and operation queues where operations depend on each other, you might be wondering why I didn't mention them in this section. You might even prefer them for problems similar to the one I highlighted. Operation queues with operations and dependencies are indeed a fantastic way to solve problems like these. However, they are more complex than using Dispatch Queues directly and also require a bit more work to get going.

Let's see how you would implement the tasks outlined in the diagram from the beginning of this section, shall we?

Using a Dispatch Group

To use a Dispatch Group, the first thing you need to do is create one. So let's define a function that we'll expand until it's a complete step in our syncing process:

func collectMovieIds() {
  let group = DispatchGroup()
  var movieIds = Set<Int>()

  // we'll write the implementation of this function here
}

This function declares two local variables. One for the Dispatch Group, and one for the movie ids we collect along the way. When all movie ids are collected, we need to kick off the next operation. Let's update the function so it does that as soon as the Dispatch Group has executed all of its tasks:

func collectMovieIds() {
  let group = DispatchGroup()
  var movieIds = Set<Int>()

  // All code snippets after this one go in this area

  group.notify(queue: DispatchQueue.global()) {
    self.handleMovieIds(movieIds)
  }
}

To specify what the Dispatch Group does after all its tasks are completed, you call the notify(queue:work:) method on the Dispatch Group. The queue argument is an important one because it specifies where the closure that's passed as the work argument is executed. If you want to update the UI when all tasks are done, you could pass DispatchQueue.main. If you want to kick off the next API call as we do here, DispatchQueue.global() is probably more appropriate. If you to learn more about Dispatch Queues and when you need to use the main queue, you can read my post called "Appropriately using DispatchQueue.main".

For convenience, we're going to assume that a UserService object is available to our function and that it can perform all API calls we need. The following snippet would go in between the movieIds set and the call to notfify(group:work:):

group.enter()
userService.fetchProfile { profile in
  movieIds.insert(profile.allTimeFavorite)
  group.leave()
}

group.enter()
userService.fetchFavorites { favorites in
  for favorite in favorites {
    movieIds.insert(favorite.movie)
  }
  group.leave()
}

group.enter()
userService.fetchTickets { tickets in
  for ticket in tickets {
    movieIds.insert(ticket.movie)
  }
  group.leave()
}

Note how every operation enters the group right before it starts its task, and it leaves the group when each completion closure is called. The Dispatch Group will keep track of all calls to enter() and leave(), and it will call the closure that you passed to notify(queue:work:) once all calls to enter() have a corresponding leave() call.

You don't have to keep the group inside of a function as I did here. You're free to abstract each operation into a different object and pass the queue around. For example, you could refactor the function we wrote in this section to look a little bit like this:

func collectMovieIds() {
  let group = DispatchGroup()
  var movieIds = Set<Int>()

  userService.fetchProfile(group: group) { profile in
    movieIds.insert(profile.allTimeFavorite)
  }

  userService.fetchFavorites(group: group) { favorites in
    for favorite in favorites {
      movieIds.insert(favorite.movie)
    }
  }

  userService.fetchTickets(group: group) { tickets in
    for ticket in tickets {
      movieIds.insert(ticket.movie)
    }
  }

  group.notify(queue: DispatchQueue.global()) {
    print("Completed work: \(movieIds)")
    // Kick off the movies API calls
    PlaygroundPage.current.finishExecution()
  }
}

In this example, it's the job of the UserService to call enter() and leave() on the group when needed. As long as that's done correctly you can pass your group around as much as you want.

If you were expecting to see or write a lot more code, I'm sorry. There isn't anything more to DispatchGroup that you need to know in order to implement a flow similar to the one I showed you in the diagram from the previous section.

In summary

When you need to implement processes in your app that run asynchronously, and you need to wait on all of them to complete before doing the next thing, Dispatch Groups are a tool worth exploring. With its relatively simple API, DispatchGroup packs quite a punch. It provides powerful mechanisms to orchestrate and manage complicated processes in your app. When used wisely and correctly, it can really help you clean up your code.

The most important things to remember about using DispatchGroup in your code is that every enter() call needs to have a corresponding call to leave(), and you need to call notify(queue:work:) to tell the DispatchQueue what to do when all of its work is completed.

If you have any questions about Dispatch Groups, have feedback or anything else, don't hesitate to reach out on Twitter.

Breaking an app up into modules

As apps grow larger and larger, their complexity tends to increase too. And quite often, the problems you're solving become more specific and niche over time as well. If you're working on an app like this, it's likely that at some point, you will notice that there are parts of your app that you know on the back of your hand, and other parts you may have never seen before. Moreover, these parts might end up somehow talking to each other even though that seems to make no sense. As teams and apps grow, boundaries in a codebase begin to grow naturally while at the same time the boundaries that grow are not enforced properly. And in turn, this leads to a more complicated codebase that becomes harder to test, harder to refactor and harder to reason about.

In today's article, I will explain how you can break a project up into multiple modules that specialize on performing a related set of tasks. I will provide some guidance on how you can implement this in your team, and how you identify parts of your app that would work well as a module. While the idea of modularizing your codebase sounds great, there are also some caveats that I will touch upon. By the end of this article, you should be able to make an informed decision on whether you should break your codebase up into several modules, or if it's better to keep your codebase as-is.

Before I get started, keep in mind that I'm using the term module interchangeably with target, or framework. Throughout this article, I will use the term module unless I'm referring to a specific setting in Xcode or text on the screen.

Determining whether you should break up your codebase

While the idea of breaking your codebase up into multiple modules that you could, in theory, reuse across projects sounds very attractive, it's important that you make an educated decision. Blindly breaking your project up into modules will lead to a modularized codebase where every module needs to use every other module in order to work. If that's the case, you're probably better of not modularizing at all because the boundaries between modules are still unclear, and nothing works in isolation. So how do you decide whether you should break your project up?

Before you can answer that question, it's important to understand the consequences and benefits of breaking a project up into multiple modules. I have composed a list of things that I consider when I decide whether I should break something out into its own module:

  • Does this part of my code have any (implicit) dependencies on the rest of my codebase?
  • Is it likely that I will need this in a different app (for example a tvOS counterpart of the iOS app)?
  • Can somebody work on this completely separate from the app?
  • Does breaking this code out into a separate module make it more testable?
  • Am I running into any problems with my current setup?

There are many more considerations you might want to put into your decision, but I think if you answer "yes" to at least three out of the five bullet points above it might make sense to break part of your code out into a separate module. Especially if you're running into problems with your current setup. I firmly believe that you shouldn't attempt to fix what isn't broken. So breaking a project up into module for the sake of breaking it up is always a bad idea in my opinion. Any task that you perform without a goal or underlying problem is essentially doomed to fail or at least introduce problems that you didn't have before.

As with most things in programming the decision to break your project up is not one that's clear cut. There are always trade-offs and sometimes the correct path forward is more obvious than other times. For example, if you're building an app that will have a tvOS flavor and an iOS flavor, it's pretty clear that using a separate module for shared business logic is a good idea. You can share the business logic, models and networking client between both apps while the UI is completely different. If your app will only work on the iPhone, or even if it works on the iPhone and iPad, it's less clear that you should take this approach.

The same is true if your team works on many apps and you keep introducing the same boilerplate code over and over. If you find yourself doing this, try to package up the boilerplate code in a framework and include it as a separate module in every project. It will allow you to save lots of time and bug fixes are automatically available to all apps. Beware of app-specific code in your module though. Once you break something out into its own module, you should try to make sure that all code that's in the module works for all consumers of that module.

Identifying module candidates

Once you've decided that you have a problem, and modularizing your codebase can fix this problem, you need to identify the scope of your modules. There are several obvious candidates:

  • Data storage layers
  • Networking clients
  • Model definitions
  • Boilerplate code that's used across many projects
  • ViewModels or business logic that used on tvOS and iOS
  • UI components or animations that you want to use in multiple projects

This list is in no way exhaustive, but I hope it gives you an idea of what things might make sense as a specialized module. If you're starting a brand new project, I don't recommend to default to creating modules for the above components. Whether you're starting a new project or refactoring an existing one, you need to think carefully about whether you need something to be its own module or not. Successfully breaking code up into modules is not easy, and doing so prematurely makes the process even harder.

After identifying a good candidate for a module, you need to examine its code closely. In your apps, you will usually use the default access level of objects, properties, and methods. The default access level is internal, which means that anything in the same module (your app) can access them. When you break code out into its own module, it will have its own internal scope. This means that by default, your application code cannot access any code that's part of your module. When you want to expose something to your app, you must explicitly mark that thing as public. Examine the following code for a simple service object and try to figure out what parts should be public, private or internal:

protocol Networking {
  func execute(_ endpoint: Endpoint, completion: @escaping (Result<Data, Error>) -> Void)

  // other requirements
}

enum Endpoint {
  case event(Int)

  // other endpoints
}

struct EventService {
  let network: Networking

  func fetch(event id: Int, completion: @escaping (Result<Event, Error>) -> Void) {
    network.execute(.event(id)) { result in
      do {
        let data = try result.get()
        let event = try JSONDecoder().decode(Event.self, from: data)
        completion(.success(event))
      } catch {
        completion(.failure(error))
      }
    }
  }
}

There's a good chance that you immediately identified the network property on EventService as something that should be private. You're probably used to marking things as private because that's common practice in any codebase, regardless of whether it's split up or not. Deciding what's internal and public is probably less straightforward. I'll show you my solution first, and then I'll explain why I would design it like that.

// 1
internal protocol Networking {
  func execute(_ endpoint: Endpoint, completion: @escaping (Result<Data, Error>) -> Void)

  // other requirements
}

// 2
internal enum Endpoint {
  case event(Int)

  // other endpoints
}

// 3
public struct EventService {
  // 4
  private let network: Networking

  // 5
  public func fetch(event id: Int, completion: @escaping (Result<Event, Error>) -> Void) {
    network.execute(.event(id)) { result in
      do {
        let data = try result.get()
        let event = try JSONDecoder().decode(Event.self, from: data)
        completion(.success(event))
      } catch {
        completion(.failure(error))
      }
    }
  }
}

Note that I explicitly added the internal access level. I only did this for clarity in the example, it's the default access level so in your own codebase it's up to you whether you want to add the internal access level explicitly. Let's go over the comments one by one so I can explain my choices:

  1. Networking is marked internal because the user of the EventService doesn't have any business using the Networking object directly. Our purpose is to allow somebody to retrieve events, not to allow them to make any network call they want.
  2. Endpoint is marked internal for the same reason I marked Networking as internal.
  3. EventService is public because I want users of my module to be able to use this service to retrieve events.
  4. network is private, nobody has any business talking to the EventService's Networking object other than the service itself. Not even within the same module.
  5. fetch(event:completion:) is public because it's how users of my module should interact with the events service.

Identifying your module's public interface helps you to identify whether the code you're abstracting into a module can stand on its own, and it helps you decide whether the abstraction would make sense. A module where everything is public is typically not a great module. The purpose of a module is that it can perform a lot of work on its own and that it enforces a natural boundary between certain parts of your codebase.

Creating new modules in Xcode

Once you've decided that you want to pull a part of your app into its own module, it's time to make the change. In Xcode, go your project's settings and add a new target using the Add Target button:

Add Target Button

In the next window that appears, scroll all the way down and select the Framework option:

Select Add Framework

In the next step, give your module a name, choose the project that your module will belong to, and the application that will use your module:

Configure new module window

This will add a new target to the list of targets in your project settings, and you will now have a new folder in the project navigator. Xcode also adds your new module to the Frameworks, Libraries, and Embedded Content section of your app's project settings:

Framework added to app

Drag all files that you want to move from your application to your module into the new folder, and make sure to update the Target Membership for every file in the File Inspector tab on the right side of your Xcode window:

Target Membership settings

Once you have moved all files from your app to your new module, you can begin to apply the public, private and internal modifiers as needed. If your code is already loosely coupled, this should be a trivial exercise. If your code is a bit more tightly coupled, it might be harder to do this. When everything is good, you should be able to (eventually) run your app and everything should be good. Keep in mind that depending on the size of your codebase this task might be non-trivial and even a bit frustrating. If this process gets to frustrating you might want to take a step back and try to split your code up without modules for now. Try to make sure that objects are clean, and that objects exist in as much isolation as possible.

Maintaining modules in the long run

When you have split your app up into multiple modules, you are now maintaining several codebases. This means that you might often refactor your app, but some of your modules might remain untouched for a while. This is not a bad thing; if everything works, and you have no problems, you might not need to change a thing. Depending on your team size, you might even find that certain developers spend more time in certain modules than others, which might result in slightly different coding styles in different modules. Again, this is not a bad thing. What's important to keep in mind that you're probably growing to a point where it's unreasonable to expect that every developer in your team has equal knowledge about every module. What's important is that the public interface for each module is stable predictable and consistent.

Having multiple modules in your codebase introduces interesting possibilities for the future. In addition to maintaining modules, you might find yourself completely rewriting, refactoring or swapping out entire modules if they become obsolete or if your team has decided that an overhaul of a module is required. Of course, this is highly dependent on your team, projects, and modules but it's not unheard of to make big changes in modules over time. Personally, I think this is where separate modules make a large difference. When I make big changes in a module I can do this without disrupting other developers. A big update of a module might take weeks and that's okay. As long as the public API remains in-tact and functional, nobody will notice that you're making big changes.

If this sounds good to you, keep in mind that the smaller your team is, the more overhead you will have when maintaining your modules. Especially if every module starts maturing and living a live off its own, it becomes more and more like a full-blown project. And if you use one module in several apps, you will always have to ensure that your module remains compatible with those apps. Maintaining modules takes time, and you need to be able to put in that time to utilize modularized projects to their fullest.

Avoiding pitfalls when modularizing

Let's say you've decided that you have the bandwidth to create and maintain a couple of modules. And you've also decided that it absolutely makes sense for your app to be cut up into smaller components. What are things to watch for that I haven't already mentioned?

First, keep in mind that application launch times are impacted by the number of frameworks that need to be loaded. If your app uses dozens of modules, your launch time will be impacted negatively. This is true for external dependencies, but it's also true for code that you own. Moreover, if you have modules that depend on each other, it will take iOS even longer to resolve all dependencies that must be loaded to run your app. The lesson here is to not go overboard and create a module for every UI component or network service you have. Try to keep the number of modules you have low, and only add new ones when it's needed.

Second, make sure that your modules can exist in isolation. If you have five modules in your app and they all import each other in order to work, you haven't achieved much. Your goal should be to write your code so it's flexible and separate from the rest of your app and other modules. It's okay for a networking module to require a module that defines all of your models and some business logic, or maybe your networking module imports a caching module. But when your networking code has to import your UI library, that's a sign that you haven't separated concerns properly.

And most importantly, don't modularize prematurely or if your codebase isn't ready. If splitting your app into modules is a painful process where you're figuring out many things at once, it's a good idea to take a step back and restructure your code. Think about how you would modularize your code later, and try to structure your code like that. Not having the enforced boundary that modules provide can be a valuable tool when preparing your code to be turned into a framework.

In summary

In today's article, you have learned a lot about splitting code up into modules. Everything I wrote in this post is based on my own experiences and opinions, and what works for me might not work for you. Unfortunately, this is the kind of topic where there is no silver bullet. I hope I've been able to provide you some guidance to help you decide whether a modularized codebase is something that fits your team and project, and I hope that I have given you some good examples of when you should or should not split your code up.

You also saw how you can create a new framework in Xcode, and how you can add your application code to it. In addition to creating a framework I briefly explained how you can add your existing code to your framework and I told you that it's important to properly apply the public, private and internal access modifiers.

To wrap it up, I gave you an idea of what you need to keep in mind in regards to maintaining modules and some pitfalls you should try to avoid. If you have any questions left, if you have an experience to share or if you have feedback on this article, don't hesitate to reach out to me on Twitter.