Updating your apps with silent push notifications

A lot of apps rely on data from a remote source to display their content. Sometimes the content on the remote source changes regularly, sometimes it changes only sporadically. In all cases, your users will want to see the most up to date version of the information that you have to offer them, and in most cases, you will do your best to make sure that your users always have up to date information.

There are many ways to make sure your users always see the latest data from your server. For example, you might be using web sockets if your data updates with very high frequency. If your data updates less frequently, or if you want to be able to fetch new data on-demand when your app is running in the background, you might want to use silent push notifications, which is exactly what this week’s Quick Tip is all about.

In a nutshell, with silent push notifications, your server can notify your app that it has new data available. Your app will be woken up if it’s in the background and a special AppDelegate method is called allowing you to retrieve and store this new data. Let’s go into a little bit more detail, shall we?

Configuring your app for silent push notifications

To receive silent push notifications you need to add the Push Notifications and Background Modes capabilities to your project. Make sure to check the Remote Notifications checkbox under the Background Modes header as shown in the following screenshot:

Screenshot of background modes enabled

Next, you need to register your application for remote notifications. Place the following code in your AppDelegate’s application(_:didFinishLaunchingWithOptions:) method:

UIApplication.shared.registerForRemoteNotifications()

Note that calling registerForRemoteNotifications() does not trigger the notification permissions popup. It only registers the current device on Apple’s notification servers. Next, implement application(_:didRegisterForRemoteNotificationsWithDeviceToken:) like you would for normal push notifications. Send the device token to a web server, or extract the device token from the data object for debugging using the following code:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
  let token = deviceToken.reduce("") { $0 + String(format: "%02.2hhx", $1) }
  print("registered for notifications", token)
}

The last step is to implement application(_:didReceiveRemoteNotification:fetchCompletionHandler:). This method is called when your application receives a remote notification. You will perform your background updates in this method. The following code is a very simple example of fetching data from a server and calling the fetchCompletionHandler:

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {

  URLSession.shared.dataTask(with: URL(string: "https://donnywals.com")!) { data, response, error in
    print(data)
    completionHandler(.newData)
  }.resume()
}

In your own applications, you will likely make a request to a more relevant URL and store the data that you fetch from that URL in UserDefaults, Core Data, a file on disk or somewhere else.

Note the following line of code completionHandler(.newData). When your app is launched in the background, you need to make sure that you call your completionHandler in a timely manner to avoid getting cut off or penalized if your task takes too long, or never completes. You can expect to have roughly 30 seconds of background execution time when fetching data in response to a silent push notification. If your task failed, call the handler with a .failed argument. If you attempted to fetch data but didn’t receive any, call the handler with .noData so iOS can prioritize future work for your app appropriately.

Now that you know how to prepare your app for silent push notifications, let’s have a brief look at what you need to do on the server to send a silent push notification.

Configuring your server for silent push notifications

Your server-side configuration is very similar to how you’d set it up for normal notifications. You can use an external service like Firebase Cloud Messaging if you want.

What’s most important is that you include the following in your push notification payload:

  • content_available: 1 in your notification payload
  • apns-push-type = background in your notification header
  • apns-priority = 5 in your notification header

I won’t go into the details of how you can build your notification exactly as this will vary per push provider or if you’re doing it manually.

Including content_available: 1 in your payload tells iOS that this is a notification that you sent because there is new content on the server. Without this, your application(_:didReceiveRemoteNotification:fetchCompletionHandler:) method will never be called.

The apns-push-type = background header is only required on watchOS but Apple recommends that you include it in your silent pushes anyway. And lastly, the apns-priority = 5 header tells iOS that it should launch your app in the background when this push is sent. If you don’t include the priority header, your app will not be launched so make sure to include this in your notification payload.

If everything is configured correctly, you now have a powerful tool at your fingertips to make sure your application always has the most up to date data available for your users!

In summary

In this Quick Tip, you saw how you can configure both your app and remote notifications to support silent push notifications or on-demand background refresh. Note that you should not try to abuse this feature. Apple trusts that you will push updates responsibly and in moderation. Abuse of silent push might be penalized, for example, Apple will heavily throttle the number of times it will wake your app up in response to silent push notifications. Apple themselves recommend that you don’t send more than two or three silent pushes per hour.

Setting up silent push notifications isn’t a terribly complex process but it provides your app with the superpower of always being up to date! And again, with great power comes great responsibility…

If you have any questions, comments or feedback for me you can always reach out to me on Twitter

Real time data exchange using web sockets in iOS 13

Apps send and receive data all the time. Some apps mostly read data from the network, others are more focussed on sending data to servers. Depending on your needs, you might need to be able to send data to a server as quickly as possible, or maybe you need to receive new data as soon as it’s available from a server. Every app has different needs and several mechanisms exist to streamline network communication.

In this post week’s blog post, I will focus on one specific use of networking in apps. We’ll look at using web sockets to receive data as soon as it becomes available on the server. We’ll explore some of the available alternatives and how you can use web sockets with URLSession in iOS 13.

Are you excited? I know I am! Let’s get started then.

Receiving updates from a web server

The simplest and possibly most well-known way to receive updates from a server is by making a request to the server with URLSession. While it’s well-known, it’s also quite possibly the least efficient way to get real-time updates. The server only sends your app new data when it asks for new data. This means that your app should either go to the server many times to get data as soon as possible, which means that a lot of requests might come back without new data. Or alternatively, you might make fewer requests which means that it takes longer for the new data to reach your apps. This process of retrieving new data from a server is called polling.

Another way to retrieve new data from a server with URLSession is by implementing silent push notifications in your app. Your server can send a push notification that includes the content-available key in its apns payload to trigger a special AppDelegate method in your app. You can then request new data from the server and know that you will always receive new data. This is really efficient if your app limits the number of push notifications it sends by grouping updates together. If your server processes a lot of changing data, this might mean that your app receives lots of silent push notifications, which results in lots of requests to your server. Batching the notifications that are sent to your app will limit the number of requests made, but it will also introduce a delay between data becoming available on the server and your app knowing about it.

An alternative to polling and silent push notifications is long-polling. When you implement long-polling in an app, you make a request to a web server and leave the connection open for an extended period of time. Much longer than you normally would when making a request for new data. The server won’t respond to your application’s request until it has new data. Once new data is received, you can make a new long-polling request to receive the next bit of data.

Retrieving data through long-polling has the advantage of receiving new data as soon as it’s available. A potential downside of this approach is that your server will have to maintain many open connections. A properly configured server shouldn’t have too much trouble doing this though, so for receiving data long-polling is actually a pretty decent solution. A real downside of long polling is that you can’t send data over the active connection. This means that you’ll need to send a new request every time you want to send data from your app to the server. And again, in a rapidly changing environment, this could mean that your server will receive lots of incoming requests.

What if there was a way to use a single connection that can be kept active and is used to send and receive data at the same time? Well, you’re in luck. That’s exactly what a web socket does! Let’s see how you can use web sockets in your iOS 13+ apps using URLSession.

Using web sockets to send and receive data

In order to understand what we’re looking at in this blog post, let’s take a quick step back and learn about web sockets from a very high level. Once you have a rough overview of how web sockets work, we’ll look at using them in an app.

Understanding web sockets

Web sockets can be considered an open connection between a client and a server. They can send each other messages, typically these messages are short to make sure they’re sent and received as quickly as possible. So while it’s possible to send a complicated JSON payload with several items over a web socket connection, it’s more likely that you’re going to send each item in your payload individually.

It might seem a little bit counter-intuitive to send and receive many smaller payloads rather than a single bigger payload. After all, when you make requests to a server in a normal setting, you want to make sure you don’t make too many requests and favor a single larger request over several small requests, depending on your exact requirements, of course.

When you’re dealing with web sockets, you don’t send requests. You’re sending messages. And since you’re already connected to the server, you don’t incur any extra overhead when you send many small messages to the server. And since your ultimate goal when using a web socket is often communication with as little latency as possible, it makes sense to send any messages you have for the server as soon as possible in a package that is as small as possible so it can be sent over the network quickly.

The same is true for server-to-app communication. A server that supports web sockets will often prefer to send your app small messages with data as soon as the data becomes available rather than batching up several messages and sending them all at once.

So when we implement web sockets in an app, we need to be prepared to:

  • Send messages as soon as we can and keep them as small as possible.
  • Receive many messages in rapid succession.

Based on these two points, you should be able to decide whether web sockets are a good fit for the task you’re trying to accomplish. For example, uploading a large high-resolution image is not typically a good task for a web socket. The image data can be quite large and the upload might take a while. You’re probably better off using a regular data task for this.

If your app has to sync a large amount of data all at once, you might also want to consider using a separate request to your server to make sure your socket remains available for the quick and short messaging it was designed to handle. Of course, the final decision depends on your use case and trial and error might be key to finding the optimal balance between using web sockets and using regular URL requests in your app.

Now that you know a bit more about web sockets, let’s find out how they work in an iOS application, shall we?

Using web sockets in your app

Using web sockets in your app is essentially a three-step process:

  1. Connecting to a web socket
  2. Send messages over a web socket connection
  3. Receiving incoming messages

Let’s go over each step individually to implement a very simple connection that will send and receive simple messages as either JSON data or a string.

Connecting to a web socket

Establishing a connection to a web socket is done similar to how you make a regular request to a server. You make use of URLSession, you need a URL and you create a task that you must resume. When you make a regular request, the task you need is usually a dataTask. For web sockets, you need a webSocketTask. It’s also important that you keep a reference to the web socket task around so you can send messages using that same task at a later moment. The following code shows a simple example of this:

var socketConnection:  URLSessionWebSocketTask?

func connectToSocket() {
  let url = URL(string: "ws://127.0.0.1:9001")!
  socketConnection = URLSession.shared.webSocketTask(with: url)
  socketConnection?.resume()
}

Note that the URL that’s used for the socket connection is prefixed with ws://, this is the protocol that’s used to connect to a web socket. Similar to how http:// and https:// are protocols to connect to a web server when making a regular request.

After obtaining the web socket task, it must be resumed to actually connect to the web socket server.

Next up, sending a message over the web socket connection.

Sending message over a web socket connection

Depending on your app and the kind of data you wish to send over the web socket, you might want to send strings or plain data objects. Both are available on the Message enum that is defined on URLSessionWebSocketTask as a case with an associated value. Let’s look at sending a string first:

func sendStringMessage() {
  let message = URLSessionWebSocketTask.Message.string("Hello!")

  socketConnection?.send(message) { error in
    if let error = error {
      // handle the error
      print(error)
    }
  }
}

The preceding code creates a message using the string version of URLSessionWebSocketTask.Message. The existing socket connection is then used to send the message. The send(_:) method on URLSessionWebSocketTask takes a closure that is called when the message is sent. The error is an optional argument for the closure that will be nil if the message was sent successfully.

Let’s see how sending data over the socket is different from sending a string:

func sendDataMessage() {
  do {
    let encoder = JSONEncoder()
    let data = try encoder.encode(anEncodableObject)
    let message = URLSessionWebSocketTask.Message.data(data)

    socketConnection?.send(message) { error in
      if let error = error {
        // handle the error
        print(error)
      }
    }
  } catch {
    // handle the error
    print(error)
  }
}

The preceding code uses a JSONEncoder to encode an Encodable object into a Data object and then it creates a message using the data case of URLSessionWebSocketTask.Message. The rest of the code is identical to the version you saw earlier. The message is sent using the existing socket connection and the completion closure is called once the message has been delivered or if an error occurred.

This is pretty straightforward, right? Nothing fancy has to be done to send messages. We just need to wrap our message in a URLSessionWebSocketTask.Message and send it using an existing web socket connection.

Let’s see if receiving messages is just as nice as sending messages is.

Receiving incoming messages

Any time our server has new data that we’re interested in, we want to receive this new data over the web socket connection. To do this, you must provide the web socket task with a closure that it can call whenever incoming data is received.

The web socket connection will call your receive closure with a Result object. The Result’s success type is URLSessionWebSocketTask.Message and the Failure type is Error.

This means that we can either get a string or data depending on the contents of the message. The following code shows how you can set up the receive closure, and how you can distinguish between the two different types of messages you can receive.

func setReceiveHandler() {
  socketConnection?.receive { result in
    defer { self.setReceiveHandler() }

    do {
      let message = try result.get()
      switch message {
      case let .string(string):
        print(string)
      case let .data(data):
        print(data)
      @unknown default:
        print("unkown message received")
      }
    } catch {
      // handle the error
      print(error)
    }
  }
}

Similar to sending messages, receiving them isn’t terribly complex. Since the receive closure can receive either a string or data, we must make sure that the web socket server only sends us responses we expect. It’s important to coordinate with your server team, or yourself, that you can handle unexpected messages and that you only send strings or data in a pre-defined format.

Note the defer statement at the start of the receive handler. It calls self.setReceiveHandler() to reset the receive handler on the socket connection to allow it to receive the next message. Currently, the receive handler you set on a socket connection is only called once, rather than every time a message is received. By using a defer statement, you make sure that self.setReceiveHandler is always called before exiting the scope of the receive handler, which makes sure that you always receive the next message from your socket connection.

If you want to use a JSONDecoder to decode that data you’ve received from the server, you need to either attempt to decode several different types of objects until an attempt succeeds, or you need to make sure that you can only receive a single type of data over a certain web socket connection.

Personally, I would recommend to always make sure that you send a single type of message over a socket connection. This allows you to write robust code that is easy to reason about.

For example, if you’re building a chat application, you’ll want to make sure that your web socket only sends and receives instances of a ChatMessage that conforms to Codable. You might even want to have separate sockets for each active chat a user has. Or if you’re building a stocks application, your web sockets will probably only receive StockQuote objects that contain a stock name, timestamp, and a current price.

If you make sure that your messages are well-defined, URLSession will make sure that you have a convenient way of using your web socket.

In Summary

Web sockets are an amazing technology that, as you now know, allow you to communicate with a server in real-time, using a single active connection that can be used to both send and receive messages. You know that the big advantage here is that there is no overhead of making requests to a server every time you want to either send or receive data.

You also saw how you can use URLSession’s web socket task to connect to a web socket, send messages and receive them by using very straightforward and convenient APIs. Lastly, you learned that you must pre-define the messages you want to send and receive over your web socket to make sure all components involved understand what’s going on.

I personally really like web sockets, especially because you can use them to build experiences that feel like magic. I once used web sockets to build an app that could be controlled through a companion website by sending messages over a web socket, it was really cool to see the app reflect the things I was doing on the website.

If you have any questions about this blog post, have feedback or anything else, don’t hesitate to reach out to me on Twitter!

Thanks to Kristaps Grinbergs for pointing out that receive handlers are always called once 🙌🏼.

Announcing: Advent of Swift

December has always been a month of sharing for me. Sharing food and experiences with family, celebrating the end of what has (hopefully) been a wonderful year, reflecting and setting goals for the new year. And this year, I have decided to share something really special with the iOS community.

In December I will publish a new blog post every day until Christmas eve. That's 24 blog posts in total. And since I want this whole endeavor to be about the community itself, I need your input. Send me messages on Twitter to let me know what you want to learn and I will do my best to cover your topic in December during Advent of Swift.

Of course, I won't be able to cover every question you send me. But rest assured, I won't ignore any of your suggestions. If your post doesn't make the cut for Advent of Swift, I'm definitely keeping it around on my list of ideas for future blog posts and will do my best to cover your suggestion in 2020.

Let's make Advent of Swift 2019 a great success together! 🙌🏼

Note
Make sure you don't miss any posts by subscribing to my newsletter using the form below this post.

Configuring projects with xcconfig

Sometimes you want to be able to install two versions of your app side by side, for example, a development version and a release version that show up as individual apps by giving them different bundle identifiers. And maybe they should also use different versions of your REST API depending on the type of build you're using. In this week's Quick Tip I will show you how you can manage this using xcconfig files.

Creating and using your xcconfig file

To create an xcconfig file, choose File -> New -> File... in your project. In the new file dialog, scroll down until you see the Configuration Settings File in the Other section.

New File Dialog

After choosing an appropriate name (like debug.xcconfig for example) you can open your xcconfig file in Xcode and add new configuration rules to it. An xcconfig file should contain key and value pairs in the following format:

PRODUCT_BUNDLE_IDENTIFIER = com.xcconfig.sample
PRODUCT_NAME = Config Example

You can add configurations for pretty much anything you want. A good way to find out what key should be used for specific purposes you can open your xcodeproj file in a text editor and look for the settings you want.

To make Xcode apply your xcconfig file, go to your app's project settings, look for the build type you want your configuration to apply to, and select your configuration as shown in the following screenshot:

Selected build configuration

After doing this, Xcode might not apply your configuration for all keys you've added in your xcconfig. If this is the case, go to your projects Build Settings and enable the Levels view. The fields that are marked in green are what Xcode will use while building your app. If Xcode uses the wrong field, you can select it and clear it so Xcode will look for the next best fit. The following screenshot shows this:

Xcode Levels view

The number of options you can configure like this are numerous and the Levels view is extremely helpful in figuring out what values Xcode uses for certain configuration keys. Next up, using your xcconfig file to configure your app's runtime.

Using xcconfig and your Info.plist to configure your app

If you want your app to use different settings for different builds, like for instance use a development endpoint of your REST API, you can use your Info.plist and xcconfig file to set this up. All you need to do is add a new key to your config, use a config key as the value and read it in your app. For example, you might have the following key in your xcconfig:

API_ROOT = dev.myapp.com

You can use this config value by adding a key to your Info.plist and using $(API_ROOT) as the value. You can then proceed to read this key from your Info.plist just like you would any other key. Nifty, right?

In Summary

In this week's Quick Tip you learned how you can use an xcconfig file to drive build time configurations of your app. At compile time, all values that are in your xcconfig are applied to your project. This allows you to specify a special bundle identifier, product name, development team and even code signing strategy that will be applied to any build configuration that you want.

You can even use the xcconfig file to add different values to your Info.plist file so you can configure certain runtime features for your app. All in all, a very powerful feature that I personally use in many projects. If you have any questions, compliments or feedback, don't hesitate to find and message me on Twitter

Building flexible components with generics and protocols

Recently I wanted to build a generic data source layer. This data source would be able to return pretty much anything from a local cache, or if the local cache doesn't contain the requested object, it would fetch the object from a server and then cache the result locally before returning it to me. To achieve this, I figured that I would write a generic local cache, a generic remote cache and a wrapper that would combine both caches, allowing me to transparently retrieve objects without having to worry about where the object came from.

It didn't take long before I saw the first compiler warnings and remembered that generics can be extremely hard to bend to your will. Especially if your generic object uses other generic objects to transparently do generic things.

In this blog post, I will show you my approach to tackling complicated problems like this, and how I use pseudo code to design and implement a fluent API that works exactly as I wanted. In this blog post we'll go over the following topics:

  • Designing an API without getting lost in the details
  • Knowing how simple generics are used in Swift, and how you can use them in your code.
  • Understanding you can have protocols with generic requirements, also known as associated types.
  • Combining generics with protocols that have associated types

Are you ready to enter the brain-melting world of generics? Good, let's go!

Designing an API without getting lost in the details

I promised you generics. Instead, I'm going to show you how to design an API that uses generics first. This is to establish a goal, something we can work towards throughout this blogpost. Generics are complicated enough as they are and I don't want you to get confused to the point where you're not sure what we were building again.

In the introduction of this post, I mentioned that I wanted to build a generic data store that cached data locally, and would use a remote data store as a back up in case the required data didn't exist locally.

A good way to get started building something like this is to write down some pseudo-code that demonstrates how you would like to use the API or component you're building. Here's what I wrote for the caching layer:

let localDataStore = UserDataStore()
let remoteDataStore = UserApi()
let dataStore = CacheBackedDataStore(localDataStore, remoteDataStore)

dataStore.fetch(userID) { result in 
  // handle result
}

This is pretty straightforward, right? You can see that I want to create two stores and a wrapping store. The wrapping store is the one that's used to retrieve information and it uses a callback to inform the caller about the results. Simple and straightforward, just how I like it. Keep in mind that whatever we design has to work with more than user objects. We also want to be able to store other information in this structure, for example, documents that belong to the user.

Let's dive a bit deeper and write a pseudo-implementation for CacheBackedDataStore:

class CacheBackedDataStore {
  let localStore: LocalStore
  let remoteStore: RemoteStore

  func fetch(_ identifier: IdentifierType, completion: @escaping Result<T, Error>) {
    localStore.fetchObject(identifier) { result in 
      if let result = try? result.get() {
         completion(.success(result))
      } else {
        remoteStore.fetchObject(identifier) { result in 
          if let result = try? result.get() {
            completion(.success(result))
          } else {
            // extract error and forward to the completion handler
          }
        }
      }
    }
  }
}

You might notice the type T on the result here. This type T is where our generic adventure begins. It's the start of a rabbit hole where we're going to turn everything into objects that could be anything. At this point, we have enough "design" to get started with setting up some of our building blocks. To do this, we're going to have a look at generics in Swift.

Adding simple generics to your code

In the pseudo-code design that I showed you in the previous section, I used the type T. Whenever we write code with generics in Swift, we typically use T to flag a type as generic. A generic type can be pretty much anything as long as it satisfies the constraints that are specified for it. If we don't specify any constraints, T can be anything you want. An example of a generic type that can be anything you want is Array. Let's look at two identical ways to define an empty array in Swift:

let words1 = [String]()
let words2 = Array<String>()

Notice that the second way uses the type name Array followed by <String>. This informs the compiler that we're defining an array where the type of element is String. Now let's try to imagine what the type definition for Array might look like:

struct Array<T> {
  // implementation code
}

This code declares a struct of type Array that contains some type T that is generic; it could be anything we want, as long as we specify it when creating an instance or when we use it as a type. In the earlier example, let words2 = Array<String> we defined T to be of type String. Let's look at one more basic example before we move on:

struct SpecializedPrinter<T> {
  func print(_ object: T) {
    print(object)
  }
}

This code declares a SpecializedPrinter that's generic over T and it has a function called print, that takes an object of type T and prints it to the console. If you paste the above into a Playground, you can use this SpecializedPrinter struct as follows:

let printer = SpecializedPrinter<String>()
printer.print("Hello!") // this is fine
printer.print(10) // this is not okay since T for this printer is String, not Int

Now that you know a bit about generics, I think we can write the first bit of code for the CacheBackedDataSource object:

struct CacheBackedDataSource<T> {
  func find(_ objectID: String, completion: @escaping (Result<T?, Error>) -> Void) {

  }
}

We're not doing much here, but it's an important milestone in your journey to mastering generics in Swift. You have written a data source that claims to cache any type (T) and will do an asynchronous lookup for an item based on a string identifier. The find(_:completion:) function will call the completion block with a Result object that contains an optional instance of T, or an Error object.

In the pseudo-code from earlier in this post, there were two properties:

let localStore: LocalStore
let remoteStore: RemoteStore

Since the caching layer should be as generic and flexible as possible, let's define LocalStore and RemoteStore as protocols. This will give us tons of flexibility, allowing any object to act as the local or remote store as long as they implement the appropriate functionality:

protocol LocalStore {

}

protocol RemoteStore {

}

And in these protocols, we will define methods to fetch the object we need, and in the local store, we'll define a method that persists an object.

protocol LocalStore {
  func find(_ objectID: String, completion: @escaping (Result<T, Error>) -> Void)
  func persist(_ object: T)
}

protocol RemoteStore {
  func find(_ objectID: String, completion: @escaping (Result<T, Error>) -> Void)
}

Unfortunately, this doesn't work. Our protocols don't know what T is since they're not generic. So how do we make these protocols generic? That's the topic of the next section.

Adding generics to protocols

While we can define a generic parameter on a struct by adding it to the type declaration between <>, just like we did for struct CacheBackedDataSource<T>, this is not allowed for protocols. If you want to have a protocol with a generic parameter, you need to declare the generic type as an associatedtype on the protocol. An associatedtype does not have to be implemented as a generic on objects that implement the protocol. I will demonstrate this shortly. For now, let's fix the local and remote store protocols so you can see associatedtype in action:

protocol LocalStore {
  associatedtype StoredObject

  func find(_ objectID: String, completion: @escaping (Result<StoredObject, Error>) -> Void)
  func persist(_ object: StoredObject)
}

protocol RemoteStore {
  associatedtype TargetObject

  func find(_ objectID: String, completion: @escaping (Result<TargetObject, Error>) -> Void)
}

Notice how we're not using a short name like T here. This is because the associated type does not necessarily have to be generic, and we want the purpose of this type to be communicated a bit better than we typically do when you're defining a generic parameter on a struct. Let's create two structs that we conform to LocalStore and RemoteStore to see how associatedtype works in the context of objects that conform to our protocols.

struct ArrayBackedUserStore: LocalStore {
  func find(_ objectID: String, completion: @escaping (Result<User, Error>) -> Void) {

  }

  func persist(_ object: User) {

  }
}

struct RemoteUserStore: RemoteStore {
  func find(_ objectID: String, completion: @escaping (Result<User, Error>) -> Void) {

  }
}

All that's needed to implement the protocol's associatedtype in this example is to use the same type in all places where the protocol uses its associated type. An alternative that's a bit more verbose would be to define a typealias inside of a conforming object and use the protocols associatedtype where we currently use the User object. An example of this would look like this:

struct RemoteUserStore: RemoteStore {
  typealias TargetObject = User

  func find(_ objectID: String, completion: @escaping (Result<TargetObject, Error>) -> Void) {

  }
}

I prefer the former way where we use User in place of TargetObject, it's just easier to read in my opinion.

Since we're dealing with data that comes from a remote server in RemoteUserStore, it would be quite convenient to constraint the value of TargetObject to only allow Decodable types to be used in place of TargetObject. We can do this as follows:

protocol RemoteStore {
  associatedtype TargetObject: Decodable

  func find(_ objectID: String, completion: @escaping (Result<TargetObject, Error>) -> Void)
}

If we try to use User in place of TargetObject and User isn't Decodable, we're shown the following error by the compiler:

candidate would match and infer 'TargetObject' = 'User' if 'User' conformed to 'Decodable'
func find(_ objectID: String, completion: @escaping (Result<User, Error>) -> Void) {

We now have the following code prepared for the CacheBackedDataSource and the local and remote store protocols:

struct CacheBackedDataSource<T> {
  func find(_ objectID: String, completion: @escaping (Result<T, Error>) -> Void) {

  }
}

protocol LocalStore {
  associatedtype StoredObject

  func find(_ objectID: String, completion: @escaping (Result<StoredObject, Error>) -> Void)
  func persist(_ object: StoredObject)
}

protocol RemoteStore {
  associatedtype TargetObject: Decodable

  func find(_ objectID: String, completion: @escaping (Result<TargetObject, Error>) -> Void)
}

Let's add some properties for the local and remote store to the CacheBackedDataStore:

struct CacheBackedDataSource<T> {
  let localStore: LocalStore
  let remoteStore: RemoteStore

  func find(_ objectID: String, completion: @escaping (Result<T, Error>) -> Void) {

  }
}

Unfortunately, this won't compile. The following errors are thrown by the Swift compiler:

error: protocol 'LocalStore' can only be used as a generic constraint because it has Self or associated type requirements
  let localStore: LocalStore
                  ^

error: protocol 'RemoteStore' can only be used as a generic constraint because it has Self or associated type requirements
  let remoteStore: RemoteStore
                   ^

Let's see how we can fix this error in the next session.

Using a protocol with associated type requirements as a generic constraint

Before we look deeper into the compiler errors we're currently stuck with I want to quickly recap what we've got prepared so far. Because even though the code doesn't compile, it's quite impressive already. We have a generic data source that can retrieve any object T.

We also have a protocol for a local store that caches any type we want, the adopter of the protocol can decide what type is cached exactly. All that matters is that the object that implements the protocol has a find method that performs a lookup based on an identifier and invokes a callback with a Result object. It also has a persist method that is expected to store objects that have the same type as the type object that the local store can fetch.

Lastly, we have a protocol for a remote store that fetches any kind of object, as long as it conforms to Decodable. Similar to how local store works, the implementer of the RemoteStore can decide what the type of the TargetObject will be.

This is really powerful stuff and if the above is a bit confusing to you, that's okay. It's not simple or straightforward even though the code looks fairly simple. Try following along with the code we've written so far, re-read what you've learned and maybe take a short break to let it sink in. I'm sure it will eventually.

In order to use the local and remote store protocols as types on the CacheBackedDataSource, we need to add generic parameters to the CacheBackedDataSource, and constrain these parameters so they have to implement our protocols. Replace your current implementation of CacheBackedDataSource with the following:

struct CacheBackedDataSource<Local: LocalStore, Remote: RemoteStore> {
  private let localStore: Local
  private let remoteStore: Remote

  func find(_ objectID: String, completion: @escaping (Result<Local.StoredObject, Error>) -> Void) {

  }
}

The declaration of CacheBackedDataSource now has two generic parameters, Local and Remote. Each has to conform to its respective protocol. This means that the localStore and remoteStore should not be of type LocalStore and RemoteStore. Instead, they should be of type Local and Remote. Note that Result<T, Error> has been replaced with Result<Local.StoredObject, Error>. The find method now uses whatever type of object the LocalStore stores as the type for its Result. This is really powerful because the underlying store now dictates the type of objects returned by the data source object.

There's still one problem though. Nothing prevents us from locally storing something that's completely incompatible with the remote store. Luckily we can apply constraints to the generic parameters of our struct. Update the declaration of CacheBackedDataSource as follows:

struct CacheBackedDataSource<Local: LocalStore, Remote: RemoteStore> where Local.StoredObject == Remote.TargetObject

We can now only create CacheBackedDataSource objects that use the same type of object for the local and remote stores. Before I show you how to create an instance of CacheBackedDataSource, let's implement the find method first:

func find(_ objectID: String, completion: @escaping (Result<Local.StoredObject, Error>) -> Void) {
  localStore.find(objectID) { result in
    do {
      let object = try result.get()
      completion(.success(object))
    } catch {
      self.remoteStore.find(objectID) { result in
        do {
          let object = try result.get()
          self.localStore.persist(object)
          completion(.success(object))
        } catch {
          completion(.failure(error))
        }
      }
    }
  }
}

The find method works by calling find on the local store. If the requested object is found, then the callback is invoked and the result is passed back to the caller. If an error occurred, for example, because the object wasn't found, the remote store is used. If the remote store finds the requested object, it's persisted in the local store and the result is passed back to the caller. If the object wasn't found or an error occurred in the remote store, we invoke the completion closure with the received error.

Note that this setup is extremely flexible. The implementation of CacheBackedDataSource doesn't care what it's caching. It only knows how to use a local store with a fallback to a remote store. Pretty awesome, right? Let's wrap this up by creating an instance of the CacheBackedDataSource:

let localUserStore = ArrayBackedUserStore()
let remoteUserStore = RemoteUserStore()
let cache = CacheBackedDataSource(localStore: localUserStore, remoteStore: remoteUserStore)
cache.find("someObjectId") { (result: Result<User, Error>) in

}

All you need to do is create instances of your stores, and supply them to the cache. You can then call find on your cache and the compiler is able to understand that the result object that's passed to the completion closure for find is a Result<User, Error>.

Take a look at the pseudo-code I showed you at the beginning of this post. It's very close to what we ended up implementing, and it's just as powerful as we imagined! If you've been following along, try to create some CacheBackedDataSource objects for other types. It should be fairly straightforward.

In summary

You have learned so much in this blog post. I wouldn't be surprised if you have to read it one or two more times to make complete sense out of all these generics and type constraints. And we haven't even covered all of it! Generics are an unbelievably powerful and complex feature of the Swift language but I hope that I have been able to help you make some sense of them. Overall, you now know that adding <T> to an object's declaration adds a generic parameter, which means that anytime you use T inside of that object, it's whatever the user of that object decided it to be.

You also learned that you can add associatedtype to a protocol to have it support generic types. And to top it off, you learned how you can use a protocol that has an associated type as a constraint for an object's generic parameter for maximum flexibility. If your brain hurts a bit after reading all this then again, don't worry. This stuff is hard, confusing, weird and complex. And if you have any questions, comments or need somebody to talk to because you feel lost now, don't hesitate to reach out on Twitter!

Add iOS 12 support to a new Xcode 11 Project

When you create a new project in Xcode 11, you automatically get the new SceneDelegate for free. This is great if you want to build an app that's for iOS 13 and newer but as soon as you change your deployment target to an iOS version that's lower than iOS 13, your app will have trouble compiling. In this Quick Tip, I will show you how to update your project in order to make it compile for iOS 12 and below. You will first learn how to use the SceneDelegate for iOS 13 and up, and use the AppDelegate as a fallback for older versions of iOS. After that, I will show you how to opt-out of using the SceneDelegate completely if you're absolutely sure that you're not interested in any of its benefits for iOS 13.

Making the existing template work for iOS 12

Since the UIWindowSceneDelegate is only available in iOS 13 and up, we'll need to exclude the entire SceneDelegate object if the app is compiled for iOS 12 or below. To do this, add an @available annotation to the SceneDelegate class as shown in the following code snippet:

@available(iOS 13, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {

This ensures that the SceneDelegate is not included in iOS 12 builds. Next, go to the AppDelegate and add the same @available annotation to application(_:configurationForConnecting:options) and application(_:didDiscardSceneSessions) as shown in the following code snippet:

@available(iOS 13, *)
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
  // ...
}

@available(iOS 13, *)
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
  // ...
}

Lastly, since applications that use storyboards must a window property defined on the AppDelegate for iOS 12 and below, add the following property to the AppDelegate:

var window: UIWindow?

This will allow iOS to instantiate the Main.storyboard and use it to create and populate the window object on iOS 12. For iOS 13 and above the window will remain unset because the SceneDelegate will set its own window on those versions of iOS.

Your app should be able to run on iOS 12 and 13 now. One last thing you might want to add to application(_:didFinishLaunchingWithOptions:) is the following:

if #available(iOS 13, *) {
  // do nothing / do things that should only be done for iOS 13
} else {
  // do iOS 12 specific window setup
}

This will allow you to do some version dependent set up in your AppDelegate.

Keep in mind that now that you have the AppDelegate for iOS 12 and the SceneDelegate for iOS 13 you have different entry points in your code for different iOS versions. This means that you might have to do some special setup or apply duplicated logic in the AppDelegate or SceneDelegate depending on what you need to achieve. If you don't want to do this, you can also opt-out of the SceneDelegate completely. Let's see how.

Opting out of the SceneDelegate completely

To opt-out of using the SceneDelegate in your project, you should take the following steps:

  1. Begin by deleting the SceneDelegate.swift file from your project.
  2. Next, open AppDelegate.swift and remove all scene related methods from that file.
  3. Lastly, you'll need to open your Info.plist and remove the Application Scene Manifest key from that file.

Three simple steps and you're able to work on your project in the same way you used to do in Xcode 10. Very nice.

In summary

In this Quick Tip, you've learned how to add a couple of clever @available and if #available statements to your code to make sure your projects work on iOS 12 and below. While this isn't a lot of work, I think it's still somewhat inconvenient that we have to do this to make Xcode 11 work for in a very reasonable scenario. Supporting older iOS versions is something a lot of developers have to do so it would have been nice to see Apple accommodate this when creating a new project in Xcode. I would recommend against using the approach of opting out of the SceneDelegate entirely because it has some awesome advantages that I describe in my posts Understanding the iOS 13 Scene Delegate and Adding support for multiple windows to your iPadOS app.

When you deploy your app to iOS 12 and use the SceneDelegate for iOS 13, you will run into some come duplication between the AppDelegate for iOS 12 and the SceneDelegate for iOS 13. I chose to omit suggestions to make this manageable because I really wanted to keep this post short. I'm sure you can come up with some way to encapsulate the duplicated logic in some kind of helper object that you can use to configure your app in either the AppDelegate or the SceneDelegate. I might write something about this in the future. Make sure to follow me on Twitter. Don't hesitate to reach out to me if you have any questions or suggestions for me!

When to use weak self and why

We all want to write good, beautiful and stable code. This includes preventing memory leaks, which we can, using [weak self] when writing a closure that needs access to self. But what's the real reason for needing this weak capture? And do we need it all the time? In this week's Quick Tip, I want to help you find an answer to this question so you can make more informed decisions about your capture lists in the future.

This post contains the following topics:

  • Understanding what a capture list is
  • Understanding different kinds of captures
  • Knowing when to rely on implicit or strong captures
  • Deciding whether you need a weak or an unowned self capture

Enjoy!

Understanding what a capture list is

When you write a closure, it will implicitly capture all properties referenced inside of the closure. Let's look at a short code sample to illustrate this:

func multiply(by multiplier: Int) -> ((Int) -> Int) {
  return { (input: Int) -> Int in
    return input * multiplier
  }
}

var multiplier = 2
let multiplyTwo = multiply(by: multiplier) // you can now call multiplyTwo as if it's a function
multiplier = 4
let multiplyFour = multiply(by: multiplier) // you can now call multiplyFour as if it's a function

The preceding code sample shows a function that takes a multiplier and returns a closure that takes a different input and multiplies it with the multiplier that was passed to multiply(by:). The closure that is returned from this function captures the multiplier and uses it as the multiplier for the input you give it. In practice, this means that calling multiplyTwo(2) will return 4 and multiplyFour(2) returns 8. The multiplier variable that is defined does not affect the closure held by multiplyTwo or multiplyFour because of this capture behavior.

I know that this can be quite confusing when you've just started learning about closures, but bear with me as we go through some more examples.

Take a look at the following example:

var name = "Donny"
var appendToName = { (string: String) -> String in
  return name.appending(string)
}

let one = appendToName("Wals")
name = "D"
let two = appendToName("Wals")

What would you expect the values of one and two to be? Remember that I just explained how closures capture properties that are used inside of a closure.

If you expect one and two to both be "DonnyWals" I don't blame you, it seems to make sense! But unfortunately, this isn't correct. The value for one is "DonnyWals" and two is "DWals". How this closure is different from the closure you saw before is that everything, from the closure to the property it references is in the same context. The closure can read the current value of name because it's on the same level. We can, however, explicitly capture name using a capture list as follows:

var name = "Donny"
var appendToCapturedName = { [name] (string: String) -> String in
  return name.appending(string)
}

let one = appendToCapturedName("Wals")
name = "D"
let two = appendToCapturedName("Wals")

When you run this code in a Playground, both one and two will equal "DonnyWals" because you explicitly told the closure to capture the current value of name by putting it in a capture list. This can be called an explicit or strong capture. You can capture more than one property in a capture list by comma separating them: [property, anotherProperty].

You just learned about strong and implicit capturing of properties. Let's look at other kinds of captures.

Understanding different kinds of captures

When you strongly capture a property, the closure will own this property. For value types like structs and enums, this means that the closure copies the current value of an object over to its own area in memory where it owns the object. When you do the same for a reference type, like a class, the closure will maintain a strong pointer reference to the object. To understand the implications of this, you need to know one thing about reference types: they are never deallocated as long as at least one other object holds a reference to them. When an object holds an unintended reference to a reference type, it could be considered a memory leak. If you're not sure what this means, don't worry, it should be a little bit clearer after the next code sample. The following code demonstrates the memory leak that I described earlier:

class MyClass {}
var instance: MyClass? = MyClass()

var aClosure = { [instance] in
  print(instance)
}

aClosure() // MyClass
instance = nil
aClosure() // MyClass

The second time we call aClosure we still print the same instance of MyClass because aClosure holds a strong reference to the instance. Sometimes this is exactly what we want, but usually, we don't want our closures to keep objects alive after they've been deinitialized. An example that comes to mind is a closure that might capture a view controller while it's waiting for a network request to finish. If the view controller is dismissed before the network request is finished, we want the view controller to be removed from memory, or deallocated. If the network request's completion closure has a strong reference to the view controller, the closure keeps the view controller alive because it still holds a reference to the view controller.

So how would we make this strong capture a not so strong capture? Well, how about we make it weak instead?

Tip: You can run the code below in a Swift Playground to see the result.

class MyClass {}

var myInstance: MyClass? = MyClass()

var aClosure = { [weak myInstance] in
  print(myInstance)
}

aClosure() // MyClass
myInstance = nil
aClosure() // nil

Because the closure only holds a weak reference to the instance of MyClass, the system doesn't count our closure's reference to instance which means that as soon as the playground releases its reference to our instance of MyClass, it can be deallocated. The downside here is that this might lead to subtle bugs where you don't immediately notice that instance was deallocated before your closure was called. If you want to assert that instance is still around when the closure is called, and want to crash your app if it's not you can use an unowned reference:

Tip: You can run the code below in a Swift Playground to see the result.

class MyClass {}
var instance: MyClass? = MyClass()

var aClosure = { [unowned instance] in
  print(instance)
}

aClosure() // MyClass
instance = nil
aClosure() // crash

The impact on memory for an unowned capture is pretty much the same as weak. It's very similar to safely unwrapping an optional value with ? or doing so forcefully with ! which crashes your app if the value to unwrap is nil. In practice, you'll find that unowned is almost never what you're looking for.

Now that you have some understanding of what weak and unowned are, and how you can implicitly or strongly capture a value, let's have a look at when you should use these different capture methods.

Knowing when to use weak, unowned, strong or implicit capture

As mentioned at the start of this post, a lot of developers use [weak self] in their closures to prevent memory leaks. But are memory leaks really that common when working with closures? That depends on what they're used for exactly. In this section, we'll explore different kinds of captures in closures and what their implications are. We'll first look at when you might want to use an implicit capture. Then we'll look at strong capture and last we'll look at weak and unowned captures.

When to use implicit capture

Implicit capture is often used when you're dealing with closures that capture self, where self is a value type, like a struct. Since structs don't have pointers that reference them, closures won't accidentally keep a struct alive for longer than it should. In fact, trying to use weak or unowned on a non-class type isn't allowed in Swift.

You can use implicit capture on reference types as long as you're certain that the closure you're calling won't be retained by the object that will receive your closure. A good example of this is performing work on a DispatchQueue:

class MyClass {
  func dispatchSomething() {
    DispatchQueue.global().async {
      // it's okay to implicitly capture self here
    }
  }
}

Since the closure passed to async isn't retained, you can safely capture it without a capture list. You know it's not retained because the closure is executed shortly after calling async, and it's only called once. If your closure is retained, for example when it's used as an event handler for an object, you should make sure to capture self weakly to avoid keeping a reference to self that you don't want. Keep in mind that you're often relying on implementation details when you're doing this so even though you don't have to use weak self here, it might be a good idea to limit your usage of implicit captures to code you own and control.

The easiest way to know whether a closure that you pass to a function is retained, is to check whether it's marked as @escaping. Closures that are not marked with @escaping do not leave the scope of the function that you pass it too which means that it can't be retained for longer than the function's scope. If you want to learn more about @escaping, take a look at this post.

When to use an explicit strong capture

Strongly, or explicitly capturing references isn't done very often. It's most useful if you want to allow partial deallocation of objects. For example, if you perform a network request and want to store its result in a Core Data store without doing anything else, the object that initiated the request doesn't have to stick around; you only need the data store. An example of this might look as follows:

struct SomeDataSource {
  var storage: MyDataStore
  let networking: NetworkingLayer
  // more properties

  func refreshData() {
    networking.refresh { [storage] data in
      storage.persist(data)
    }
  }
}

Since the closure only requires the storage property, there's no need to capture self here since that would keep the entire SomeDataSource object around. Keep in mind though that the storage property is captured at the time the closure is created as I showed you earlier in this post. So in this case that means that the value of storage is captured when we call refreshData. If MyDataStore is a struct, that means that it's copied at capture-time in the closure and any changes that are made to storage after the closure is created are not visible inside of the closure.

If MyDataStore is a reference type, you would be able to see changes made to the instance that's captured in the closure, but if you change storage by assigning a new storage to it after storage is captured, you will have captured the old storage instead of the new one.

When to use weak and unowned captures

Weak capture is by far the most common capture and it's usually a good default choice. It should typically be used when you don't want an object to stick around until the closure is performed, or if the closure is retained for an unknown amount of time which is often the case for network requests. Keep in mind that a closure with a weak capture will treat the captured property as an optional. So if you have a [weak self] in your capture list, you'll likely want to unwrap self in your closure body using guard let self else { return } to make sure that self still exists by the time the closure is executed. Using weak or unowned is the only way to make 100% sure your reference type doesn't hang around in memory for longer than necessary. This makes it a good option if you're not sure which type of capture if most appropriate for your current use case.

When it comes to deciding between weak and unowned my personal opinion is to go with weak. There are little to no performance gains when using unowned (not to the point where it will matter in most apps anyway) and if any of your assumptions become incorrect later on, your unowned capture would crash you app which isn't great.

I always think of unowned as a force unwrap. It's probably fine in some cases, but you're essentially putting a little landmine in your code that in my experience is likely to go off when you least expect it.

In Summary

And that's another Quick Tip that became much larger than I initially intended. Closures and capture lists have all kinds of subtle implications that are important to keep in mind while programming and I hope this post has given you some insights into why you sometimes need a [weak self] for reference types but can reference self freely in value types (remember, it's all about the reference count). You saw that you can even capture specific properties of an object if you don't need to capture the entire self. You can even apply weak or unowned to individual properties of an object to prevent your closures from keeping individual objects around.

Now that you know pretty much everything there is to know about closures and capture lists, go ahead and look through your codebase to see if there are any improvements you can make based on the information you just read. As always, thanks for reading and don't hesitate to reach out on Twitter if you have any feedback or questions.

Special thanks to Bas Broek for proofreading this article!

Adding support for multiple windows to your iPadOS app

Now that Apple has split iPadOS into a separate OS, and launched Catalyst to enable developers to compile their iPad apps for the Mac, there’s a whole new multi-window paradigm we must understand and cater for. Up until this year, we only had to worry about a single window for our iOS applications. This meant that we never had to worry about the user being at two places in our app at the same time. For instance, what would happen if a user has two windows of an app open and both are on the edit profile page?

In this blog post, I will introduce the concept of iPad apps with more than one window to you. Last week I explained that multi-window support is enabled through UIScene, UISceneSession and the SceneDelegate. If you’re not sure what these objects are and what they do, I can highly recommend that you go ahead and read last week’s post first.

This week’s blog post covers the following topics:

  • Understanding how a user creates a new scene for your app.
  • Defining the scenes that are supported by your app.
  • Opening, closing, and refreshing scenes.
  • Adding advanced behavior to open new scenes.

Understanding how a user creates a new scene for your app

Before we dive into the details of defining your scenes and implementing code to work with your scenes, let's go over all the ways a user will be able to create scenes for your app once you enable the "Supports Multiple Windows" checkbox in your project settings. We'll not just look at what's made available out of the box, but I also want to show you several of the behaviors your users will likely expect once your application supports multiple windows. After that, we'll have a look at how you can implement multi-window support for your apps.

Ready to get started?

Default ways to open a new scene

In iPadOS, there are two standard paths to opening a new scene. First, the user can drag an app's icon upwards from the dock to create a new scene for the dragged app if an active scene is already on screen. If the app isn't currently visible on the screen, iPadOS will grab an active scene and move that over to where you dropped the app icon. Users can use this method to make your app appear as a flyover or side-by-side with another app.

The second way is to go to the Exposè, select your app to show all windows for your app, and use the plus symbol in the top right corner of the screen to launch a new scene. This will create a new full-screen scene for your app using your app's default scene. The user can then rearrange windows, show them side by side or change one into a flyover using the same gestures and patterns that exist in iOS 12. The only difference is that in iOS 12 every app only had a single scene and in iPadOS 13 an app can have multiple scenes.

In addition to these default methods of opening a scene, there is a special kind of interaction that users will likely come to expect in any app that supports multiple scenes. This interaction is the drag and drop interaction.

Drag and drop interactions your users will likely expect

Users love patterns, and so does Apple. So a good place to look for interactions that your user is likely to expect from your app is the default apps Apple ships with iPadOS. If you examine some of Apple's multi-scene applications, you can see that a lot of them support drag and drop to create a new scene. In Safari, you can pick up a tab and drag it to the side of the screen to open that tab in a new scene. In Contacts, you can grab a contact from the contact list and you can drop it on the side of the screen to show the dragged contact in a new scene. And lastly, in the Mail app, a user can grab the email compose modal and drag it to the side of the screen to open the composer in a new scene.

All of these interactions feel very natural and if your app has similar interaction patterns, it's seriously worth considering implementing drag and drop in a similar way to Apple's implementation to make sure your users feel right at home in your app.

Now that you know about some of the ways your users will expect to open scenes, let's see how you can add support for more than a single type of scene in your app.

Defining the scenes that are supported by your app

If you started your project in Xcode 11 and you've checked the "Supports multiple windows" checkbox, you've done the first couple of steps to support multiple scenes. Your app can now have more than one active scene, and iOS will always use your SceneDelegate to set up your scene. Before we continue, go ahead and grab this blog post's sample project, open the .xcodeproj in the Starter folder and examine the project. It's a very simple cat image viewer application. I know, super cute. If you don't want to follow along, here's a screenshot of the app.

Screenshot of the end result

Okay, back to business. Cats are cool but it would be even cooler if you could see the detail page for the cats side by side, for even more cuteness. When a detail page is tapped, we'll go ahead and open a new scene to display the detail page in. Note that this is probably not what you'd want to do in a real application. Normally a simple tap should just push a detail view in the existing scene, and you'd open a new scene if a user drags the cat picture to the side of the screen. We'll implement this drag and drop behavior in the last section of this blog post. For now I just really want to get you up and running with multiple scene support.

If you examine the sample project, you'll find that it contains a secondary SceneDelegate object called CatSceneDelegate. This specific SceneDelegate looks very similar to the default SceneDelegate that Xcode generates except instead of the app's main view, the CatSceneDelegate uses the cat detail page.

To make sure that your app is aware of this new scene delegate, you must add a new entry to the Application Scene Manifest's Scene Configuration array.

A scene configuration is nothing more than a string identifier and a reference to a scene delegate that should be used when your application is asked to create a scene. The default scene configuration is called Default Configuration and uses the $(PRODUCT_MODULE_NAME).SceneDelegate object to set up its scene and display it to the user.

Tip:
You must always use the $(PRODUCT_MODULE_NAME). prefix before your scene delegate's class name to make sure iOS knows where to look for your scene delegate while running your app.

To add our cat detail scene configuration, click on the plus icon next to the Application Session Role keyword. Xcode will create a new configuration entry for you. Remove the Storyboard Name and Class Name fields. Set $(PRODUCT_MODULE_NAME).CatSceneDelegate as the Delegate Class Name and Cat Detail as the Configuration Name. Make sure to rearrange the configurations by dragging them so that the Default Configuration is above the Cat Detail configuration. Your configuration should like the following screenshot:

Scene configuration example

Run the app, it should work as normal because we're not launching any scenes with our newly created scene configuration yet. Let's go ahead and do that next!

Opening, closing, and refreshing scenes

In a typical application, you will want to control when your app opens a new scene or when it closes one. But how do you open or close a scene, and why would you want to refresh a scene? In this section, I will answer these questions. Let's start by opening a new scene and implementing logic to close it. We will then look at some advanced logic to determine whether a new scene should be opened, or if we can reuse an existing scene. Lastly, we will look at scene refreshing.

To open a new scene, you pass a request to your UIApplication object. The UIApplication will then check if a new scene session needs to be created, or if an existing scene session can be used. In the sample code, I've added a method called didTapCat(_:). Let's add some code to this method to open the cat detail page in a new scene:

let activity = NSUserActivity(activityType: "com.donnywals.viewCat")
activity.userInfo = ["tapped_cat_name": tappedCat(forRecognizer: sender)]
UIApplication.shared.requestSceneSessionActivation(nil, userActivity: activity, options: nil, errorHandler: nil)

The preceding code isn't terribly complex, and it's all we need for a very simple detail page. We create an NSUserActivity that contains all information needed to determine that we want to view a cat detail page, and what cat we want to view the detail page for. After configuring the NSUserActivity, we call requestSceneSessionActivation(_:userActivity:options:errorHandler:) on UIApplication.shared to initiate the request to launch a new scene. When we call this method, application(_:configurationForConnecting:options:) is called on your AppDelegate. You must return an appropriate UISceneConfiguration from this method that matches a configuration that's in your Info.plist. Update this method in this method in AppDelegate.swift so its body looks as follows:

if let activity = options.userActivities.first, activity.activityType == "com.donnywals.viewCat" {
  return UISceneConfiguration(name: "Cat Detail", sessionRole: connectingSceneSession.role)
}

return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)

We check whether we have a user activity that matches the type we expect. If one exists, we create and return an instance of our Cat Detail configuration. If we don't have the expected user activity, we return the Default Configuration. We're almost ready to see the app in action. Let's just have a quick look at the CatDetailSceneDelegate.swift that's already created for you. The following line are especially interesting:

let detail: CatDetailViewController
if let activity = connectionOptions.userActivities.first,
  let catName = activity.userInfo?["tapped_cat_name"] as? String {
  detail = CatDetailViewController(catName: catName)
} else {
  detail = CatDetailViewController(catName: "default")
}

We check whether a user activity was provided to us. If we have one, we extract the relevant values from it. If this fails, or if we don't have a user activity, we create a CatDetailViewController with a default name. You'll see why we need this in a moment. If you run the app now, you'll see that tapping one of the two cats spawns a new scene every time. While this is cool, it would be much better to reuse the same scene session and activate it if one of our cats is tapped.

This can be achieved by looping over the currently active sessions and inspecting the targetContentIdentifier associated with each session's scene. If we find a match, we can request activation of that specific scene rather than asking for a new scene session. Update didTapCat(_:) so it looks as follows:

@objc func didTapCat(_ sender: UITapGestureRecognizer) {
  let activity = NSUserActivity(activityType: "com.donnywals.viewCat")
  activity.targetContentIdentifier = tappedCat(forRecognizer: sender)

  let session = UIApplication.shared.openSessions.first { openSession in
    guard let sessionActivity = openSession.scene?.userActivity,
      let targetContentIdentifier = sessionActivity.targetContentIdentifier  else {

        return false
    }

    return targetContentIdentifier == activity.targetContentIdentifier
  }

  UIApplication.shared.requestSceneSessionActivation(session, userActivity: activity, options: nil, errorHandler: nil)
}

Note:
In one of Apple's WWDC presentations they mention the use of predicates to automatically find the most appropriate scene for a target content identifier. Unfortunately, I haven't been able to get this to work myself. If you have, please do reach out to me so I can update this post.

When you run the app again in Xcode, you will notice that it recreates all the scenes that were active when you quit the app. This is why you added the fallback earlier. When the app is used in normal conditions, this shouldn't happen. But it's good to guard against it for development purposes anyway. Despite Xcode recreating existing detail scenes with the default identifier, tapping the same cat multiple times should now only open one scene for each cat.

When a user wants to close a scene, they can do this from the iPad's Exposé. There is a close button on the cat detail page right now, but it doesn't do anything, Let's write some code to destroy the current scene if a user taps the close button. Add the following code to the close method in CatDetailViewController.swift:

if let session = self.view.window?.windowScene?.session {
  let options = UIWindowSceneDestructionRequestOptions()
  options.windowDismissalAnimation = .commit
  UIApplication.shared.requestSceneSessionDestruction(session, options: options, errorHandler: nil)
}

This code obtains the current session and creates an instance of UIWindowSceneDestructionRequestOptions. We can use this object to configure a nice animation for when the scene is discarded. In this case, we pick a commit style. You can also choose decline and standard depending on the message you want to send to the user.

Now let's look at refreshing a scene. This is something you'll typically want to do to make sure your app's snapshot in the iPad Exposé is up to date and accurately reflects your user interface. For the cat app this doesn't make a lot of sense, but let's assume it would. The following code would go over all currently connected scene sessions and if the session is used to display a relevant user activity, we ask the application to refresh the session:

for session in UIApplication.shared.openSessions {
  if session.scene?.userActivity?.activityType == "some.activity.type" {
    UIApplication.shared.requestSceneSessionRefresh(session)
  }
}

Note that the refresh action might not take place immediately, the system reserves the right to delay the refreshes to an appropriate moment in time.

Now that you've seen how you would create a session and reuse it for the same content, how to destroy a session and how to refresh a session, it's time to implement one last method of creating new scenes; drag and drop.

Adding advanced behavior to open new scenes

We've implemented everything we set out to implement. All we need now is drag and drop support. I won't go into all the fine details for drag and drop in this post. Instead, I will show you how to implement a drag interaction for this specific scenario and how to configure your app so it can use drag and drop to open new scenes. Before your app can support drag and drop to open new scenes, you must register the user activity types your app will handle in your Info.plist. Add a new NSUserActivityTypes key of type Array and add the user activity types you wish to support to this array. You should end up with an entry that's similar to the following screenshot:

Example plist entry

Next, add the following two lines of code to CatsOverviewViewController.swift right after the code that sets up a tap gesture (should be around line 41):

let dragInteraction = UIDragInteraction(delegate: self)
image.addInteraction(dragInteraction)

The preceding code adds drag support to the image view. The next step is to make CatsOverviewViewController.swift conform to UIDragInteractionDelegate so it can provide the appropriate drag item that's used to open a new scene for the app. Add the following extension to CatsOverviewViewController.swift:

extension CatsOverviewViewController: UIDragInteractionDelegate {
  func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] {
    let selectedCat = cats[interaction.view?.tag ?? 0]

    let userActivity = NSUserActivity(activityType: "com.donnywals.viewCat")
    userActivity.targetContentIdentifier = selectedCat
    userActivity.title = "View Cat"

    let itemProvider = NSItemProvider(object: UIImage(named: selectedCat)!)
    itemProvider.registerObject(userActivity, visibility: .all)

    let dragItem = UIDragItem(itemProvider: itemProvider)
    dragItem.localObject = selectedCat

    return [dragItem]
  }
}

After doing this, run the app and drag one of the two cats to the side of the screen. You should be able to drop the image to create a new floating scene or create a new one that's opened right next to the existing scene. Amazing that we did this with only a few lines of code right?

In summary

Wow! This post turned out much longer than I expected! You have learned a ton about adding support for multiple scenes to your iPad app, and there's still more for you to explore! We haven't looked at supporting URLs or Shortcut Items in this blog post. And we also didn't have time to go over adding a target-content-id to your push notifications to make certain notifications launch in a specific scene. I'm sure you'll be able to figure this out on your own now that you have a solid foundation of scene session knowledge.

Let's recap what you've learned in this post. First, you learned about the ways users are likely to expect to use multiple scenes with apps that support them. Then you saw how you can configure your Info.plist with all the scenes that your app supports. Next, you saw how to launch a new scene when a user taps a button, and how to destroy it when they tap another button. You also saw how you would refresh a scene session if needed. And lastly, we added drag and drop support to allow users to drag elements of your app to the side of the screen to launch a new scene.

This is cool stuff! And I'm sure you're going to build amazing things with this knowledge. And, as always, if you enjoyed this blog post, have feedback, questions or anything else. Don't hesitate to share this post with your friends and reach out to me on Twitter. If you're looking for the sample code for this post, it's right here on Github.

Uploading images and forms to a server using URLSession

One of those tasks that always throws me off balance is building a form that allows users to upload a form with a picture attached to it. I know that it involves configuring my request to be multipart, that I need to attach the picture as data and there’s something involved with setting a content disposition. This is usually about as far as I go until I decide it might be a good time to go to github.com and grab the Carthage URL for Alamofire. If you’re reading this and you’ve implemented POST requests that allow users to upload photos and forms, I’m sure this sounds familiar to you.

In this week’s quick tip I will show you how to implement a multipart form with file upload using only Apple’s built-in URLSession. Ready? On your marks. GO!

Understanding what a multipart request actually looks like

If you’ve ever inspected a multipart request using a tool like Charles or Proxyman you may have found out that the headers of your post requests contained the following key amongst several others:

Content-Type: multipart/form-data; boundary=3A42CBDB-01A2-4DDE-A9EE-425A344ABA13

This header tells us that the content that's being sent to the server is multipart/form-data. This content type is used when you upload a file alongside other fields in a single request. This is very similar to how an HTML form is uploaded to a server for example. This header also specifies a boundary which is a string that's used by the server to detect where lines / values start and end.

The value you saw for the boundary was very likely to be different, but it should look familiar. The body of your post request typically looks a little bit like like the following:

--Boundary-3A42CBDB-01A2-4DDE-A9EE-425A344ABA13
Content-Disposition: form-data; name="family_name"

Wals
--Boundary-3A42CBDB-01A2-4DDE-A9EE-425A344ABA13
Content-Disposition: form-data; name="name"

Donny
--Boundary-3A42CBDB-01A2-4DDE-A9EE-425A344ABA13
Content-Disposition: form-data; name="file"; filename="somefilename.jpg"
Content-Type: image/png

-a long string of image data-
--Boundary-3A42CBDB-01A2-4DDE-A9EE-425A344ABA13—

If you've inspected your request and found similar content to the content above, you might have decided that this looks complicated and you’re better off using a library that handles the creation of the header and HTTP body for you. If that’s the case, I completely understand. Especially because the part where I wrote -a long string of image data- can be really long. I used to reach for Alamofire to handle uploads for the longest time. However, once you take the time to dissect the Content-Type header and HTTP body a little bit, you’ll find that it follows a pretty logical pattern that takes a bunch of effort to implement but I wouldn't say it's very hard. It's mostly very tedious.

First, there’s the Content-Type header. It contains information about the type of data you’re sending (multipart/form-data;) and a boundary. This boundary should always have a unique, somewhat random value. In the example above I used a UUID. Since multipart forms are not always sent to the server all at once but rather in chunks, the server needs some way to know when a certain part of the form you’re sending it ends or begins. This is what the boundary value is used for. This must be communicated in the headers since that’s the first thing the receiving server will be able to read.

Next, let’s look at the http body. It starts with the following block of text:

--Boundary-3A42CBDB-01A2-4DDE-A9EE-425A344ABA13
Content-Disposition: form-data; name="family_name"

Wals

We send two dashes (--) followed by the predefined boundary string (Boundary-3A42CBDB-01A2-4DDE-A9EE-425A344ABA13) to inform the server that it’s about to read a new chunk of content. In this case a form field. The server knows that it’s receiving a form field thanks to the first bit of the next line: Content-Disposition: form-data;. It also knows that the form field it’s about to receive is named family-name due to the second part of the Content-Disposition line: name=“family_name”. This is followed by a blank line and the value of the form field we want to send the server.

This pattern is repeated for the other form field in the example body:

--Boundary-3A42CBDB-01A2-4DDE-A9EE-425A344ABA13
Content-Disposition: form-data; name="name"

Donny

The third field in the example is slightly different. It’s Content-Disposition looks like this:

Content-Disposition: form-data; name="file"; filename="somefilename.jpg"
Content-Type: image/png

It has an extra field called filename. This tells the server that it can refer to the uploaded file using that name once the upload succeeded. This last chunk for the file itself also has its own Content-Type field. This tells the server about the uploaded file’s Mime Type. In this example, it’s image/png because we’re uploading an imaginary png image.

After that, you should see another empty line and then a whole lot of cryptic data. That’s the raw image data. And after all of this data, you’ll find the last line of the HTTP body:

--Boundary-E82EE6C1-377D-486C-AFE1-C0CE9A03E9A3--

It’s one last boundary, prefixed and suffixed with --. This tells the server that it has now received all of the HTTP data that we wanted to send it.

Every form field essentially has the same structure:

BOUNDARY
CONTENT TYPE
-- BLANK LINE --
VALUE

This structure is mandatory, we didn't pick it ourselves, and we shouldn't modify it.

Once you understand this structure, the HTTP body of a multipart request should look a lot less daunting, and implementing your own multipart uploader with URLSession shouldn’t sound as scary anymore. Let’s dive right in and implement a multipart URLRequest that can be executed by URLSession!

Preparing a multipart request with an image

In the previous section, we focussed on the contents of a multipart form request in an attempt de demystify its contents. Now it’s time to construct a URLRequest, configure it and build its httpBody so we can send it off to the server with URLSession instead of a third-party solution.

Since I only want to focus on building a multipart for request that contains a file, I won’t be showing you how you can obtain an image that your user can upload.

The first bit of this task is pretty straightforward. We’ll create a URLRequest, make it a POST request and set its Content-Type header:

let boundary = "Boundary-\(UUID().uuidString)"

var request = URLRequest(url: URL(string: "https://some-page-on-a-server")!)
request.httpMethod = "POST"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

Next, let’s look at the HTTP body. In the previous section, you saw that every block in a multipart request is constructed similarly. Let’s create a method that will output these chunks of body data so that we're not having to bother with writing the same code over and over again.

func convertFormField(named name: String, value: String, using boundary: String) -> String {
  var fieldString = "--\(boundary)\r\n"
  fieldString += "Content-Disposition: form-data; name=\"\(name)\"\r\n"
  fieldString += "\r\n"
  fieldString += "\(value)\r\n"

  return fieldString
}

The code above should pretty much speak for itself. We construct a String that has all the previously discussed elements. Note the \r\n that is added to the string after every line. This is needed to add a new line to the string so we get the output that we want.

While this method is pretty neat for the form fields that contain text, we need a separate method to create the chunk for file data since it works slightly different from the rest. This is mainly because we need to specify the content type for our file, and we have file's Data as the value rather than a String. The following code can be used to create a body chunk for the file:

func convertFileData(fieldName: String, fileName: String, mimeType: String, fileData: Data, using boundary: String) -> Data {
  let data = NSMutableData()

  data.appendString("--\(boundary)\r\n")
  data.appendString("Content-Disposition: form-data; name=\"\(fieldName)\"; filename=\"\(fileName)\"\r\n")
  data.appendString("Content-Type: \(mimeType)\r\n\r\n")
  data.append(fileData)
  data.appendString("\r\n")

  return data as Data
}

extension NSMutableData {
  func appendString(_ string: String) {
    if let data = string.data(using: .utf8) {
      self.append(data)
    }
  }
}

Instead of a String, we create Data this time. The reason for this is twofold. One is that we already have the file data. Converting this to a String and then back to Data when we add it to the HTTP body is wasteful. The second reason is that the HTTP body itself must be created as Data rather than a String. To make appending text to the Data object, we add an extension on NSMutableData that safely appends the given string as Data. From the structure of the method, you should be able to derive that it matches the HTTP body that was shown earlier.

Let’s put all these pieces together and finish preparing the network request!

let httpBody = NSMutableData()

for (key, value) in formFields {
  httpBody.appendString(convertFormField(named: key, value: value, using: boundary))
}

httpBody.append(convertFileData(fieldName: "image_field",
                                fileName: "imagename.png",
                                mimeType: "image/png",
                                fileData: imageData,
                                using: boundary))

httpBody.appendString("--\(boundary)--")

request.httpBody = httpBody as Data

print(String(data: httpBody as Data, encoding: .utf8)!)

The preceding code shouldn’t be too surprising at this point. You use the methods you wrote earlier to construct the HTTP body. After adding the form fields you add the final boundary with the two trailing dashes and the resulting data is set as the request’s httpBody. Note the print statement at the end of this snippet. Try printing the HTTP body and you’ll see that it matches the format from the beginning of this post perfectly.

Now all that’s left to do is run your request just like you would normally:

do {
  let (data, response) = try await URLSession.shared.data(from: request)
  // use your data
} catch {
  print(error)
}

// Or if you're not using async / await yet
URLSession.shared.dataTask(with: request) { data, response, error in
  // handle the response here
}.resume()

Pretty awesome right? It took a little bit of work, but once you understand what you’re doing it’s suddenly not so bad anymore.

In summary

Even though I called this post a Quick Tip, this turned out to be an information-packed, fairly long write up on making a multipart form request with URLSession. And while there is quite some text involved in explaining everything, I hope you see now that making this kind of request doesn’t require a third-party library. Sure, it takes away the boilerplate for you, but at the same time, you might want to take a step back and ask yourself whether the boilerplate is really so bad that you want to use an external dependency for a single task.

If you want to grab a copy of the finished example, head over to Github. I’ve uploaded a Playground for you to, well, play with. As always, thanks for reading this week’s Quick Tip and any questions, feedback and even compliments are more than welcome. You can reach me on X or Threads.

Understanding the iOS 13 Scene Delegate

When you create a new project in Xcode 11, you might notice something that you haven’t seen before. Instead of only creating an AppDelegate.swift file, a ViewController.swift, a storyboard and some other files, Xcode now creates a new file for you; the SceneDelegate.swift file. If you’ve never seen this file before, it might be quite confusing to understand what it is, and how you are supposed to use this new scene delegate in your app.

By the end of this week's blog post you will know:

  • What the scene delegate is used for.
  • How you can effectively implement your scene delegate.
  • Why the scene delegate is an important part of iOS 13.

Let’s jump right in, shall we?

Examining the new Xcode project template

Whenever you create a new Xcode project, you have the option to choose whether you want to use SwiftUI or Storyboards. Regardless of your choice here, Xcode will generate a new kind of project template for you to build upon. We’ll take a closer look at the SceneDelegate.swift and AppDelegate.swift files in the next section, what’s important for now is that you understand that Xcode has created these files for you.

In addition to these two delegate files, Xcode does something a little bit more subtle. Take a close look at your Info.plist file. You should see a new key called Application Scene Manifest with contents similar to the following image:

Screenshot of the Info.plist file's scene manifest

This scene manifest specifies a name and a delegate class for your scene. Note that these properties belong to an array (Application Session Role), suggesting that you can have multiple configurations in your Info.plist. A much more important key that you may have already spotted in the screenshot above is Enable Multiple Windows. This property is set to NO by default. Setting this property to YES will allow users to open multiple windows of your application on iPadOS (or even on macOS). Being able to run multiple windows of an iOS application side by side is a huge difference from the single window environment we’ve worked with until now, and the ability to have multiple windows is the entire reason our app’s lifecycle is now maintained in two places rather than one.

Let’s take a closer look at the AppDelegate and SceneDelegate to better understand how these two delegates work together to enable support for multiple windows.

Understanding the roles of AppDelegate and SceneDelegate

If you’ve built apps prior to iOS 13, you probably know your AppDelegate as the one place that does pretty much everything related to your application’s launch, foregrounding, backgrounding and then some. In iOS 13, Apple has moved some of the AppDelegate responsibilities to the SceneDelegate. Let’s take a brief look at each of these two files.

AppDelegate’s responsibilities

The AppDelegate is still the main point of entry for an application in iOS 13. Apple calls AppDelegate methods for several application level lifecycle events. In Apple’s default template you’ll find three methods that Apple considers to be important for you to use:

  • func application(_:didFinishLaunchingWithOptions:) -> Bool
  • func application(_:configurationForConnecting:options:) -> UISceneConfiguration
  • func application(_:didDiscardSceneSessions:)

These methods have some commentary in them that actually describes what they do in enough detail to understand what they do. But let’s go over them quickly anyway.

When your application is just launched, func application(_:didFinishLaunchingWithOptions:) -> Bool is called. This method is used to perform application setup. In iOS 12 or earlier, you might have used this method to create and configure a UIWindow object and assigned a UIViewController instance to the window to make it appear.

If your app is using scenes, your AppDelegate is no longer responsible for doing this. Since your application can now have multiple windows, or UISceneSessions active, it doesn’t make much sense to manage a single-window object in the AppDelegate.

The func application(_:configurationForConnecting:options:) -> UISceneConfiguration is called whenever your application is expected to supply a new scene, or window for iOS to display. Note that this method is not called when your app launches initially, it’s only called to obtain and create new scenes. We’ll take a deeper look at creating and managing multiple scenes in a later blog post.

The last method in the AppDelegate template is func application(_:didDiscardSceneSessions:). This method is called whenever a user discards a scene, for example by swiping it away in the multitasking window or if you do so programmatically. If your app isn’t running when the user does this, this method will be called for every discarded scene shortly after func application(_:didFinishLaunchingWithOptions:) -> Bool is called.

In addition to these default methods, your AppDelegate can still be used to open URLs, catch memory warnings, detect when your app will terminate, whether the device’s clock changed significantly, detect when a user has registered for remote notifications and more.

Tip:
It’s important to note that if you’re currently using AppDelegate to manage your app’s status bar appearance, you might have to make some changes in iOS 13. Several status bar related methods have been deprecated in iOS 13.

Now that we have a better picture of what the new responsibilities of your AppDelegate are, let’s have a look at the new SceneDelegate.

SceneDelegate’s responsibilities

When you consider the AppDelegate to be the object that’s responsible for your application’s lifecycle, the SceneDelegate is responsible for what’s shown on the screen; the scenes or windows. Before we continue, let’s establish some scene related vocabulary because not every term means what you might think it means.

When you’re dealing with scenes, what looks like a window to your user is actually called a UIScene which is managed by a UISceneSession. So when we refer to windows, we are really referring to UISceneSession objects. I will try to stick to this terminology as much as possible throughout the course of this blog post.

Now that we’re on the same page, let’s look at the SceneDelegate.swift file that Xcode created when it created our project.

There are several methods in the SceneDelegate.swift file by default:

  • scene(_:willConnectTo:options:)
  • sceneDidDisconnect(_:)
  • sceneDidBecomeActive(_:)
  • sceneWillResignActive(_:)
  • sceneWillEnterForeground(_:)
  • sceneDidEnterBackground(_:)

These methods should look very familiar to you if you’re familiar with the AppDelegate that existed prior to iOS 13. Let’s have a look at scene(_:willConnectTo:options:) first, this method probably looks least familiar to your and it’s the first method called in the lifecycle of a UISceneSession.

The default implementation of scene(_:willConnectTo:options:) creates your initial content view (ContentView if you’re using SwiftUI), creates a new UIWindow, sets the window’s rootViewController and makes this window the key window. You might think of this window as the window that your user sees. This, unfortunately, is not the case. Windows have been around since before iOS 13 and they represent the viewport that your app operates in. So, the UISceneSession controls the visible window that the user sees, the UIWindow you create is the container view for your application.

In addition to setting up initial views, you can use scene(_:willConnectTo:options:) to restore your scene UI in case your scene has disconnected in the past. For example, because it was sent to the background. You can also read the connectionOptions object to see if your scene was created due to a HandOff request or maybe to open a URL. I will show you how to do this later in this blog post.

Once your scene has connected, the next method in your scene’s lifecycle is sceneWillEnterForeground(_:). This method is called when your scene will take the stage. This could be when your app transitions from the background to the foreground, or if it’s just becoming active for the first time. Next, sceneDidBecomeActive(_:) is called. This is the point where your scene is set up, visible and ready to be used.

When your app goes to the background, sceneWillResignActive(_:) and sceneDidEnterBackground(_:) are called. I will not go into these methods right now since their purpose varies for every application, and the comments in the Xcode template do a pretty good job of explaining when these methods are called. Actually, I’m sure you can figure out the timing of when these methods are called yourself.

A more interesting method is sceneDidDisconnect(_:). Whenever your scene is sent to the background, iOS might decide to disconnect and clear out your scene to free up resources. This does not mean your app was killed or isn’t running anymore, it simply means that the scene passed to this method is not active anymore and will disconnect from its session.

Note that the session itself is not necessarily discarded too, iOS might decide to reconnect a scene to a scene session at any time, for instance when a user brings a particular scene to the foreground again.

The most important thing to do in sceneDidDisconnect(_:) is to discard any resources that you don’t need to keep around. This could be data that is easily loaded from disk or the network or other data that you can recreate easily. It’s also important to make sure you retain any data that can’t be easily recreated, like for instance any input the user provided in a scene that they would expect to still be there when they return to a scene.

Consider a text processing app that supports multiple scenes. If a user is working in one scene, then backgrounds it to do some research on Safari and change their music in Spotify, they would absolutely expect all their work to still exist in the text processing app, even though iOS might have disconnected the text processing app’s scene for a while. To achieve this, the app must retain the required data, and it should encode the current app state in an NSUserActivity object that can be read later in scene(_:willConnectTo:options:) when the scene is reconnected.

Since this workflow of connecting, disconnecting and reconnecting scenes is going to separate the good apps from the great, let’s have a look at how you can implement state restoration in your app.

Performing additional scene setup

There are several reasons for you to have to perform additional setup when a scene gets set up. You might have to open a URL, handle a Handoff request or restore state. In this section, I will focus mostly on state restoration since that’s possibly the most complex scenario you might have to handle.

State restoration starts when your scene gets disconnected and sceneDidDisconnect(_:) is called. At this point, it's important that your application already has a state set up that can be restored later. The best way to do this is to use NSUserActivity in your application. If you’re using NSUserActivity to support Handoff, Siri Shortcuts, Spotlight indexing and more, you don’t have a lot of extra work to do. If you don’t use NSUserActivity yet, don’t worry. A simple user activity might look a bit as follows:

let activity = NSUserActivity(activityType: "com.donnywals.DocumentEdit")
activity.userInfo = ["documentId": document.id]

Note that this user activity is not structured how Apple recommends it, it’s a very bare example intended to illustrate state restoration. For a complete guide on NSUserActivity, I recommend that you take a look at Apple’s documentation on this topic.

When the time comes for you to provide a user activity that can be restored at a later time, the system calls stateRestorationActivity(for:) method on your SceneDelegate. Note that this method is not part of the default template

func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? {
  return scene.userActivity
}

Doing this associates the currently active user activity for a scene with the scene session. Remember that whenever a scene is disconnected, the UISceneSession that owns the UIScene is not discarded to allow the session to reconnect to a scene. When this happens, scene(_:willConnectTo:options:) is called again. In this method, you have access to the UISceneSession that owns the UIScene so you can read the session’s stateRestorationActivity and restore the application state as needed:

if let activity = session.stateRestorationActivity,
  activity.activityType == "com.donnywals.DocumentEdit",
  let documentId = activity.userInfo["documentId"] as? String {

  // find document by ID
  // create document viewcontroller and present it
}

Of course, the fine details of this code will vary based on your application, but the general idea should be clear.

If your UISceneSession is expected to handle a URL, you can inspect the connectionOptions object’s urlContexts to find URLs that your scene should open and information about how your application should do this:

for urlContext in connectionOptions.urlContexts {
  let url = urlContext.url
  let options = urlContext.options

  // handle url and options as needed
}

The options object will contain information about whether your scene should open the URL in place, what application requested this URL to be opened and other metadata about the request.

The basics of state restoration in iOS 13 with the SceneDelegate are surprisingly straightforward, especially since it's built upon NSUserActivity which means that a lot of applications won’t have to do too much work to begin supporting state restoration for their scenes.

Keep in mind that if you want to have support for multiple scenes for your app on iPadOS, scene restoration is especially important since iOS might disconnect and reconnect your scenes when they switch from the foreground to the background and back again. Especially if your application allows a user to create or manipulate objects in a scene, a user would not expect their work to be gone if they move a scene to the background for a moment.

In summary

In this blog post, you have learned a lot. You learned what roles the AppDelegate and SceneDelegate fulfill in iOS 13 and what their lifecycles look like. You now know that the AppDelegate is responsible for reacting to application-level events, like app launch for example. The SceneDelegate is responsible for scene lifecycle related events. For example, scene creation, destruction and state restoration of a UISceneSession. In other words, the main reason for Apple to add UISceneDelegate to iOS 13 was to create a good entry point for multi-windowed applications.

After learning about the basics of UISceneDelegate, you saw a very simple example of what state restoration looks like in iOS 13 with UISceneSession and UIScene. Of course, there is much more to learn about how your app behaves when a user spawns multiple UISceneSessions for your app, and how these scenes might have to remain in sync or share data.

If you want to learn more about supporting multiple windows for your iPad app (or your macOS app), make sure to check out my post Adding support for multiple windows to your iPadOS app. Thanks for reading, and don’t hesitate to reach out on Twitter if you have any questions or feedback for me.