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.

When to use map, flatMap and compactMap in Swift

Any time you deal with a list of data, and you want to transform the elements in this list to a different type of element, there are several ways for you to achieve your goal. In this week’s Quick Tip, I will show you three popular transformation methods with similar names but vastly different applications and results.

By the end of this post, you will understand exactly what map, flatMap and compactMap are and what they do. You will also be able to decide which flavor of map to use depending on your goals. Let’s dive right in by exploring the most straightforward of the three; map.

Understanding map

Any time you have a set of data where you want to transform every element of the sequence into a different element, regardless of nested lists or nil values, you’re thinking of using map. The working of map is probably best explained using an example:

let numbers = [1, 2, 3, 4, 5, 6]

let mapped = numbers.map { number in
  return "Number \(number)"
}

// ["Number 1", "Number 2", "Number 3", "Number 4", "Number 5", "Number 6"]
print(mapped)

The example shows an array of numbers from one through six. By calling map on this array, a loop is started where you can transform the number and return another number or, like in this example, something completely different. By using map, the original array of numbers is unaltered and a new array is created that contains all of the transformed elements. When you call map on an array with nested arrays, the input for the map closure will an array. Let’s look at another example:

let original = [["Hello", "world"], ["This", "is", "a", "nested", "array"]]
let joined = original.map { item in
  return item.joined(separator: " ")
}

// ["Hello world", "This is a nested array"]
print(joined)

By joining all strings in the nested array together, we get a new array with two strings instead of two nested arrays because each array was mapped to a string. But what if we wanted to do something else with these strings like for instance remove the nested arrays and get the following output:

["Hello", "world", "This", "is", "a", "nested", "array"]

This is what flatMap is good at.

Understanding flatMap

The slightly more complicated sibling of map is called flatMap. You use this flavor of map when you have a sequence of sequences, for instance, an array of arrays, that you want to "flatten". An example of this is removing nested arrays so you end up with one big array. Let’s see how this is done exactly:

let original = [["Hello", "world"], ["This", "is", "a", "nested", "array"]]
let flatMapped = original.flatMap { item in
  return item
}

// ["Hello", "world", "This", "is", "a", "nested", "array"]
print(flatMapped)

Even though the type of item is [String], and we return it without changing it, we still get a flat array of strings without any nested arrays. flatMap works by first looping over your elements like a regular map does. After doing this, it flattens the result by removing one layer of nested sequences. Let’s have a look at an interesting misuse of flatMap:

let original = [["Hello", "world"], ["This", "is", "a", "nested", "array"]]
let flatMapped2 = original.flatMap { item in
  item.joined(separator: " ")
}

print(flatMapped2)

This example joins the item (which is of type [String]) together into a single string, just like you did in the map example. I want you to think about the output of this flatMap operation for a moment. What do you expect to be printed?

I’ll give you a moment here.

If you thought that the result would be the same as you saw before with map, you expected the following result:

["Hello world", "This is a nested array"]

This makes sense, you’re returning a String from the flatMap closure. However, the actual result is the following:

["H", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d", "T", "h", "i", "s", " ", "i", "s", " ", "a", " ", "n", "e", "s", "t", "e", "d", " ", "a", "r", "r", "a", "y"]

Wait, what? Every letter is now an individual element in the array? That’s correct. Since String is a sequence of Character, flatMap will flatten the String, pulling it apart into its individual Character objects. If this is not the result you had in mind, that’s okay. flatMap can be a confusing beast. It’s important to keep in mind that it will flatten anything that is a sequence of sequences into a single sequence. Even if it’s a String.

Let’s see how the last flavor of map, compactMap is different from map and flatMap.

Understanding compactMap

The last flavor of map we’ll look at in this Quick Tip is compactMap. Whenever you have an array of optional elements, for instance [String?], there are cases when you want to filter out all the nil items so you end up with an array of [String] instead. It might be tempting to do this using a filter:

let arrayWithOptionals = ["Hello", nil, "World"]
let arrayWithoutOptionals = arrayWithOptionals.filter { item in
  return item != nil
}

// [Optional("Hello"), Optional("World")]
print(arrayWithoutOptionals)

This kind of works but the type of arrayWithoutOptionals is still [String?]. How about using a for loop:

let arrayWithOptionals = ["Hello", nil, "World"]
var arrayWithoutOptionals = [String]()
for item in arrayWithOptionals {
  if let string = item {
    arrayWithoutOptionals.append(string)
  }
}

// ["Hello", "World"]
print(arrayWithoutOptionals)

Effective, but not very pretty. Luckily, we have compactMap which is used for exactly this one purpose. Turning a sequence that contains optionals into a sequence that does not contain optionals:

let arrayWithOptionals = ["Hello", nil, "World"]
var arrayWithoutOptionals = arrayWithOptionals.compactMap { item in
  return item
}

// ["Hello", "World"]
print(arrayWithoutOptionals)

Neat, right? A somewhat more interesting example might be to take an array of strings and converting them into an array of URL objects. Immediately filtering out the nil results that occur when you convert an invalid String to URL:

let urlStrings = ["", "https://blog.donnywals.com", "https://avanderlee.com"]
let urls = urlStrings.compactMap { string in
  return URL(string: string)
}

// [https://blog.donnywals.com, https://avanderlee.com]
print(urls)

We now have converted the input strings to valid URL instances, omitting any nil values. Pretty neat, right.

In summary

This Quick Tip showed you three ways to convert an array or sequence of elements into a different kind of sequence. map is the most basic variant, it converts every element in the sequence into a different sequence. No exceptions. Next, we looked at flatMap which looks similar to map but it flattens the first level of nested sequences. You also saw how it would flatten an array of two strings into an array of many single characters because a String is a sequence of Character. Lastly, you learned about compactMap. This mapping function transforms all elements in a sequence into a different element, just like map, but it removes nil values afterward.

Now that you have learned about map on sequence types like arrays, you should have a vague idea of what might happen if you map a Publisher in Combine, or maybe if you call map on an Optional. If you’re not sure, I challenge to go ahead and try it out. You might find some interesting results!

Thanks for reading this post, and as always your feedback is more than welcome! Don’t hesitate to shoot me a Tweet if you have any questions, feedback or just want to reach out.

Faking network responses in tests

Modern applications often rely on data from a network connection to work as intended. Sometimes they rely heavily on the network and are almost worthless without an internet connection while other apps can function mostly fine without a network connection. What these apps have in common is that they contain code that might be challenging to write tests for.

Whenever you write unit tests, you should always strive to make your tests as predictable, reproducible and most importantly independent of external factors as possible. This is a huge difference compared to integration testing where you’d test a certain part of a system within its context. An internet connection is quite possibly one of the most unpredictable factors that you do not want to introduce in a unit test suite.

Why is the network so unpredictable you ask? Reasons include but are not limited to:

  • The network connection might suddenly drop, this is outside of your control.
  • You might not control the server, changes, bugs or outages on the server would impact your tests.
  • Network speeds might vary depending on several factors, this could result in unjust test failures.

There are several things you can do to remove the network as a dependency from your tests. In this post, I will show you how you can build a dedicated networking layer that is abstracted using protocols. This allows you to swap out the networking implementation when running tests, which helps you avoid going to the network altogether. A setup like this allows you to test logic completely independent from what might be happening on a server. Let’s say, for example, that you’re building a reward system. Whenever a user is eligible for a reward, they tap a button. Your app fires off a request to a web server that returns a response to your app that contains between 0 and 2 rewards. There are four reward types:

  1. Jackpot, you win a free ticket for an event.
  2. Normal reward, you win a 50% ticket discount for an event.
  3. Small reward, you win a 25% ticket discount for an event.
  4. Loyalty points, you win between 50 and 100 loyalty points.

If a user wins the jackpot they should not receive any other prices. A user cannot receive the Small and Normal reward at once. A user can, however, get the Normal or Small reward together with loyalty points.

If a user wins the Jackpot, they should be shown a special Jackpot animation. If a user wins the Normal or Small award, they should be taken to a shopping page where they can use their discount if they desire to do so. If they win loyalty points and a reward, they should see a small banner at the top of the screen and they should also be taken to the shopping page. If a user only wins loyalty points, they should only be shown a banner that informs them about their loyalty points.

These are a lot of different paths that might have to be taken in the app, and testing this with a network connection on a real server isn’t feasible. The selection process is random after all! Complex flows like the one described above are another reason that makes faking network responses so convenient. Let’s get started with implementing this complex feature!

Designing the basics

Since this is a complex feature, we’re going to pick and choose a couple of elements from different common app architectures to use as building blocks. As described in the introduction, we’re building a feature where a user is on a certain screen and depending on the result of a network call, they will be taken to a new screen, and potentially they will see an overlay banner. This sounds like a great job for a ViewModel, and we’ll want to extract the networking logic into an object that the ViewModel can use to make network calls.

In this blog post, we will focus on tests for the networking layer.

Preparing the fake responses

When you’re dealing with responses from a server, you often have some kind of documentation or example response to work from. In this case, we have an example JSON response that contains all the information needed to derive every response we can expect from the server:

{
  "rewards": [
    {
      "type": "jackpot" // jackpot, regular or small
    }
  ],
    "loyalty_points": 60 // value between 50 and 100 or null
}

This relatively simple document contains all the information we need right now. It’s tempting to create a bunch of JSON files with different compositions of valid responses and use those while testing. Not a bad approach but we can do better. Since we have the Codable protocol in Swift, we can easily convert JSON data into model objects, but this also works the other way around! We can define our model in the main project, make it Codable and then spawn instances of the model in our test suite, convert them to JSON data and use those as inputs for the networking client. Here’s the model definition we’re using:

struct RewardsResponse: Codable {
  let rewards: [Reward]
  let loyaltyPoints: Int?
}

struct Reward: Codable {
  enum RewardType: String, Codable {
    case jackpot, regular, small
  }

  let type: RewardType
}

This simple model should be sufficient to hold the example JSON data. Let’s add a new file to the test target too, it should be a new Swift file named RewardsResponseFactory. This factory is going to help you create RewardsResponse objects and it will contain a convenient extension on RewardsResponse so you can easily convert the response object to JSON using a JSONEncoder. Add the following code to RewardsResponseFactory.swift:

@testable import RewardsApp

class RewardsResponseFactory {
  static func createResponseWithRewards(_ types: [Reward.RewardType], points: Int?) -> RewardsResponse {
    let rewards = types.map { type in Reward(type: type) }
    return RewardsResponse(rewards: rewards, loyaltyPoints: points)
  }
}

extension RewardsResponse {
  var dataValue: Data {
    let encoder = JSONEncoder()
    encoder.keyEncodingStrategy = .convertToSnakeCase
    return try! encoder.encode(self)
  }
}

You know have everything in place to start writing some tests and implementing the complex reward flow!

Writing your tests and implementation code

Like good practitioners of TDD, we’ll begin implementing our feature in tests first. Once we have a couple of tests outlined we can go ahead and begin implementing the code for our feature.

In Xcode’s Project Navigator, find the test target and rename the default test file and class to RewardsServiceTests. Remove the default code from Xcode’s test template. You should now have the following contents in RewardsServiceTests:

import XCTest
@testable import RewardsApp

class RewardsServiceTests: XCTestCase {
}

We’ll begin by adding tests for the API itself, we want to make sure that the API makes the correct requests and that it converts the JSON data that it receives to RewardResponse objects which are then passed back to the caller of the RewardsService’s fetchRewards method. Quite the mouthful but what we’re testing here is whether the RewardsService can make and handle requests. Since we can’t use the internet in our test, we’ll need to abstract the networking behind a protocol called Networking, add a new file called Networking.swift to your application target and add the following contents to it:

protocol Networking {
  func fetch(_ url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> Void)
}

This protocol will allow us to use a fake networking object in our tests and a URLSession in the app. You can use the following extension on URLSession to make it usable as a Networking object in your application:

extension URLSession: Networking {
  func fetch(_ url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> Void) {
    self.dataTask(with: url) { data, response, error in
      completion(data, response, error)
    }.resume()
  }
}

To write your test, create a new file in your test target and name it MockNetworking.swift. This file will hold the code used to fake networking responses. Don’t forget to add an @testable import statement for the app target and add the following implementation to this file:

class MockNetworking: Networking {
  var responseData: Data?
  var error: Error?

  func fetch(_ url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> Void) {
    completion(responseData, nil , error)
  }
}

Simple as it might look, this mock networking object is really powerful. You can assign the response data and error, and the networking object will simply invoke the fetch completion handler callback with your predefined response. Users of the mock networking object won’t notice the difference between going to the network or receiving this predefined response which is exactly what we want because now you can use the mock network instead of a URLSession without any changes to your RewardsService. Let’s write some tests for the rewards service and then implement the service itself. Add the following code to the RewardsServiceTests file.

class RewardsServiceTests: XCTestCase {
  func testRewardServiceCanReceiveEmptyRewardsWithNoPoints() {
    let expect = expectation(description: "Expected test to complete")

    let response = RewardsResponseFactory.createResponseWithRewards([], points: nil)
    let mockNetwork = MockNetworking()
    mockNetwork.responseData = response.dataValue
    let service = RewardsService(network: mockNetwork)
    service.fetchRewards { result in
      guard case .success(let response) = result else {
        XCTFail("Expected the rewards to be fetched")
        return
      }

      XCTAssertTrue(response.rewards.isEmpty)
      XCTAssertNil(response.loyaltyPoints)
      expect.fulfill()
    }

    waitForExpectations(timeout: 1, handler: nil)
  }
}

This code uses the factory you created earlier to generate a response object. The extension on RewardsResponse that you added to the test target is used to convert the response into JSON data and you assign this data to the mock network. Next, the rewards service is initialized with the mock network and we call its fetchRewards method with a completion closure that fulfills the test expectation and checks whether the rewards response has the expected values.

Tip:
If you’re not quite sure what a test expectation is, check out Getting Started With Unit Testing - part 2. This blog post covers the basics of testing asynchronous code, including test expectations.

Your tests don’t compile yet because you haven’t implemented the RewardsService yet. Add a new Swift file called RewardsService.swift to your application target and add the following implementation to this file:

struct RewardsService {
  let network: Networking

  func fetchRewards(_ completion: @escaping (Result<RewardsResponse, Error>) -> Void) {
    network.fetch(URL(string: "https://reward-app.com/rewards")!) { data, urlResponse, error in
      let decoder = JSONDecoder()

      decoder.keyDecodingStrategy = .convertFromSnakeCase

      guard let data = data,
        let response = try? decoder.decode(RewardsResponse.self, from: data) else {
          // You should call the completion handler with a .failure result and an error
          return
      }

      completion(.success(response))
    }
  }
}

This is a pretty minimal implementation and that’s fine. Since we’re separating concerns in this little feature as much as possible it isn’t surprising that every single bit of implementation code we’ve written so far was fairly small; every bit of out code only does a single thing, making it easy to use in a test. Go ahead and run your test now, it should succeed!

Try adding a couple more test cases for using the following calls to the RewardsResponseFactory:

let response = RewardsResponseFactory.createResponseWithRewards([.jackpot], points: nil)
let response = RewardsResponseFactory.createResponseWithRewards([.regular], points: 50)
let response = RewardsResponseFactory.createResponseWithRewards([], points: 100)
let response = RewardsResponseFactory.createResponseWithRewards([.small, .regular], points: 70)
let response = RewardsResponseFactory.createResponseWithRewards([.small, .regular], points: nil)

There are many more possible combinations for you to test, go ahead and add as many as you want. Don’t forget to keep your code DRY and use XCTest's setUp method for shared configuration to avoid lots of boilerplate code in every test.

Next steps

If you have implemented the tests I suggested, you should currently have a pretty solid test suite set up for the RewardsService. Your suite tests whether the service can decode many different shapes of responses. If you got stuck implementing these tests, fear not. You can go to this blog post’s accompanying Github Repository and look at the ServiceTests folder to see the results.

But the feature I described at the start of this post isn’t complete yet! Try to add some tests for a RewardsViewModel, maybe add some tests to see whether your coordinator can determine the correct next view controller based on the response from the RewardsService. In the Completed folder in this post’s Github Repository, you’ll find an example of a completed test suite that covers the entire rewards feature!

In summary

In this post, you have learned a lot about testing code that involves using data that comes from a remote source. You saw how you can hide networking logic behind a protocol, allowing you to mock the object that’s responsible for ultimately making the network call.

I have also shown you how you can leverage extensions and Swift’s Codable protocol to generate response data based on the objects you ultimately want to decode. Of course, there are many other ways to obtain such mock data, for example by adding prepared JSON files to your test target, or possibly running a local web server that your tests can use instead of a remote server. These are all valid ways to test networking logic, but I use and prefer the method described in this post myself.

Thank you for reading this post! And as always, feedback, compliments, and questions are welcome. You can find me on Twitter if you want to reach out to me.

An in-depth look at for loops in Swift

In Swift, there is more than one way to loop over a list of elements. If we only consider the loops that aren't intended to create new lists or have side effects, you end up with two types of loops that are very similar; forEach and for item in list, or the for loop. In this week's Quick Tip, I will explain the difference between for and forEach loops, and you I will show some examples that should help you make a decision picking one of either loop methods, depending on your needs.

Understanding for loops

Commonly when you write code that need to do something with every item in a list (Array, Set or other Sequence), you might write something like the following:

let list = [1, 2, 3, 4, 5]

for item in list {
  // do something with item
}

This is possibly the most basic example of a for loop and I'm sure you have written loops like this at least once before you found this Quick Tip. So, what else can we do with for loops?

A cool feature that for loops have is that you can apply a filter to them using a where clause. This means that your loop body is only called for elements that meet the requirements specified in the where clause. The following code is an example of such a filter:

let list = [1, 2, 3, 4, 5]

for item in list where item > 2 {
  print(item)
}

The above code applies a where clause to the for loop that makes sure any element that we receive in the for loop's body is larger than two. This can make your for loop more readable than checking this criterion inside of the for loop body. This segues nicely into learning about the continue keyword that can be used in a for loop!

When you write continue in a for loop, you are telling Swift to stop executing the for loop body for the current item and move over to the next item in the list. The following code produces the same output as the for loop that has a where clause:

let list = [1, 2, 3, 4, 5]

for item in list {
  guard item > 2
    else { continue }

  print(item)
}

In terms of performance, you won't really notice a difference between using a guard or a where clause. You might think that the where clause would make sure you don't loop over as many items but this is not entirely true. Swift will still need to make sure whether each item meets the criteria specified in the where clause. The bigger difference in my personal opinion is readability and semantics. The version that has a where clause tells me exactly under what condition a certain item is passed on to the for loop's body, but it also makes it very easy to spot that there is a condition present in the first place. In the example with the guard, you need to spend a little bit more time looking at the code to figure out that we need the item to be larger than two. Of course, a where clause isn't always the best option, especially if there are multiple, potentially complicated, reasons for an item to be considered skippable.

Sometimes, you don't want to loop over every item in a list. You might want to only loop until you have found a certain item, much like Swift's first(where:) method does. Or maybe you just want to collect items from an array of numbers until you've reached a certain accumulated value. An example of this is shown in the following code snippet:

let list = [1, 2, 3, 4, 5]
var accumulated = 0

for item in list {
  accumulated += item

  if accumulated >= 4 {
    break
  }
}
print(accumulated)

This code loops through items and adds them to an accumulated value. Once this accumulated value is larger than or equal to 4, the break keyword is used to break out of the for loop. Doing this abandons the loop altogether, unlike continue which will only cancel the current executing of the for loop body, allowing the next item to be processed. In the example above, we would loop over 1, then 2 and finally 3. This puts the accumulated value at 6, and we will never reach the 4 or 5 in the list since the loop is cancelled using the break keyword.

Another way to break out of a for loop early is by returning a value:

let list = [1, 2, 3, 4, 5]

func findFirstItemLargerThan(_ threshold: Int) -> Int? {
  for item in list {
    if item > threshold {
      return item
    }
  }

  return nil
}

print(findFirstItemLargerThan(3))

Returning a value from a for loop will prevent further invocations of the loop body and immediately returns the provided value to the caller of the method that contains the for loop. This can be useful if you're implementing a function that should find the first match in a list, like in the example above.

Two other important features of for loops are the ability to use named for loops, and the ability to nest for loops. Let's look at both of these features in a single example:

let list = [1, 2, 3, 4, 5]

outerLoop: for item in list {
  for nestedItem in list {
    if item == nestedItem {
      continue outerLoop
    }

    print(item, nestedItem)
  }
}

By prepending the outer for loop with the outerLoop: label, we can now refer to it from inside of the inner loop. In the preceding code example, we loop over a list of integers in the outer loop. We loop over the same list in the inner loop. If we would omit the if statement and continue statement you would see an output that roughly looks like this:

1 1
1 2
1 3
1 4
1 5
2 1

//etc...

With the continue outerLoop statement in place, you see the following output:

2 1
3 1
3 2
4 1

//etc...

If you try removing the outerLoop label from the continue statement, you'll find that only the entries where item and nestedItem are equal will not be printed since the inner loop would move on to the next item in the inner list. By putting the outerLoop after the continue statement, the outer loop itself will proceed to the next item in the outer list. Pretty powerful stuff!

In this short introduction to for loops, you have seen several features and ways to use for loops. However, I promised you a comparison between for and forEach, so let's have a look at what a forEach loop is, and how it's different from a for loop.

Comparing for loops to forEach loops

Honestly, it's almost unfair to compare for loops to forEach loops, because if you look at how forEach is defined in the Swift language you might quickly understand how they're different:

@inlinable
public func forEach(_ body: (Element) throws -> Void) rethrows {
  for element in self {
    try body(element)
  }
}

We can see that forEach is a method that takes a closure and uses a for loop to iterate over all of the elements in the list and invokes the closure with each element as the argument. Let's look at a simple usage example:

let list = [1, 2, 3, 4, 5]

list.forEach { item in
  print(item)
}

So, what are the differences between for and forEach then? If they both are really just a for loop, they must be pretty much the same right? Unfortunately, they are not the same. A forEach loop lacks the capability to apply a where clause, use a continue statement, you can't return anything from a forEach loop and you also can't break out of it. You can nest forEach loops but you can't label them and you can't access (or continue / break / return from) the outer loop.

A forEach will always iterate over all elements in your list and apply whatever closure you pass it to every single element in your list, no exceptions.

In summary

In this blog post, you learned a lot about for loops, and you saw how Swift implements the forEach method. Now that you understand the differences between the two different ways of looping, you should be able to determine which loop you need. Do you need to break out of the loop early? Or maybe you want to use a where clause to prevent the loop body from being called for every item. Or maybe you're just looking for the first item to match a certain condition and you want to return that item. In all of these scenarios you'll want to use a for loop or a built-in function like first(where:) from the Swift standard library.

Alternatively, if you want to loop over every single item in an array or another type of list, without any exceptions, you can safely use forEach since that's what it's good at.

Thanks for reading this blog post and as always, your input, feedback, and questions are highly appreciated. Reach out on Twitter if you want to get in touch!