Using preconditions, assertions, and fatal errors in Swift

As developers, we are often told that we should avoid crashing our apps at all costs. It's why we are told that we shouldn't force unwrap our optionals, that we should avoid unowned references and that we should never use try! in production code. In today's article, I would like to offer you a counter opinion on this never crash train of thought. I firmly believe that sometimes we should crash and that not crashing could sometimes be worse than crashing because it might leave your app in an unresponsive state, or maybe it hides a problem that you won't know about until your App is in the store and gets its first one-star reviews.

In this post, we'll spend some time on exploring the following topics:

  • Understanding how your code can fail
  • Crashing your app in production
  • Crashing your app during development

It's important to make the distinction between crashing during development and in production because you'll often want to be less forgiving during development than you'd be in production. I'm going to show you several of Swift's built-in assertion mechanisms that you can use to verify that your app is in an expected state and can continue to function.

Understanding how your code can fail

There are many things that can go wrong in code. And there are many ways for code to communicate errors and propagate them through your application. For example, a method might use a Result object to specify whether an operation succeeded, or if an error occurred. This is common in asynchronous code that uses callbacks.

When code executes synchronously or uses Swift Concurrency's async functions, it's common for methods to be marked with throws to indicate that the method will either complete successfully and return a value if relevant or that the function will throw an error if something goes wrong. Both Result and throw force developers to think about what should happen when something goes wrong. Throws does this by requiring a do / catch block and a try keyword in front of your call. Result does this by requiring you to extract the success or error from the Result object.

Both Result and throws are examples of errors that can and should be handled by the developer. It's also possible for a function to be written in a way that doesn't allow a developer to handle and recover from errors.

Consider this example:

let array = [String]()
print(array[1])

The above code will crash when you run it. The reason for this is that the subscript function on Array in Swift considers accessing an index that is not in the array to be a programmer error.

In other words, they consider it a bug in your code that you should fix. The example above crashes both in production and development. In the Swift source code, you can see that Apple uses _precondition() to crash your program because error is not recoverable and that the program should be considered in an invalid state.

A very similar way to crash your app when something is wrong is when you use fatalError(). While the result is the same (your app crashes), fatal errors should be used less often than precondition failures because they carry a slightly different meaning. A precondition failure is usually used in response to something a programmer is doing. For example, accessing an invalid index on an array. A fatal error is more unexpected. For example, imagine that the program knows that the index you're accessing on an array is valid, but the object has gone missing somehow. This would be unexpected and is a fatalError() because there it's unlikely that your program can still reliably when objects randomly go missing from memory. To be honest, I doubt you would even be able to detect something like that.

The last way of failing that I want to highlight is assertionFailure(). An assertion failure is very similar to a precondition failure. The main difference is that an assertion failure is not always evaluated. When your app is running in debug mode and you call assertionFailure() in your code, your app crashes. However, when you export your app for the app store, the assertion failures are stripped from your code and they won't crash your app. So assertion failures are a fantastic way for you to add loads of sanity checks to your codebase, and crash safely without impacting the user experience of your app in production.

All in all, we roughly have the following ways of communicating errors in Swift:

  • Swift's Result type.
  • Throwing errors.
  • Crashing with preconditionFailure().
  • Crashing with fatalError().
  • Crashing during development using assertionFailure().

I would like to spend the next two sections on using the last three failure modes on the preceding list. Note that this list might be incomplete, or that some nuances could be missing. I've simply listed the errors that I see most commonly in code from others, and in my own code.

Crashing your app in production

When your app is on the App Store, there are a couple of things you should care about:

  • Your app shouldn't crash.
  • Your app should be responsive.
  • You want to know about any problems your users run into.

It's not always easy to be able to hit all three targets. Especially if you avoid crashing at all costs, it might happen that instead of crashing your app shows a blank screen. The user will usually remedy this by force-closing your app and trying again, and if that works, it's a problem that you are not made aware of. So at that point, you miss two out of three marks; your app wasn't responsive, and you didn't know about the problem.

Sometimes, this is okay. Especially if it's unlikely to ever happen again. But if it does, you might not hear about it until the first bad reviews for your app start pouring in. One good way for you to be made aware of these kinds of problems is through logging. I won't go into any details for logging right now, but it's good practice to log any unexpected states you encounter to a server or other kind of remote log. By doing that, your app was unresponsive but you also have a log in your database.

This approach is great if you encounter a recoverable or rare error in production. It's not so great that your app may have been unresponsive, but at least you have captured the bad state and you can fix it in future releases. This is especially useful if the error or bug has a limited impact on your app's functionality.

There are also times when your app is more severely impacted and it makes little to no sense to continue operating your app in its current state. For example, if your app uses a login mechanism to prevent unauthorized access to certain parts of your app, it might make sense to use a preconditionFailure if your app is about to show an unauthenticated user something they aren't supposed to see. A problem like this would indicate that something went wrong during development, and a programming error was made. Similar to how accessing an out of bounds index on Array is considered a programming error. Let's look at a quick example of how preconditionFailure can be used:

override func viewDidLoad() {
  guard dataProvider.isUserLoggedIn == true else {
    preconditionFailure("This page should never be loaded for an unauthenticated user")
  }
}

Note that the return keyword can be omitted in this guard's else clause because there is no way for the program to continue after a preconditionFailure.

Or maybe your app is supposed to include a configuration file that is read when your app launches. If this file is missing and your app has no way to determine a default or fallback configuration, it makes perfect sense to crash your app with a fatalError. In that specific example, your app shouldn't even pass Apple's App Review because it doesn't work at all, and bundled files don't typically go missing from people's devices randomly. And when they do, it's probably because the app was tampered with and the user should expect things to crash. So when your app is live in the App Store, it's pretty safe to expect any files you bundled with the app to exist and be valid, and to crash if this is not the case.

The major difference between being unresponsive and allowing the user to manually kill your app and pro-actively crashing is the message it sends to the user. An unresponsive UI could mean anything, and while it's frustrating it's likely that the user will kill the app and relaunch it. They will expect whatever issue they ran into to be a rare occurrence and it probably won't happen again. While crashing is more serious. This tells your user that something is seriously wrong.

I can't tell you which of the two is the better approach. In my opinion, you need to find a balance between recovering from errors if possible, leaving your app in a potentially unexpected state and crashing. What's maybe most important here is that you make sure that you know about the issues users run into through logging unexpected states and crashing so you get crash reports.

Crashing your app during development

While you need to be careful about crashing in production, you can be a little bit more relentless during development. When your app is run in debug mode, fatal errors and preconditions work the same as they do in your App Store build. However, you have one more tool at your disposal during development; assertion failures.

Assertion failures are best used in places where your app might still work okay but is not in an expected state. These are the cases where you would want your production builds to send a log message to a server so you know that something was wrong. During development, however, you can call assertionFailure and crash instead. Doing this will make sure that you and your teammates see this error, and you can decide whether it's something you can fix before shipping your app.

The nice thing about assertion failure is that they are only used during development, so you can use them quite liberally in your code to make sure that your code doesn't do anything unexpected, and that no mistakes go unnoticed through silent failures. Let's look at the same example I showed earlier for the unauthenticated access of a part of your app, except this time we're less restrictive.

override func viewDidLoad() {
  guard dataProvider.isUserLoggedIn == true else {
    assertionFailure("This page should never be loaded for an unauthenticated user")
    dataLogger.log("Attempted to show view controller X to unauthenticated user.", level: .error)
    return
  }
}

In the above example, the view would still be shown to the user in production and an error message is logged to a data logger. We need to return from the guard's else clause here because an assertion failure does not guarantee that it will terminate your app.

In summary

There are many ways for developers to communicate errors in their apps. Some are very friendly, like for example a Result type with an error state, or using throws to throw errors. Others are more rigorous, like fatalError, preconditionFailure and assertionFailure. These errors crash your app, sending a very strong message to you and your users.

In this article, I have explained several reasons for crashing your apps, and I've shown you that different ways of crashing your app make sense in different circumstances. The example that I used for preconditionFailure and assertionFailure is one that might seem farfetched. When I wrote them down I realized that the obvious fix for the problem was to not show a restricted view controller in the first place if a user isn't logged in. If you thought the same you'd be right. But the problem we're solving here is a different one. By performing precondition and assertion checks in this hypothetical case, we make sure that any mistakes your teammates might make are caught. You don't always know whether your app attempts to present a restricted view controller to a non-authenticated user until something in your app starts yelling at you. And that's what assertions and preconditions are both really good at. So I hope that with that in mind, the example makes sense to you.

If you have any questions, or feedback for me, please reach out on X.

Testing your push notifications without a third party service

Many apps rely on push notifications to inform their users about interesting updates, important events, or interactions on social media that a user probably wants to know about. It's the perfect way to grab your users' attention and inform them about information they are likely interested in. To send push notifications, a lot of companies and developers make use of third-party services like Firebase, Amazon, Pusher, and others.

Today, I would like to show you a simple way to send push notifications without needing any of these services. Personally, I like to use this approach in the early stages of development because I don't want to have to go through a third party dashboard, integrate an SDK and just prefer to keep everything on my own machine.

It's also much quicker to set up quick prototypes or test cases if you don't need to involve an external service in your workflow. In today's article you will learn the following:

  • Preparing your app and certificates for push notifications
  • Writing a script to send notifications

Note that I mentioned services in this introduction, not libraries or frameworks. As you will see in the second section of this article, I use a small Javascript library to help me send push notifications because that actually saves me a lot of time.

Preparing your app and certificates for push notifications

Preparing for push notifications is a two-step process:

  1. Prepare and register the app
  2. Create the needed certificates

Preparing your app for push notifications

In order for your app to be able to receive push notifications, you need to add the Push Notifications entitlement to your project. To do this, go to the Signing & Capabilities tab in your project settings, click the + Capability button and add the Push Notifications capability to your app. Doing this will update the certificate that Xcode manages for you, and it allows you to create the needed certificates for your test script. It also allows you to register your app for push notifications.

To register your app for push notifications, you need to call the following method. Make sure to do this after you've asked the user for permission to send notifications.

UIApplication.shared.registerForRemoteNotifications()

Also, implement the following two AppDelegate methods:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
  let deviceTokenString = deviceToken.map { String(format: "%02x", $0) }.joined()
  print(deviceTokenString)
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
  print(error.localizedDescription)
}

When your app successfully registers for push notifications, the device token is printed to the console. Make sure to keep the token somewhere where you can easily access it so you can use it when we write the script that will send our test notifications.

Generating the required certificates

There are two ways that you can use to generate your push certificates; manually or using a tool called Fastlane. I will briefly outline both options here.

Generating certificates manually

To generate your push certificates manually, you need to log in to the Apple Developer Portal and go to the Certificates, Identifiers & Profiles section. Click the + button to create a new certificate and choose the iOS Apple Push Notification service SSL (Sandbox) certificate.

iOS Apple Push Notification service SSL (Sandbox) option

Click continue and find the App ID that you want to send push notifications to. If your app doesn't show up, make sure you have added the push notifications entitlement to your app in Xcode.

The next step is to create a certificate signing request. To do this, open the Keychain Access app on your Mac and choose Keychain Access -> Certificate Assistant -> Request a Certificate from a Certificate Authority from the menu. Enter an email address and name in the required fields, and leave the CA Email address empty. Also, make sure to check the Saved to disk option.

Certificate Sign request example

Click continue and store the certificate in a convenient place, for example on your desktop.

Go back to the developer portal where you should see a screen that asks you to Upload a Certificate Signing Request. Choose the request you just created using Keychain Access. After clicking continue you should be able to download your freshly generated certificate.

After downloading the certificate, double click it so it's added to your keychain. Once it's added, make sure to select the Certificates option in the list of keychain Categories:

Certificates option selected

Now find the certificate you just added, expand it using the arrow that should be visible next to it. Select both the certificate and the private key, right-click and choose the Export 2 items... option. Store the .p12 file that Keychain will export in a place where you can easily find it, for example, your desktop.

Open your terminal, navigate to the folder where you've stored your .p12 file and type the following command. Make sure to replace <your file> with the filename you chose in the previous step:

openssl pkcs12 -in <your filename>.p12 -out certs.pem -nodes -clcerts

This command will generate a .pem file which is needed to connect to Apple's push service. Move the generated file to the folder where you'll be writing your script. I like to keep the certs file and the script itself all in the same folder. Of course, you're free to do whatever you want if something else works better for you.

Generating certificates with Fastlane

By far the easier option and the one I prefer is to generate the needed .pem files with Fastlane. If you have Fastlane installed, use the following command to generate a .pem file for your app:

fastlane pem --development

You will be asked to log in to your Apple ID and to provide the bundle identifier for the app that you need a .pem file for. Once you've done this Fastlane will generate three files for you. Copy the .pem file to a place where you can easily reference it from the push notification script. Like I mentioned earlier, I like to keep them all in the same folder but you can store them wherever you want.

Writing a script to send push notifications

Before you get started writing your push script, make sure you have Node.js installed. Navigate to the folder where you'll create your push script and use the following command to install node-apn, the helper library we'll use to send notifications:

npm install --save node-apn

The preceding command will pull down the node-apn package from Node.js' version of SPM and install it in the current directory. Next, create a new javascript file. Call it what you want but I will call it send_push.js. Sending push notifications with node-apn is fairly simple. First, import the package and create the push service:

const apn = require("apn");

let provider = new apn.Provider({
  "cert": "certs.pem",
  "key": "certs.pem",
});

Next, create a notification to send to your app:

let notification = new apn.Notification();
notification.alert = "Hello, this is a test!";
notification.badge = 1337;

The Notification object that's created has all the properties that you might normally see on an apns payload. For more information on what can be included in the payload, refer to Apple's documentation and the documentation for node-apn.

After creating the notification, all you need to do is grab the push token that you obtained from your app in the first section of this article, and send the notification:

let token = "<your-token>";

provider.send(notification, token).then( (response) => {
  console.log("done");
});

To run this script, open your terminal, navigate to the folder that your script is in and run the following command:

node send_push.js

This will run your javascript file using Node.js, and you should see a push message appear on your device! Pretty cool, right? Even if you've never written a line of javascript in your life, modifying this sample script should be fairly straightforward and both Apple's documentation and node-apn's documentation should be able to point you in the right direction if you get stuck.

In summary

In today's article, you learned how you can set up push notifications in your app, generate certificates that are used to send push notifications manually or through Fastlane and you saw how you can create a simple Node.js script to send push notifications. Personally, I love using Fastlane to generate my .pem file and sending push notifications through a simple script feels so much more flexible than having to use third-party provider during development.

Of course, when your app is finished and you deploy it to the App Store, it might make much more sense for you to integrate Firebase push messages in your app. They handle all the complicated stuff like keeping track of push tokens, certificates and know how to send push notifications without blowing up their server. However, if you don't send many push messages, or if you have some knowledge of back-end development, it might be feasible for you to own your push notification service yourself. It really depends on what you're comfortable with.

That said, during development I personally prefer using a local script to send test push notifications. And maybe you will too now that you realize it's not extremely complicated to do. If you have any questions, feedback or anything else please feel to reach out on Twitter.

Scheduling daily notifications on iOS using Calendar and DateComponents

On iOS, there are several ways to send notifications to users. And typically every method of sending push notifications has a different goal. For example, when you're sending a remote push notification to a user, you will typically do this because something interesting happened outside of the user's device. Somebody might have sent them a message for example, or something exciting happened during a sports game.

However, when we schedule notifications on the device locally, we typically do this as a reminder, or in response to a user action. Like, for example, entering or exiting a geofence. In today's post, I'm going to focus on scheduling notifications that are essentially repeating reminders. They are notifications that are scheduled to show up every day at a certain time, or maybe every Monday at 09:00 am, or on every first day of the month. The same technique can be used to schedule each of these notifications.

In today's post you will learn about the following:

  • Working with the Calendar and DateComponents.
  • Scheduling notifications using a UNCalendarNotificationTrigger.

Let's get started with learning about the Calendar, shall we?

Working with the Calendar and DateComponents

If you have worked extensively with dates and times on a global scale, you know that pretty much any assumptions you may have about how dates work are wrong. A good way to explore how complicated dates can be is by playing with the Calendar object that is part of the Foundation framework. If you're reading this, it's likely that you're used to the Gregorian calendar. That's the calendar that typically has 365 days in a year unless it's a leap year and 24 hours in a day unless a leap second is added. I don't want to go into how messy dates are, so I'm going to leave it at that, but let's have some fun exploring calendars!

Take a look at the following code:

var gregorianCalendar = Calendar(identifier: .gregorian)
var japaneseCalendar = Calendar(identifier: .japanese)
var hebrewCalendar = Calendar(identifier: .hebrew)

func currentDate(for calendar: Calendar) -> DateComponents {
  calendar.dateComponents([.year, .month, .day], from: Date())
}

print("Gregorian", currentDate(for: gregorianCalendar))
print("Japanese", currentDate(for: japaneseCalendar))
print("Hebrew", currentDate(for: hebrewCalendar))

How different do you expect the output for each call to currentDate(for:) to be? Will the years all be the same? What about the months? Or the day of the month? Let's look at the output of the preceding code:

Gregorian year: 2019 month: 12 day: 11 isLeapMonth: false 
Japanese year: 1 month: 12 day: 11 isLeapMonth: false 
Hebrew year: 5780 month: 3 day: 13 isLeapMonth: false 

If you guessed that the dates would all be completely different, you were right! The Calendar object can take dates, like the current date and represent them as DateComponents for whatever the Calendar represents. So we can take the current date on your machine, and convert it to a date representation that makes sense for the calendar you use to represent dates.

This also works the other way around, we can use DateComponents to represent a date that's based on a specific Calendar:

var components = DateComponents()
components.year = 1989
components.month = 11
components.day = 15

func date(for components: DateComponents, using calendar: Calendar) -> Date? {
  return calendar.date(from: components)
}

print("Gregorian", date(for: components, using: gregorianCalendar)!)
print("Japanese", date(for: components, using: japaneseCalendar)!)
print("Hebrew", date(for: components, using: hebrewCalendar)!)

The preceding code creates a DateComponents object that's based on the current calendar of the machine that's running this code. So in my case, it's Gregorian. Let's see what my birthday is on the Japanese and Hebrew calendars:

Gregorian 1989-11-14 23:00:00 +0000
Japanese 4007-11-14 23:00:00 +0000
Hebrew -1771-06-25 23:40:28 +0000

Notice how the Gregorian date is not what you would expect. The DateComponents were configured for November 15th. However, the printed date is November 14th. The reason for this is that we didn't specify a timezone for the calendar. Because of this, it defaults to GMT and since I'm not in the GMT timezone but in the UTC timezone, I get the wrong date. If you set components.timeZone to the timezone that you're representing your date in, you will get the expected output. So setting the timezone on the components objects like this: components.timeZone = TimeZone(identifier: "UTC") will produce the following output:

Gregorian 1989-11-15 00:00:00 +0000
Japanese 4007-11-15 00:00:00 +0000
Hebrew -1771-06-26 00:00:00 +0000

Much better. As you might realize by now, DateComponents describe dates using their components. In addition to year, month and day it's possible to specify things like era, minute, second, quarter, microsecond and much more. It's also possible to take a date and extract its DateComponents as I've shown in the first code snippet of this section.

Now that you have some working knowledge of Calendar and DateComponent, let's see how this applies to scheduling recurring notifications with UNCalendarNotificationTrigger.

Scheduling notifications using a UNCalendarNotificationTrigger

You probably didn't come here to spend a bunch of time reading about calendars and dates, you wanted to see how to schedule recurring notifications based on the time of day, day of the week, day of the month or pretty much any other similar kind of rule. To do that, I had to show you some examples of calendars. If you've never seen calendars and date components before, it would be incredibly hard to explain UNCalendarNotificationTrigger to you.

I'm going to assume that you have basic knowledge of asking a user for permission to send them notifications, and what the best way is to do this. If you need a quick reminder, here's how you ask for notification permissions in code:

UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { success, error in

  if error == nil && success {
    print("we have permission")
  } else {
    print("something went wrong or we don't have permission")
  }
}

Once you have the user's permission to send them notifications, you can schedule your local notifications as needed. Let's start with a simple example. Imagine sending the user a local notification every day at 09:00 am. You would use the following code to do this:

// 1
var dateComponents = DateComponents()
dateComponents.hour = 9
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)

// 2
let content = UNMutableNotificationContent()
content.title = "Daily reminder"
content.body = "Enjoy your day!"

let randomIdentifier = UUID().uuidString
let request = UNNotificationRequest(identifier: randomIdentifier, content: content, trigger: trigger)

// 3
UNUserNotificationCenter.current().add(request) { error in
  if error != nil {
    print("something went wrong")
  }
}

The preceding code snippet creates a UNCalendarNotificationTrigger using a DateComponents object where only the hour is set to 9. This means that the notification will trigger as soon as the user's current date reaches the ninth hour of the day. Keep in mind that for this to work, the calendar that the user uses and the one that's used to configure the DateComponent must match. Since DateComponent uses the current calendar it's not a problem in this case. But if you allow the user to configure their reminder, and your UI assumes a Gregorian calendar while the user's device uses a different calendar, you might run into trouble.

You can configure your date components however you want, and every time the current date on the device matches your criteria, a notification will be shown to the user. If you want to show a notification only on Wednesdays at 2:00 pm, you could use the following DateComponents configuration:

var dateComponents = DateComponents()
dateComponents.hour = 14
dateComponents.weekday = 3

Keep in mind that not all calendars start their week on the same day. Most calendars will start their week on a Monday, but some might start on a Sunday. You can account for this by grabbing the current calendar's firstWeekday property and calculating its offset from the day you're targeting. Math for dates is quite complicated so make sure to always triple check your work.

With this knowledge, you should now be able to schedule recurring notification using DateComponents!

In Summary

In this article, you learned that dates are not the same everywhere in the world. Some calendars that are used in certain places of the world are completely different from the Gregorian calendar that I am familiar with. Luckily, the Calendar and DateComponents objects from Foundation make these differences somewhat manageable because they allow us to convert from a date represented in one calendar, to the same moment in time on another calendar. You saw an example of this in the first section of this article.

Once you learned the basics of Calendar and DateComponents, you learned how to use your new knowledge to schedule recurring notifications based on DateComponents using the UNCalendarNotificationTrigger class. It's really cool to be able to schedule notifications using this trigger because it allows you to set up notifications that repeat indefinitely and fire on a certain time, day of the week, month or any other interval that you can represent using DateComponent.

If you have any questions, feedback or anything else for me, don't hesitate to reach out on Twitter. I love hearing from you!

Handling deeplinks in your app

iOS 14 introduced a new way to build apps where you no longer need an App- and SceneDelegate for SwiftUI apps. Learn how to handle deeplinks for these apps in this article.

On iOS, it's possible to send users from one app to the next or to send them from a webpage into an app. A link that's used to send a user to an app is typically called a deeplink. Often, there is more than one way for a deeplink to be passed to your app. Because of this, it's not always trivial to handle deeplinks. In today's article, I will go over two kinds of deeplinks that you can support in your apps, and I will show you how you can create a deeplink handler that can handle virtually any deeplink you throw at it. This article is divided into the following topics:

  • Using a custom URL scheme to handle deeplinks
  • Adding Universal Links to your app
  • Interpreting a deeplink and navigating to the correct view

Ready to level up your deeplink knowledge? Let's get going with custom URL schemes!

Using a custom scheme to handle deeplinks

A popular way for apps to support deeplinks is through custom URL schemes. A URL scheme is a prefix that is placed before your url. For example, if you make a request a webserver, the scheme is typically https:// or if you make user of websockets in your app, you might use the wss:// scheme. It's also possible to define your own, custom URL scheme. For example: deeplink-example:// or myapplication://. These schemes won't mean anything on devices that don't have your app installed. Using a custom scheme in a web browser like Safari won't work. Only your app knows how to handle your custom URL scheme, and iOS knows how to forward URLs that use your scheme to your app.

Before you can handle custom URL schemes in your app, you must register the URL schemes you want to support in your Info.plist file. Registering your URL scheme helps iOS determine what app it should ask to open a deeplink, but it's also used to protect the privacy of users. Apps cannot check whether iOS can handle URL schemes that they haven't registered in their Info.plist. If you could check for any URL scheme, at any time, it would be possible for apps to detect other installed apps by random asking iOS whether it can open as many URL schemes as possible. Luckily for your users, iOS limits the amount of attempts apps have to check whether a certain scheme is supported, and they must specify the schemes that they wish to support upfront.

To register a custom URL scheme for your app, you can either add entries to your Info.plist by hand, or you can go through the Info tab in your project's settings. I prefer the latter. The Info tab has a section called URL Types. This is where you register your custom URLs. An example of a custom scheme looks as shown in the following screenshot:

URL Types entry

The identifier field is equal to our app's bundle identifier, the URLSchemes field contains a custom scheme, in this case, deeplink-example and the other fields are empty. Adding a scheme here will automatically update the Info.plist as shown in the following screenshot:

URL in Info.plist

Because I added the deeplink-example scheme to this app's Info.plist it can now handle URLs that look as follows:

deeplink-example://some.host/a/path

When your app is asked to handle this deeplink, the scene(_:openURLContexts:) method is called on your scene delegate. If you're using the older app delegate rather than a scene delegate, the application(_:open:options:) method would be called on your app delegate object.

You can validate whether your scheme was added and works correctly by creating a simple view controller, adding a button to it and making it call a method that does the following:

let url = URL(string: "deeplink-example://donnywals.com/")!
UIApplication.shared.open(url, options: [:], completionHandler: nil)

And in your open URL method (scene(_:openURLContexts:) if you're using a scene delegate and application(_:open:options:) if you're only using an app delegate) you could print the following:

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
  for context in URLContexts {
    print("url: \(context.url.absoluteURL)")
    print("scheme: \(context.url.scheme)")
    print("host: \(context.url.host)")
    print("path: \(context.url.path)")
    print("components: \(context.url.pathComponents)")
  }
}

Note:
All examples used for URL handling will be based on the scene delegate. The only major difference between the app delegate and scene delegate is that the app delegate receives a single URL that it can use directly. The scene delegate receives a Set of URLContext objects where the URL has to be extracted from the context.

The preceding code takes the URL that should be opened and prints several of its components, I will explain more about these components later in the interpreting a deeplink and navigating to the correct view section.

For now, you know all you need to know about custom URL schemes. Let's take a short look at Universal Links before we move on to handling deeplinks.

Adding Universal Links to your app

In the previous section, you saw how easy it is to add any URL scheme to your app. The example I just showed you is fine, but it's also possible to register schemes for apps you don't own. Like for example uber:// or facebook://. When you claim a URL scheme like this, and you're not using the scene delegate, you can easily call UIApplication.shared.canOpenURL(_:) to figure out whether a user has certain apps installed. It also means that anybody can potentially hijack your custom URL scheme, which is not ideal. You can get around these issues by using Universal Links instead of URL schemes.

To add Universal Links to your app, you need to add the Associated Domains capability to your app. In your project settings, go to the Signing & Capabilities tab and click the + icon in the top left corner. Look for the Associated Domains capability and add it. After doing this you need to add your domains to the capability. When you add domains for Universal Links, you should use the following format: applinks:<your domain name>. The following screenshot shows the entry for donnywals.com:

App Links entry

After doing this, you need to upload a file to the server that you serve your website from. This file is called the apple-app-site-association file and it's used to verify that your app and domain belong together. Apple will look for this file on the root of your domain. In my case, that would be https://donnywals.com/apple-app-site-association. The apple-app-site-association file should use a JSON format and contain the following contents:

{
  "applinks": {
    "apps": ["A4VDH56G8B.com.donnywals.deeplinks"]
  }
}

The apps array contains all apps that belong to this domain. The format of each app in the apps array is as follows: <team identifier>.<app bundle id>.

Important:
Once you've added this file to your domain, make sure to reinstall your app if you're running it from Xcode. The system appears to only download your apple-app-site-association file when your app is installed or updated.

Once the system has matched your app and domain to each other, all links found in iOS that point to the domain you're using for Universal Links will be sent to your app unless the user chooses otherwise. If you only want to open very specific URLs in your app, consider using a subdomain for Universal Links rather than using your website's regular domain name. Make sure to update the domain in your app capability settings and make sure that your server can serve the apple-app-site-association file.

When the system decides that your app should open a Universal Link, it calls scene(_:continue:) on your scene delegate, or application(_:continue:restorationHandler:) on the app delegate. This method receives an instance of NSUserActivity with NSUserActivityTypeBrowsingWeb set as its activityType, and the URL that needs to be opened as the webpageURL of the user activity. The following shows how you could extract the target URL from a user activity:

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
  guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
    let urlToOpen = userActivity.webpageURL else {
      return
  }

  print(url)
}

Interpreting a deeplink and navigating to the correct view

Probably the most interesting part of implementing deeplinks is determining how your app will handle them. Since the implementation that's best for you is dependent on a lot of things, I will show you a simple implementation that should be possible to adopt for most applications somehow. I'm not going to show you how this might work in a coordinator pattern and I won't be using any complex helper objects. Both are very suited for handling deeplinks but I want to make sure you understand how you can handle deeplinks with a very plain setup rather than get bogged down into the details of a specific architecture.

Note:
In the code examples I will be working from the scene delegate, keep in mind that if you don't have a scene delegate, all code should go in your app delegate and application(_:open:options:) will be the entry point for all deeplinks that use a custom URL scheme, and application(_:continue:restorationHandler:) is the entry point for Universal links.

So far, I have shown you the following code:

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
  for context in URLContexts {
    print("url: \(context.url.absoluteURL)")
    print("scheme: \(context.url.scheme)")
    print("host: \(context.url.host)")
    print("path: \(context.url.path)")
    print("components: \(context.url.pathComponents)")
  }
}

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
  guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
    let urlToOpen = userActivity.webpageURL else {
      return
  }

  print(url)
}

The scene delegate can be asked to open multiple URLs. In practice, this shouldn't happen if you're handling normal deeplinks, so let's simplify this code a little bit.

guard let context = URLContexts.first else { return }

print("url: \(context.url.absoluteURL)") // https://app.donnywals.com/post/10
print("scheme: \(context.url.scheme)") // https
print("host: \(context.url.host)") // app.donnywals.com
print("path: \(context.url.path)") // /post/10
print("components: \(context.url.pathComponents)") // ["/", "posts", "10"]

Every URL is constructed using components. The preceding code lists the most important components. I added some comments to show you how any URL can be deconstructed into components. When you're handling a deeplink, you're usually most interested in the pathComponents array because that contains the most useful information in your URL. For this reason, it's often not needed to distinguish between Universal Links or custom schemes when you're handling deeplinks, unless you absolutely have to. It this is the case, the scheme property of the URL should contain the information you need to make the distinction between Universal Links and custom schemes. Let's see how we could implement a deeplink handling strategy to handle two different links:

  • https://app.donnywals.com/post/10
  • https://app.donnywals.com/settings/profile

Since a lot of apps use tab bars for their main navigation and navigation controllers for their sub-navigation, I will do the same in my example of handling deeplinks.

The following code passes a URL from each of the URL handling entry points to a new method that picks apart the path components and determines whether we're showing a settings page or a post.

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
  guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
    let urlToOpen = userActivity.webpageURL else {
      return
  }

  handleURL(urlToOpen)
}

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
  guard let urlToOpen = URLContexts.first?.url else { return }

  handleURL(urlToOpen)
}

func handleURL(_ url: URL) {
  guard url.pathComponents.count >= 3 else { return }

  let section = url.pathComponents[1]
  let detail = url.pathComponents[2]

  switch section {
  case "post":
    guard let id = Int(detail) else { break }
    navigateToItem(id)
  case "settings":
    navigateToSettings(detail)
  default: break
  }
}

Since the first component in the path components array is always "/", we need to look at the second and third entries to retrieve the section of the app we need to show, and the detail page we need to show. Depending on the section, we then call different methods to handle navigating to the appropriate page. The reason for this is mostly so the code remains clear, concise and easy to read. Let's look at navigateToItem(_:):

func navigateToItem(_ id: Int) {
  // 1
  guard let tabBarController = window?.rootViewController as? UITabBarController else {
    return
  }

  // 2
  guard let viewControllers = tabBarController.viewControllers,
    let listIndex = viewControllers.firstIndex(where: { $0 is ListNavigationController }),
    let listViewController = viewControllers[listIndex] as? ListNavigationController else { return }

  // 3
  listViewController.popToRootViewController(animated: false)
  tabBarController.selectedIndex = listIndex

  // 4
  let detailViewController = ListDetailViewController(item: id)
  listViewController.pushViewController(detailViewController, animated: true)
}

This method grabs the window's root view controller and casts it to a tab bar controller. This is really only done so we can access the tab bar controller's viewControllers property. We then look for the position of the ListNavigationController that shows our posts, and we make sure that we can extract the ListNavigationController from the view controllers array.

The purpose of steps one and two is to obtain the view controller that will show the post detail and to find its position within the tab bar controller's tab bar items. In step three, the tab bar controller's selectedIndex is set to the index of our list view controller, and since the list view controller is a navigation controller, we pop it to its root view controller.

Depending on whether this is needed for your app, you might not need to pop to the root view controller. Or maybe your app shows modal view controllers. If that's the case you might want to call dismiss() on both your tab bar and your navigation controller.

In step four, the target view controller is created and the navigation controller is told to present the view controller.

Let's look at the implementation of navigateToSettings(_:) to see if it's any different:

func navigateToSettings(_ detailType: SettingsDetailViewController.DetailType) {
  guard let tabBarController = window?.rootViewController as? UITabBarController else {
    return
  }

  guard let viewControllers = tabBarController.viewControllers,
    let settingsIndex = viewControllers.firstIndex(where: { $0 is SettingsNavigationController }),
    let settingsViewController = viewControllers[settingsIndex] as? SettingsNavigationController else { return }

  settingsViewController.popToRootViewController(animated: false)
  tabBarController.selectedIndex = settingsIndex

  settingsViewController.popToRootViewController(animated: false)

  let detailViewController = SettingsDetailViewController(type: detailType)
  settingsViewController.pushViewController(detailViewController, animated: true)
}

Examine this code closely, and you'll find that the process of showing the settings page is pretty much the same as it is for the posts page.

At this point, your app will happily open links as long as your app is active in the background. When it's not active in the background and your user is sent to your app through a deeplink, the logic you have implemented currently won't work.

If your app is launched because to open a deeplink, the link is passed to the connectionOptions in your scene(_:willConnectTo:options:) method in the SceneDelegate and it can be accessed through the connection options' urlContexts property. To handle the link, you can call scene(_:openURLContexts:) with the contexts that are present on the connectionOptions:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
  self.scene(scene, openURLContexts: connectionOptions.urlContexts)
}

Similarly, if your app is launched to open a Universal Link, you can inspect connectionOptions.userActivities to see if there are any user activities for your app to handle. If this is the case, you might grab the first user activity and pass it to scene(_:continue:):

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
  if let userActivity = connectionOptions.userActivities.first {
    self.scene(scene, continue: userActivity)
  } else {
    self.scene(scene, openURLContexts: connectionOptions.urlContexts)
  }
}

Note that you might receive several user activities so the URL that you're supposed to open could very well be the second or third item in the user activities set. Make sure to account for this in your app if needed.

You need to make sure that you call this method after you've set up and configured your scene and layout because your UI won't be available otherwise which means that you can't activate the correct screen in your app.

Note:
If you're using the AppDelegate instead of the SceneDelegate, the link is passed along with the launchOptions dictionary in application(_:didFinishLaunchingWithOptions:) under the UIApplication.LaunchOptionsKey.url key.

The way of handling deeplinks I have just shown you should contain enough information to give an idea of how deeplink handling might fit in your app. If you're using Storyboards, you could obtain a reference to your Storyboard, make it instantiate view controllers or ask the storyboard to perform segues depending on the path components in the URL you're handling. When you're implementing deeplinks in your app, it's important to try and keep your implementation flexible and avoid hardcoding things like indexes of tabs in a tab bar controller. If your tab bar items or ordering ever change, your deeplinking logic might have to change too, which is typically not what you want. It's better to look up appropriate indices and view controllers dynamically like I've shown in my example.

In Summary

Deeplink functionality is something a lot of apps need but it's not always easy to get started with adding deeplinks for your app. Do you use a custom URL scheme? Or are Universal Links a better fit for you? Or maybe a combination of both?

In today's article, I have shown you how to add support for both Universal Links and URL schemes. You know how each behaves and what you need to do to register each type of deeplink on your app so iOS knows where to send users when they encounter one of your links.

You also saw an example of deeplink handling code. Again, you might want to adapt this to fit better in your architecture or you might have to make some changes to make this example work with Storyboards, but the idea remains the same. Extract the path, determine what the user should see and find a good path to get there. Usually, this involves setting a selected tab on a tab bar controller and pushing a view controller on a navigation controller.

If you have any questions, feedback or additions for me about this article, don't hesitate to contact me on Twitter.

Thanks to Mat Gadd for making me realize Universal Links are handled in a different way than custom url schemes.

Measuring performance with os_signpost

One of the features that got screen time at WWDC 2018 but never really took off is the signposting API, also known as os_signpost. Built on top of Apple’s unified logging system, signposts are a fantastic way for you to gain insight into how your code behaves during certain operations.

In this post, I will show you how to add signpost logging to your app, and how you can analyze the signpost output using Instruments.

Adding signposts to your code

If you’re familiar with OSLog and already use it in your app, adding signpost logging should be fairly simple for you. If you have never used OSLog before, don’t worry, you can follow along with this post just fine.

Once you have determined what operation in your code you want to measure exactly, you need to create a so-called log handle that you can use as an entry point for your signpost logging. Recently I wanted to measure the difference in execution speed of certain quality of service settings on an operation queue. I built a simple sample app and added my log handle to my view controller. In your code, you should add it near the operation you want to measure. For example by making the log handle a property on your networking object, view model, view controller or anything else. What’s important is that it’s an instance property since you want to make sure the log handle can be accessed from anywhere within the code you’re measuring.

You can create a log handle as follows:

import os.log

let logHandler = OSLog(subsystem: "com.dw.networking", category: "qos-measuring")

A log handler always belongs to a subsystem, you might consider your entire app to be a subsystem, or maybe you consider different components in your app to be their own subsystem. You should name your subsystem some unique for your app, and it’s good practice to use reverse DNS notation for the naming. You also need to specify a category, in this case, I chose one that describes the thing I’m measuring with the signpost we’ll add next. Note that the preceding code imports the os.log framework. The signpost API is part of this framework so we need to import it in order to use signposts.

In a very simple example, you might want to add signposts in a way similar to what the following code snippet shows:

func processItem(_ item: Item) {
  os_signpost(.begin, log: logHandler, name: "Processing", "begin processing for %{public}s", item.id)

  // do some work
  os_signpost(.event, log: pointsOfInterestHandler, name: "Processing", "reached halfway point for %{public}s", label)
  // do some more work

  os_signpost(.end, log: logHandler, name: "Processing", "finished processing for %{public}s", item.id)
}

Note that there are three different event types used in the preceding code:

  • .begin
  • .event
  • .end

The .begin event is used to mark the start of an operation, and the .end event is used to mark the end of an operation. In this example, the system will use the name as a way of identifying each operation to link up the operation start and end events. We can also add points of interest that occur during an event, for example when you reach the halfway point. You add points of interest using the .event event type.

In order to log .event events, you need a special log handler that specializes in points of interest. You create such a log handler as follows:

let pointsOfInterestHandler = OSLog(subsystem: "dw.qostest", category: .pointsOfInterest)

It works pretty much the same as the normal logger, except you use a predefined category.

Also note the final two arguments passed to os_signpost: "finished processing for %{public}s", item.id. The first of these two arguments is a format string. Depending on the number of placeholders in the format string, the first, second, third, etc. arguments after the format string will be used to fill the placeholders. You can specify placeholders as either {public} or {private}. Specifying neither will default to {private}. Values passed to public placeholders are visible in the console, even if your app is running without the Xcode debugger attached. So if you’re handling sensitive data, make sure to mark your placeholders as private.

The s after the placeholder’s access level specifier marks that the value that’s used to fill the placeholder will be a string. You could also use a number instead of a string if you replace s with a d. Apple recommends that you only use strings and numbers for your placeholders in order to keep your logs simple and lightweight.

This example is very simple, everything occurs in the same method and we could use a string to link our signpost begin and end. But what if we have multiple operations running that all use signposts. If they all have the same name, they will start interfering with each other. In that case, you can use a SignpostID. You can create SignpostID objects in two ways:

let uniqueID = OSSignpostID(log: logHandler)
let idBasedOnObject = OSSignpostID(log: logHandler, object: anObject)

If you use the first method, you need to keep a reference to the identifier around so you can use it to correctly link .begin and .end events together. If your operation is strongly related to an instance of a class, for example, if each instance only runs one operation, or if you’re manipulating an object that’s a class in your operation, you can use the second method to obtain a SignpostID. When you create an identifier using an object, you always get the same SignpostID back as long as you’re using the same instance of the object. Note that the object must be a class. You can’t use value types for this.

You can use SignpostID in your code as follows:

class ImageManipulator {
  // other properties, like the logHandle
  let signpostID = SignpostID(log: logHandler, object: self)

  func start() {
    os_signpost(.begin, log: logHandler, name: "Processing", signpostID: signpostID, "begin processing for %{public}s", item.id)
    // do things
  }

  func end() {
    os_signpost(.end, log: logHandler, name: "Processing", signpostID: signpostID, "finished processing for %{public}s", item.id)
  }
}

Our signposts are now uniquely identified through the signpostID that gets generated based on the ImageManipulator itself. Note that this object is now expected to only work on one image at a time. If you would use this object for multiple operations in parallel, you’d need to either create a unique SignpostID for each operation or, for example, generate the identifier based on the image.

Reading signposts with instruments

Once you’ve added signposts to your code, you can view them in Console.app, or you can analyze them with Instruments. To do this, run your app with Instruments like you normally would (cmd + i or Product -> Profile) and select a blank Instruments template:

New Instrument Window

In the blank Instrument window, click the + icon in the top right, find the os_signpost instrument and double click it to add it to your Instruments session. Also, add the points of interest instrument from the same menu.

Add signpost Instrument

After doing that, hit record and use your app so a bunch of signposts are logged, and you have some data to look at:

Instruments overview

If you have the os_signpost track selected, Instruments will group measurements for each of its begin and end signposts based on your signpost message. So if you’re using the same message for operations, as we have in the earlier examples, performing the same operation over and over will cause Instruments to group those operations. And more importantly, Instruments will tell you the maximum duration, minimum duration, average duration and more for each operation. That way, you can easily measure the performance of the things your app does, without relying on print statements or date calculations that might negatively impact your code!

In summary

In this post, you’ve seen that Instruments and os_signpost are a powerful team that can help you gain insight into your code. You can use signposts as a way of regular logging to Console.app, but it’s also very well suited to do low-impact performance measuring of your code if you combine signposts with Instruments. Both signposts and Instruments are tools you might not need or use all the time, but knowing they exist, what they do and when you use them is essential to learning more about the code you write, and ultimately to becoming a better developer.

If you have feedback, questions or anything else regarding this post for me, please reach out on Twitter. I love hearing from you.

Using Xcode’s memory graph to find memory leaks

There are many reasons for code to function suboptimally. In a post, I have shown you how to use the Time Profiler to measure the time spent in each method in your code, and how to analyze the results. While a lot of performance-related problems can be discovered, analyzed and fixed using these tools, memory usage must often be debugged slightly differently. Especially if it's related to memory leaks.

In today's post, I will show you how to use the Memory Graph tool in Xcode to analyze the objects that are kept in memory for your app, and how to use this tool to discover memory leaks. I will focus specifically on retain cycles today.

Activating the Memory Graph

When you run your app with Xcode, you can click the memory debugger icon that's located between your code and the console, or at the bottom of your Xcode window if you don't have the console open:

Memory debugger icon

When you click this icon, Xcode will take a snapshot of your app's memory graph and the relationships that every object has to other objects. Your app's execution will be paused and Xcode will show you all objects that are currently in memory. Note that this might take a little while, depending on how big your app is.

Example memory graph

In the sidebar on the left-hand side, Xcode shows a full list of all objects that it has discovered. When you select an object in the sidebar, the middle section will show your selected object, and the relationships it has to other objects. Sometimes it's a big graph, like in the screenshot. Other times it's a smaller graph with just a couple of objects.

If Xcode spots a relationship that it suspects to be a memory leak, or retain cycle, it will add a purple square with a exclamation mark behind the object in the sidebar. In the screenshot you just saw, it's quite obvious where the purple squares are. If they are more hidden, or you just want to filter memory leaks, you can do so using the filter menu at the bottom of the sidebar as shown in the following screenshot:

Filtered view

The screenshot above shows that instances of two different objects are kept in memory while Xcode thinks they shouldn't. When you click one of them, the problem becomes visible immediately.

Retain cycle image

The DataProvider and DetailPage in this example are pointing at each other. A classic example of a retain cycle. Let's see how this occurs and what you can do to fix it.

Understanding how retain cycles occur and how to fix them

In iOS, objects are removed from memory when there are no other objects that keep a strong reference to them. Every instance of an object you create in your app has a retain count. Any time you pass a reference to your object to a different place in your code, its retain count is increased because there is now one more object pointing at the location in memory for that object.

This principle of retain counts mostly applies to classes. Because when you pass around an instance of a class in your code, you're really passing around a memory reference, which means that multiple objects point to the same memory address. When you're passing around value types, the value is copied when it's passed around. This means that the retain count for a value type is typically always one; there is never more than one object pointing to the memory address of a value type.

In order for an object to be removed from memory, its reference count must be zero; no objects should be referencing that address in memory. When two objects hold a reference to each other, which is often the case when you're working with delegates, it's possible that the reference count for either object never reached zero because they keep a reference to each other. Note that I mentioned a strong reference at the beginning of this section. I did that on purpose, if we have a strong reference, surely there is such a thing as a weak reference right? There is!

Weak references are references to instances of reference types that don't increase the reference count for the object the reference points to. The principles that apply here are exactly the same as using weak self in closures. By making the delegate property of an object weak, the delegate and its owner don't keep each other alive and both objects can be deallocated. In the example we were looking at this means that we need to change the following code:

class DataProvider {
  var delegate: DataDelegate?

  // rest of the code
}

Into the following:

class DataProvider {
  weak var delegate: DataDelegate?

  // rest of the code
}

For this to work, DataDelegate must be constrained to being a class, you can do this by adding : AnyObject to your protocol declaration. For example:

protocol DataDelegate: AnyObject {
  // requirements
}

When you'd run the app again and use the memory graph to look for retain cycles, you would notice that there are no more purple squares and the memory graph looks exactly like you'd expect.

In Summary

In this article, I have shown you that you can use Xcode to visualize and explore the memory graph of your app. This helps you to find memory leaks and retain cycles. When clicking on an object that's in memory, you can explore its relationship with other objects, and ultimately you can track down retain cycles. You also learned what a retain cycle is, how they occur and how you can break them.

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

Finding slow code with Instruments

Every once in a while we run into performance problems. One thing you can do when this happens is to measure how long certain things in your code take. You can do this using signposts. However, there are times when we need deeper insights in our code. More specifically, sometimes you simply want to know exactly how long each function in your code takes to execute. You can gain these insights using the Time Profiler Instrument. In today's article, I will show you how you can use the Time Profiler to analyze your code, and how you can optimize its output so you can gain valuable insights.

Exploring the Time Profiler Instrument

If you want to analyze your app, you need to run it for profiling. You can do this by pressing cmd+i or by using the Product -> Profile menu item. When your app is done compiling, it will be installed on your device and Instruments will launch. In the window that appears when Instruments launches, pick the Time Profiler template:

Instruments template selection

When you select this template, Instruments will launch a new Instruments session with several tracks.

Empty Instruments window

The one you're most interested in is the Time Profiler track. When you select the Time Profiler track, the table under the Instruments timeline will show your app's objects and their methods, and how much time is spent in each method. To profile your app, unlock your device and hit the record button in the top left corner. Use your app like you normally would and make sure to spend some time with the feature your most interested in. Instruments will begin filling up with measurements from your code as shown in the following screenshot.

Instruments with measurements

The Time Profiler takes snapshots of your app's memory and CPU usage every couple of milliseconds to create a picture of what is running, and when. Based on this, the Time Profiler measures how much time is spent in each method. The flip side here is that the Time Profiler is not suited for fine-grained, high-resolution profiling of your code. If this is what you need, you should use signposts instead.

Note
It's always best to run your app on a real device if you want to run the Time Profiler on it. The simulator has all the processing power from your working machine at its disposal so measurements will be very skewed if you profile your app using the Simulator.

Once you feel like you've captured enough data to work with, you can begin analyzing your measurements.

Analyzing the Time Profiler's measurements

By default, Instruments will show its measurements from the inside out. The topmost item in the tree is your app, followed by several threads. Note how instruments displays a number of seconds spent in each thread. This counter only increments if your app is actively processing data on the corresponding thread. Since you're probably not really interested in working your way from the inside out, and also not in system libraries, it's a good idea to change the way instruments visualizes data. In the bottom of the window, there's a button named Call Tree, if you click that, you can specify how Instruments should display its measurements. I always use the following settings:

Instruments settings

At the surface, not much will seem to have changed. Your code is still separated by thread, but if you expand the threads, your code is listed first because the call tree is now shown from the outside in rather than from the inside out. Every time you drill down one level deeper, Instruments shows what method called the method you're drilling into.

In the app I've been profiling here I was looking for reasons that it took a long time to update my UI after an image has finished downloading. I can see that a lot of time is spent in my performTask method. This is the method that's responsible for fetching and processing the image, and ultimately pass it to the UI. There's also some time spent in the UIImage initializer. Which is called from the performTask method as shown in the following screenshot:

Instruments readout

Based on these findings, you would conclude that something fishy might be happening in performTask because we're spending all of our time there. If the UIImage initialization was slow, we would be spending way more time in that initializer. And since the code spends so much time in performTask, but not in the UIImage initializer, this is a good first guess.

In this case, I made performTask slow on purpose. After loading an image I would write it to the phone's documents directory a couple of times, and also convert it to a UIImage not once, but five times before updating the UI. In this case, a potential fix would be to either update the UI immediately before persisting the image to the documents directory and to remove the loop that's obviously not needed.

In summary

From personal experience, I can tell you that the Time Profiler Instrument is an extremely valuable tool in an iOS developer's toolbox. If your UI doesn't scroll as smooth as you want, or if your device runs hot every time you use your app, or if see CPU and memory usage in Xcode rise all the time, the Time Profiler is extremely helpful to gain an understanding of what your code is doing exactly. When you profile your code and know what's going on, you can start researching performance problems in your code with more confidence.

If you have any questions about the Time Profiler, have feedback or just want to reach out, you can find me on Twitter.

Effectively using static and class methods and properties

Swift allows us to use a static prefix on methods and properties to associate them with the type that they’re declared on rather than the instance. We can also use static properties to create singletons of our objects which, as you have probably heard before is a huge anti-pattern. So when should we use properties or methods that are defined on a type rather than an instance? In this blog post, I’m going to go over several use cases of static properties and methods. Once we’ve covered the hardly controversial topics, I’m going to make a case for shared instances and singletons.

Sometimes you'll run into a Swift Concurrency errors stating that your static vars aren't concurrency safe. In this post we explore how you can solve this and why.

Using static properties for configuration

A very common use case for static properties is configuration. All over UIKit you can find these properties whose sole purpose is to configure other objects. The main reason they are defined as static strings is quite possibly because making them static provides a namespace of sorts. A common example I like to use for explaining static properties for configuration is when you use objects with static properties as a style guide. For example, it’s much nicer to define colors or font sizes in a single place than having their values scattered throughout your code. When you want to change a certain color or font size across your app you would have to go through your entire app and then replace the appropriate values. If this is the approach you take it’s only a matter of time before you forget one or more properties. Now consider the following configuration object:

enum AppStyles {
  enum Colors {
    static let mainColor = UIColor(red: 1, green: 0.2, blue: 0.2, alpha: 1)
    static let darkAccent = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1)
  }

  enum FontSizes {
    static let small: CGFloat = 12
    static let medium: CGFloat = 14
    static let large: CGFloat = 18
    static let xlarge: CGFloat = 21
  }
}

If you have to slightly tweak your main color, you only have to change a single place in your code. By naming the color using a descriptive name, your properties are not bound to the visual representation they end up having on-screen.

Note that I’m using an enum with static properties here. The reason for that is because I don’t want to be able to create instances of my configuration objects. And since enums don’t have initializers they are well suited for this purpose. A struct with a private initializer would do the trick as well, I just happen to think enums are nicer for this job.

Static properties also make sense if you want to define a finite set of strings that you might want to use for notifications that you’re sending through the Notification Center on iOS, or for any other time where you want to have some kind of global configuration in your app that should be constant across your entire app without having to pass around a configuration object.

Using static properties for expensive objects

Another use of static properties is to use them as a cache. Creating certain objects in your app might be quite expensive, even though you might be able to create an instance once and reuse it throughout the lifetime of your app. A good example of an expensive object that you might want to keep around in a static property is a dateformatter:

struct Blogpost {
  private static var dateFormatter = ISO8601DateFormatter()

  let publishDate: Date?
  // other properties ...

  init(_ publishDateString: String /* other properties */) {
    self.publishDate = Blogpost.dateFormatter.date(from: publishDateString)
  }
}

No matter how many times we want to create a blogpost instance, the date formatter we use to convert the string will always be an ISO8601DateFormatter, we can create a static property on Blogpost that holds the date formatter we need. This is useful because date formatters are expensive to create and can be reused without any consequences. If we would associate the date formatter with an instance of Blogpost rather than making it static and associating it with the type, a new date formatter would be created for every instance of Blogpost. This can lead to many identical dateformatters being created which is pretty wasteful.

So any time you have objects that are expensive to create, and that can be safely reused many times, it might be a good idea to define them statically so you only have to create an instance once for the type rather than creating a new expensive object for every instance that uses said expensive object.

Creating a factory with static methods

A common pattern in programming is the factory pattern. Factories are useful to create complex objects using a simple mechanism while hiding certain details about the target object’s initializer. Let’s look at an example:

enum BlogpostFactory {
  static func create(withTitle title: String, body: String) -> Blogpost {
    let metadata = Metadata(/* metadata properties */)
    return Blogpost(title: title, body: body, createdAt: Date(), metadata: metadata)
  }
}

What’s nice is that we can use this BlogpostFactory to create new instances of Blogpost. Depending on your use case you might not want to build factories this way. For example, if there is some kind of state associated with your factory. In simple cases like this, however, it might make sense to have a simple static method to create instances of an object on your behalf. Another example is using a default static method on a type to create some kind of basic starting point for a blog post or form:

extension Blogpost {
  static func sensibleDefault() -> Blogpost {
    return Blogpost(title: "Hello, world!",
                    body: "Hello, sample body",
                    createdAt: Date())
  }
}

You could use this default() static method to create a placeholder object whenever a user is about to create a new blog post.

Static methods are useful if you want to associate a certain method with a type rather than an instance. Nothing stops us from creating a free function called defaultBlogpost() that creates a blog post instance. However, it’s much nicer to associate the default() method directly with Blogpost.

Understanding how class methods differ from static methods

In the previous examples, I always used static methods. In Swift, it’s also possible to define class methods on classes. Class methods are also associated with types rather than instances, except the main difference is that subclasses can override class methods:

class SomeClass {
  class func date(from string: String) -> Date {
    return ISO8601DateFormatter().date(from: string)!
  }
}

class SubClass: SomeClass {
  override class func date(from string: String) -> Date {
    return DateFormatter().date(from: string)!
  }
}

In the preceding example, SubClass overrides the date(from:) from its superclass so it uses a different date formatter. This behavior is limited to class methods, you can’t override static methods like this.

Understanding when to create shared instances

So far you have seen that you can use static properties for configuration, or expensive objects and how you can use static methods to create simple factories. A more controversial topic is the topic of shared instances. We have probably all used at least a couple of the following examples in our code at some point:

  • URLSession.shared
  • UserDefaults.standard
  • NotificationCenter.default
  • DispatchQueue.main

These are all examples of shared instances.

Each of the above objects has a static property that holds a default, or shared instance of the type it’s defined on. If you think of these objects as singletons, you are mistaken. Let me explain why.

A singleton is an object that you can only ever have one instance of. It’s often defined as a static property in Swift, but in other languages, you might simply use the types initializer and instead of getting a new instance every time, you would get the same instance over and over.

The static properties on the types listed earlier are all shared instances rather than singletons because you are still free to create your own instances of every object. Nothing is preventing you from creating your own UserDefaults store, or your own URLSession object.

The shared instances that are offered on these objects are merely suggestions. They are fully configured, useful instances of these objects that you can use to quickly get up and running. In some cases, like DispatchQueue.main or NotificationCenter.default, the shared instances have specific purposes in your app. For example like how DispatchQueue.main is used for all UI manipulations, or how NotificationCenter.default is used for all notifications sent by UIKit and the system.

Whenever you use a shared instance, try to immediately add a built-in escape hatch for when you might decide that you want to use a different instance than the shared one. Let me show you an example of how you can do this:

struct NetworkingObject {
  let urlSession: URLSession

  init(urlSession: URLSession = URLSession.shared) {
    self.urlSession = urlSession
  }
}

The NetworkingObject uses a URLSession to make requests. Its initializer accepts a URLSession instance and has URLSession.shared as its default value. This means that in most cases you can create new instances of your networking object without passing the URL session explicitly. If you decide that you want to use a different URL session, for example in your unit tests, you can simply pass the session to the networking object’s initializer and it will use your custom URL session.

Shared instances are very useful for objects that you’ll likely every only need a single instance of, that is preconfigured and easily accessible from throughout your app. Their main benefit is that they also allow you to create your own instances, which means that you get the benefits of having a shared instance with shared state without losing the ability to create your own instances that have their own state if needed.

Knowing when to use a singleton

Singletons are universally known as an anti-pattern throughout the development community. I personally tend to prefer shared instances over singletons because with a shared instance you can still create your own instance of an object if needed while using the shared one when it makes sense to do so.

I do think, however, that there are responsible ways to use the singleton pattern in Swift. Given that a singleton’s only real requirement is that you can only ever have one instance of a singleton, you might write some code like this:

protocol Database {
  /* requirements */
}

struct AppDatabase: Database {
  static let singleton = AppDatabase()

  private init() {}
}

struct UserProvider {
  let database: Database
}

When used as described above, the singleton conforms to a protocol. In this case Database. The object that uses the database has a property that requires an object that conforms to Database. If we don’t access the singleton’s singleton property when we access the database but instead inject it into the UserProvider and other users of the database, the singleton is used like any other dependency.

So why make AppDatabase a singleton? You might ask. The reason is simple. If I have two instances of my database, it might be possible for two objects to write to my underlying storage at the same time if I don’t have a very good read/write mechanism in place. So to make sure that you can only ever create one instance of AppDatabase you can implement it as a singleton.

The major drawback to this approach for me is that this might encourage people to use the singleton property even though they should be using dependency injection to inject the singleton instance to hide the fact that we’re using a singleton. This is what code reviews are for though, and if everybody on your team agrees that it’s okay to use singletons like this you can go ahead and do it.

Keep in mind that singletons are still an anti-pattern though, all I provided here is a use case where I think the downsides are limited and isolated.

In summary

In this post, I showed you how you can use static properties to drive configuration on a type-level rather than an instance level. I also showed you that static properties are great for storing objects that are reused often and are expensive to create. Next, you saw how static methods can be used to implement a factory of sorts and how class methods are different from static methods.

Then we took a turn into a more controversial realm by exploring shared instances and singletons. I argued that shared instances are often nicer than singletons because you can still create your own instances of objects that offer a shared instance if needed. I then showed you that a singleton might not be so bad if you make it implement a protocol and inject the singleton into the initializer of an object that depends on a protocol rather than the singleton’s explicit type.

If you have any feedback, suggestions or if you want to talk to me about singletons and shared instances, my Twitter for you.

What is the “some” keyword in Swift?

If you have spent some time with SwiftUI or if you have watched the WWDC videos on SwiftUI this year, you may have noticed that views in SwiftUI have a property called body of type some View. The some keyword is new in Swift 5.1 and it’s part of a feature called opaque result types (SE-0244). What is this some keyword then? And how can you use it in your code?

I aim to answer these questions in this blog post. We’ll first explore what opaque result types are, and more specifically what problem they solve. Next, we’ll look at how opaque result types are used in SwiftUI and we’ll discover whether it’s a Swift feature that you’re likely to adopt in your code at some point.

Exploring opaque result types

To fully understand the problems solved by opaque result types, it’s good to have a solid understanding of generics. If you’re not familiar with generics at all, I recommend reading these to posts I wrote to get yourself up to speed:

If you’re not interested in learning loads about generics and just want to learn about opaque result types and what the some keyword is, that’s fine too. Just be aware that some of the content in this post could be confusing without understanding generics.

In Swift, we can use protocols to define interfaces or contracts for our objects. When something conforms to a protocol, we know that it can do certain things, or has certain properties. This means that you can write code like this:

protocol ListItemDisplayable {
  var name: String { get }
}

struct Shoe: ListItemDisplayable {
  let name: String
}

var listItem: ListItemDisplayable = Shoe(name: "a shoe")

When using this listItem property, only the properties exposed by ListItemDisplayable are exposed to us. This is especially useful when you want to have an array of items that are ListItemDisplayable where the concrete types can be more than just Shoe:

struct Shoe: ListItemDisplayable {
  let name: String
}

struct Shorts: ListItemDisplayable {
  let name: String
}

var mixedList: [ListItemDisplayable] = [Shoe(name: "a shoe"),
                                        Shorts(name: "a pair of shorts")]

The compiler treats our Shoe and Shorts objects as ListItemDisplayable, so users of this list won't know whether they’re dealing with shoes, shorts, jeans or anything else. All they know is that whatever is in the array can be displayed in a list because it conforms to ListDisplayable.

Opaque result types for protocols with associated types

The flexibility shown in the previous section is really cool, but we can push our code further:

protocol ListDataSource {
  associatedtype ListItem: ListItemDisplayable

  var items: [ListItem] { get }
  var numberOfItems: Int { get }
  func itemAt(_ index: Int) -> ListItem
}

The above defines a ListDataSource that holds some list of an item that conforms to ListItemDisplayable. We can use objects that conform to this protocol as data source objects for table views, or collection views which is pretty neat.

We can define a view model generator object that will, depending on what kind of items we pass it, generate a ListDataSource:

struct ShoesDataSource: ListDataSource {
  let items: [Shoe]
  var numberOfItems: Int { items.count }

  func itemAt(_ index: Int) -> Shoe {
    return items[index]
  }
}

struct ViewModelGenerator {
  func listProvider(for items: [Shoe]) -> ListDataSource {
    return ShoesDataSource(items: items)
  }
}

However, this code doesn’t compile because ListDataSource is a protocol with associated type constraints. We could fix this by specifying ShoesDataSource as the return type instead of ListDataSource, but this would expose an implementation detail that we want to hide from users of the ViewModelGenerator. Callers of listProvider(for:) only really need to know is that we’re going to return a ListDataSource from this method. We can rewrite the generator as follows to make our code compile:

struct ViewModelGenerator {
  func listProvider(for items: [Shoe]) -> some ListDataSource {
    return ShoesDataSource(items: items)
  }
}

By using the some keyword, the compiler can enforce a couple of things while hiding them from the caller of listProvider(for:):

  • We return something that conforms to ListDataSource.
  • The returned object’s associated type matches any requirements that are set by ListDataSource.
  • We always return the same type from listProvider(for:).

Especially this last point is interesting. In Swift, we rely on the compiler to do a lot of compile-time type checks to help us write safe and consistent code. And in turn, the compiler uses all of this information about types to optimize our code to ensure it runs as fast as possible. Protocols are often a problem for the compiler because they imply a certain dynamism that makes it hard for the compiler to make certain optimizations at compile time which means that we’ll take a (very small) performance hit at runtime because the runtime will need to do some type checking to make sure that what’s happening is valid.

Because the Swift compiler can enforce the things listed above, it can make the same optimizations that it can when we would use concrete types, yet we have the power of hiding the concrete type from the caller of a function or property that returns an opaque type.

Opaque result types and Self requirements

Because the compiler can enforce type constraints compile time, we can do other interesting things. For example, we can compare items that are returned as opaque types while we cannot do the same with protocols. Let’s look at a simple example:

protocol ListItemDisplayable: Equatable {
  var name: String { get }
}

func createAnItem() -> ListItemDisplayable {
  return Shoe(name: "a comparable shoe: \(UUID().uuidString)")
}

The above doesn’t compile because Equatable has a Self requirement. It wants to compare two instances of Self where both instances are of the same type. This means that we can’t use ListItemDisplayable as a regular return type, because a protocol on its own has no type information. We need the some keyword here so the compiler will figure out and enforce a type for ListItemDisplayable when we call createAnItem():

func createAnItem() -> some ListItemDisplayable {
  return Shoe(name: "a comparable shoe: \(UUID().uuidString)")
}

The compiler can now determine that we’ll always return Shoe from this function, which means that it knows what Self for the item that’s returned by createAnItem(), which means that the item can be considered Equatable. This means that the following code can now be used to create two items and compare them:

let left = createAnItem()
let right = createAnItem()

print(left == right)

What’s really cool here is that both left and right hide all of their type information. If you call createAnItem(), all you know is that you get a list item back. And that you can compare that list item to other list items returned by the same function.

Opaque return types as reverse generics

The Swift documentation on opaque result types sometimes refers to them as reverse generics which is a pretty good description. Before opaque result types, the only way to use protocols with associated types as a return type would have been to place the protocol on a generic constraint for that method. The downside here is that the caller of the method gets to decide the type that’s returned by a function rather than letting the function itself decide:

protocol ListDataSource {
  associatedtype ListItem: ListItemDisplayable

  var items: [ListItem] { get }Æ’
  var numberOfItems: Int { get }
  func itemAt(_ index: Int) -> ListItem

  init(items: [ListItem])
}

func createViewModel<T: ListDataSource>(for list: [T.ListItem]) -> T {
  return T.init(items: list)
}

func createOpaqueViewModel<T: ListItemDisplayable>(for list: [T]) -> some ListDataSource {
  return GenericViewModel<T>(items: list)
}

let shoes: GenericViewModel<Shoe> = createViewModel(for: shoeList)
let opaqueShoes = createOpaqueViewModel(for: shoeList)

Both methods in the preceding code return the exact same GenericViewModel. The main difference here is that in the first case, the caller decides that it wants to have a GenericViewModel<Shoe> for its list of shoes, and it will get a concrete type back of type GenericViewModel<Shoe>. In the example that uses some, the caller only knows that it will get some ListDataSource that holds its list of ListItemDisplayable items. This means that the implementation of createOpaqueViewModel can now decide what it wants to do. In this case, we chose to return a generic view model. We could also have chosen to return a different kind of view model instead, all that matters is that we always return the same type from within the function body and that the returned object conforms to ListDataSource.

Using opaque return types in your projects

While I was studying opaque return types and trying to come up with examples for this post, I noticed that it’s not really easy to come up with reasons to use opaque return types in common projects. In SwiftUI they serve a key role, which might make you believe that opaque return types are going to be commonplace in a lot of projects at some point.

Personally, I don’t think this will be the case. Opaque return types are a solution to a very specific problem in a domain that most of us don’t work on. If you’re building frameworks or highly reusable code that should work across many projects and codebases, opaque result types will interest you. You’ll likely want to write flexible code based on protocols with associated types where you, as the builder of the framework, have full control of the concrete types that are returned without exposing any generics to your callers.

Another consideration for opaque return types might be their runtime performance. As discussed earlier, protocols sometimes force the compiler to defer certain checks and lookups until runtime which comes with a performance penalty. Opaque return types can help the compiler make compile-time optimizations which is really cool, but I’m confident that it won’t matter much for most applications. Unless you’re writing code that really has to be optimized to its core, I don’t think the runtime performance penalty is significant enough to throw opaque result types at your codebase. Unless, of course, it makes a lot of sense to you. Or if you’re certain that in your case the performance benefits are worth it.

What I’m really trying to say here is that protocols as return types aren’t suddenly horrible for performance. In fact, they sometimes are the only way to achieve the level of flexibility you need. For example, if you need to return more than one concrete type from your function, depending on certain parameters. You can’t do that with opaque return types.

This brings me to quite possibly the least interesting yet easiest way to start using opaque return types in your code. If you have places in your code where you’ve specified a protocol as return type, but you know that you’re only returning one kind of concrete type from that function, it makes sense to use an opaque return type instead. In fact, the Swift team is considering inferring some whenever you use a protocol as a type in Swift 6.0. This might never make it into Swift 6.0, but it does show that the Swift team is serious about some being a good default to try whenever you can.

A more interesting consideration to make for using some is in places where you've defined a single use generic. For example, in the following situation you might be able to use some instead of a generic:

class MusicPlayer {
  func play<Playlist: Collection<Track>>(_ playlist: Playlist) { /* ... */ }
}

In this example, our play function has a generic argument Playlist that's constrained to a Collection that holds Track objects. We can write this constraint thanks to Swift 5.7's primary associated types. Learn more about primary associated types in this post. If we only use the Playlist generic in a single place like a function argument, we can use some instead of the generic from Swift 5.7 onward. Swift 5.7 allows us to use some for function arguments which is a huge improvement.

Rewriting the example above with some looks as follows:

class MusicPlayer {
  func play(_ playlist: some Collection<Track>) { /* ... */ }
}

Much better, right?

Verify your existential usage for Swift 6 with Xcode 15.3

If you want to make sure that your app is ready for Swift 6.0 and uses any or some everywhere you're supposed to, pass the -enable-upcoming-feature ExistentialAny in your Swift build flags. To learn how, take a look at this post where I dig into experimental Swift versions and features. Note that the EsistentialAny build flag is available in the default Xcode 15.3 toolchain.

In summary

In this post you saw what problems opaque return types solve, and how they can be used by showing you several examples. You learned that opaque return types can act as a return type if you want to return an object that conforms to a protocol with associated type constraints. This works because the compiler performs several checks at compile time to figure out what the real types of a protocol’s associated types are. You also saw that opaque return types help resolve so-called Self requirements for similar reasons. Next, you saw how opaque result types act as reverse generics in certain cases, which allows the implementer of a method to determine a return type that conforms to a protocol rather than letting the caller of the method decide.

Next, I gave some insights into what opaque result types are likely going to in your apps. With Swift 5.7's ability to use some in more places than just return types I think some will become a very useful tool that will help us use conrete types instead of existentials (protocols) in lots of places which should make our code more performant and robust.

If you have any questions, feedback or if you have awesome applications of opaque return types that I haven’t covered in this post, I would love to hear from you on Twitter.

Generics in Swift explained

Whenever we write code, we want our code to be well-designed. We want it to be flexible, elegant and safe. We want to make sure that Swift’s type system and the compiler catch as many of our mistakes as possible. It’s especially interesting how Swift’s type system can help us avoid obvious errors. For example, Swift won’t allow you to assign an Int to a String property like this:

var anInt = 10
anInt = "Hello, World!"

The Swift compiler would show you an error that explains that you can’t assign a String to an Int and you’d understand this. If something is declared or inferred to be a certain type, it can’t hold any types other than the one it started out as.

It’s not always that simple though. Sometimes you need to write code where you really don’t know what type you’re working with. All you know is that you want to make sure that no matter what happens, that type cannot change. If this doesn’t sound familiar to you, or you’re wondering who in their right mind would ever want that, keep reading. This article is for you.

Reverse engineering Array

A great example of an object that needs the flexibility that I described earlier is an array. Considering that arrays in Swift are created to hold objects of a specific type, whether it’s a concrete type or a protocol, array’s aren’t that different from the mistake I showed you earlier. Let’s adapt the example to arrays so you can see what I mean:

var arrayOfInt = [1, 2, 3]
arrayOfInt = ["one", "two", "three"]

If you try to run this code you will see an error that explains you can’t assign an object of Array<String> to Array<Int>. And this is exactly the kind of magic that you need generics for.

Arrays are created in such a way that they work with any type you throw at them. The only condition being that the array is homogenous, in other words, it can only contain objects of a single type.

So how is this defined in Swift? What does a generic object like Array look like? Instead of showing you the exact implementation from the Swift standard library, I will show you a simplified version of it:

public struct Array<Element> {
  // complicated code that we don’t care about right now
}

The interesting part here is between the angle brackets: <Element>. The type Element does not exist in the Swift standard library. It’s a made-up type that only exists in the context of arrays. It specifies a placeholder that’s used where the real, concrete type would normally be used.

Let’s build a little wrapper around Array that will help you make sense of this a little bit more.

struct WrappedArray<OurElement> {
  private var array = Array<OurElement>()

  mutating func append(_ item: OurElement) {
    array.append(item)
  }

  func get(atIndex index: Int) -> OurElement {
    return array[index]
  }
}

Notice how instead of Element, we use the name OurElement. This is just to prove that Element really doesn’t exist in Swift. In the body of this struct, we create an array. We do this by using its fully written type Array<OurElement>(). The same can be achieved using the following notation: [OurElement](). The outcome is the same.

Next, in the append and get methods we accept and return OurElement respectively. We don’t know what OurElement will be. All we know is that the items in our array, the items we append to it and the items we retrieve from it, will all have the same type.

To use your simple array wrapper you might write something like this:

var myWrappedArray = WrappedArray<String>
myWrappedArray.append("Hello")
myWrappedArray.append("World")
let hello = myWrappedArray.get(atIndex: 0) // "Hello"
let world = myWrappedArray.get(atIndex: 1) // "World"

Neat stuff, right! Try adding an Int, or something else to myWrappedArray. Swift won’t let you, because you specified that OurElement can only ever be String for myWrappedArray by placing String between the angle brackets.

You can create wrapped arrays that hold other types by placing different types between the angle brackets. You can even use protocols instead of concrete types:

var codableArray = WrappedArray<Codable>

The above would allow you to add all kinds of Codable objects to codableArray. Note that if you try to retrieve them from the array using get, you will get a Codable object back, not the conforming type you might expect:

var codableArray = WrappedArray<Codable>

let somethingCodable = Person()
codableArray.append(somethingCodable)
let item = codableArray.get(0) // item is Codable, not Person

The reason for this is that get returns OurElement and you specified OurElement to be Codable.

Similar to arrays, Swift has generic objects for Set (Set<Element>), Dictionary (Dictionary<Key, Value>) and many other objects. Keep in mind that whenever you see something between angle brackets, it’s a generic type, not a real type.

Before we look at an example of generics that you might be able to use in your own code someday, I want to show you that functions can also specify their own generic parameters. A good example of this is the decode method on JSONDecoder:

func decode<T>(_ type: T.Type, from data: Data) throws -> T where T : Decodable {
  // decoding logic
}

If you’ve never used this method yourself, it would normally be called as follows:

let result = try? decoder.decode(SomeType.self, from: data) // result is SomeType

Let’s pick apart the method signature for decode a bit:

  • func decode<T>: the decode method specifies that it uses a generic object called T. Again, T is only a placeholder for whatever the real type will be, just like Element and OurElement were in earlier examples.
  • (_ type: T.Type, from data: Data): one of the arguments here is T.Type. This means that we must call this method and specify the type we want to decode data into. In the example, I used SomeType.self. When you call the method with SomeType.self without explicitly specifying T, Swift can infer that T will now be SomeType.
  • throws -> T: This bit marks decode as throwing and it specifies that decode will return T. In the example, T was inferred to be SomeType.
  • where T : Decodable: this last bit of decode's method signature applies a constraint to T. We can make T whatever we want, as long as that object conforms to Decodable. So in our example, we’re only allowed to use SomeType as the type of T if SomeType is decodable.

Take another look at the method signature of decode and let it all sink in for a moment. We’re going to build our own struct in a moment that will put everything together so if it doesn’t make sense yet, I hope it does after the next section.

Applying generics in your code

You have seen how Array and JSONDecoder.decode use generics. Let’s build something relatively simple that applies your newfound logic using an example that I have run into many times over the years.

Imagine you’re building an app that shows items in table views. And because you like to abstract things and separate concerns, you have taken some of your UITableViewDataSource logic and you’ve split that into a view model and the data source logic itself. Yes, I said view model and no, we’re not going to talk about architecture. View models are just a nice way to practice building something with generics for now.

In your app you might have a couple of lists that expose their data in similar ways and heavily simplified, your code might look like this:

struct ProductsViewModel {
  private var items: [Products]
  var numberOfItems: Int { items.count }

  func item(at indexPath: IndexPath) -> Products {
    return items[indexPath.row]
  }
}

struct FavoritesViewModel {
  private var items: [Favorite]
  var numberOfItems: Int { items.count }

  func item(at indexPath: IndexPath) -> Favorite {
    return items[indexPath.row]
  }
}

This code is really repetitive, isn’t it? Both view models have similar property and method names, the only real difference is the type of the objects they operate on. Look back to our WrappedArray example. Can you figure out how to use generics to make these view models less repetitive?

If not, that’s okay. Here’s the solution:

struct ListViewModel<Item> {
  private var items: [Item]
  var numberOfItems: Int { items.count }

  func item(at indexPath: IndexPath) -> Item {
    return item[indexPath.row]
  }
}

Neat, right! And instead of the following code:

let viewModel = FavoritesViewModel()

You can now write:

let viewModel = ListViewModel<Favorite>()

The changes in your code are minimal, but you’ve removed code duplication which is great! Less code duplication means fewer surface areas for those nasty bugs to land on.

One downside of the approach is that you can now use any type of object as Item, not just Favorite and Product. Let’s fix this by introducing a simple protocol and constraining ListViewModel so it only accepts valid list items as Item:

protocol ListItem {}
extension Favorite: ListItem {}
extension Product: ListItem {}

struct ListViewModel<Item> where Item: ListItem {
  // implementation
}

Of course, you can decide to add certain requirements to your ListItem protocol but for our current purposes, an empty protocol and some extensions do the trick. Similar to how decode was constrained to only accept Decodable types for T, we have now constrained ListViewModel to only allow types that conform to ListItem as Item.

Note
Sometimes the where is moved into the angle brackets: struct ListViewModel<Item: ListItem> the resulting code functions exactly the same and there are no differences in how Swift compiles either notation.

In summary

In this blog post, you learned where the need for generics come from by looking at type safety in Swift and how Array makes sure it only contains items of a single type. You created a wrapper around Array to experiment with generics and saw that generics are placeholders for types that are filled in at a later time. Next, you saw that functions can also have generic parameters and that they can be constrained to limit the types that can be used to fill in the generic.

To tie it all together you saw how you can use generics and generic constraints to clean up some duplicated view model code that you actually might have in your projects.

All in all, generics are not easy. It’s okay if you have to come back to this post every now and then to refresh your memory. Eventually, you’ll get the hang of it! If you have questions, remarks or just want to reach out to me, you can find me on Twitter.