Effectively using static and class methods and properties

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

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

Using static properties for configuration

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

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

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

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

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

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

Using static properties for expensive objects

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

struct Blogpost {
  private static var dateFormatter = ISO8601DateFormatter()

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

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

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

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

Creating a factory with static methods

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

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

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

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

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

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

Understanding how class methods differ from static methods

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

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

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

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

Understanding when to create shared instances

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

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

These are all examples of shared instances.

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

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

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

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

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

struct NetworkingObject {
  let urlSession: URLSession

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

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

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

Knowing when to use a singleton

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

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

protocol Database {
  /* requirements */
}

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

  private init() {}
}

struct UserProvider {
  let database: Database
}

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

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

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

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

In summary

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

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

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

What is the “some” keyword in Swift?

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

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

Exploring opaque result types

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

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

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

protocol ListItemDisplayable {
  var name: String { get }
}

struct Shoe: ListItemDisplayable {
  let name: String
}

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

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

struct Shoe: ListItemDisplayable {
  let name: String
}

struct Shorts: ListItemDisplayable {
  let name: String
}

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

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

Opaque result types for protocols with associated types

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

protocol ListDataSource {
  associatedtype ListItem: ListItemDisplayable

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

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

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

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

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

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

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

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

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

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

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

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

Opaque result types and Self requirements

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

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

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

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

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

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

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

print(left == right)

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

Opaque return types as reverse generics

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

protocol ListDataSource {
  associatedtype ListItem: ListItemDisplayable

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

  init(items: [ListItem])
}

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

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

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

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

Using opaque return types in your projects

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

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

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

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

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

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

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

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

Rewriting the example above with some looks as follows:

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

Much better, right?

Verify your existential usage for Swift 6 with Xcode 15.3

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

In summary

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

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

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

Generics in Swift explained

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

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

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

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

Reverse engineering Array

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

var codableArray = WrappedArray<Codable>

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

var codableArray = WrappedArray<Codable>

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

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

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

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

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

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

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

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

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

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

Applying generics in your code

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

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

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

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

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

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

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

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

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

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

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

Neat, right! And instead of the following code:

let viewModel = FavoritesViewModel()

You can now write:

let viewModel = ListViewModel<Favorite>()

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

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

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

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

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

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

In summary

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

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

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

Efficiently loading images in table views and collection views

When your app shows images from the network in a table view or collection view, you need to load the images asynchronously to make sure your list scrolls smoothly. More importantly, you’ll need to somehow connect the image that you’re loading to the correct cell in your list (instead of table view or collection view, I’m going to say list from now on). And if the cell goes out of view and is reused to display new data with a new image, you’ll want to cancel the in-progress image load to make sure new images are loaded immediately. And, to make sure we don’t go to the network more often than needed, we’ll need some way to cache images in memory, or on disk, so we can use the local version of an image if we’ve already fetched it in the past. Based on this, we can identify three core problems that we need to solve when loading images for our cells asynchronously:

  1. Setting the loaded image on the correct cell.
  2. Canceling an in-progress load when a cell is reused.
  3. Caching loaded images to avoid unneeded network calls.

In this post, I’m going to show you how to build a simple image loader class, and write a table view cell that will help us solve all these problems. I will also show you how you can use the same image loader to enhance UIImage with some fancy helpers.

The loader and techniques in this post focus on UIKit do not use Swift's async/await. If you're interested in building an image loader that leverages async/await make sure you check out this post alongside this one.

Building a simple image loader

When you make a GET request using URLSession, you typically do so through a data task. Normally, you don’t hold on to that data task because you don’t need it. But if you keep a reference to your data task around, you can cancel it at a later time. I’m going to use a dictionary of [UUID: URLSessionDataTask] in the image loader we’re building because that will allow me to keep track of running downloads and cancel them later. I’m also going to use a dictionary of [URL: UIImage] as a simple in-memory cache for loaded images. Based on this, we can begin writing the image loader:

class ImageLoader {
  private var loadedImages = [URL: UIImage]()
  private var runningRequests = [UUID: URLSessionDataTask]() 
}

We can also implement a loadImage(_:completion:) method. This method will accept a URL and a completion handler, and it’s going to return a UUID that’s used to uniquely identify each data task later on. The implementation looks as follows:

func loadImage(_ url: URL, _ completion: @escaping (Result<UIImage, Error>) -> Void) -> UUID? {

  // 1
  if let image = loadedImages[url] {
    completion(.success(image))
    return nil
  }

  // 2
  let uuid = UUID()

  let task = URLSession.shared.dataTask(with: url) { data, response, error in
    // 3
    defer {self.runningRequests.removeValue(forKey: uuid) }

    // 4
    if let data = data, let image = UIImage(data: data) {
      self.loadedImages[url] = image
      completion(.success(image))
      return
    }

    // 5
    guard let error = error else {
      // without an image or an error, we'll just ignore this for now
      // you could add your own special error cases for this scenario
      return
    }

    guard (error as NSError).code == NSURLErrorCancelled else {
      completion(.failure(error))
      return
    }

    // the request was cancelled, no need to call the callback
  }
  task.resume()

  // 6
  runningRequests[uuid] = task
  return uuid
}

Let’s go over the preceding code step by step, following the numbered comments.

  1. If the URL already exists as a key in our in-memory cache, we can immediately call the completion handler. Since there is no active task and nothing to cancel later, we can return nil instead of a UUID instance.
  2. We create a UUID instance that is used to identify the data task that we’re about to create.
  3. When the data task completed, it should be removed from the running requests dictionary. We use a defer statement here to remove the running task before we leave the scope of the data task’s completion handler.
  4. When the data task completes and we can extract an image from the result of the data task, it is cached in the in-memory cache and the completion handler is called with the loaded image. After this, we can return from the data task’s completion handler.
  5. If we receive an error, we check whether the error is due to the task being canceled. If the error is anything other than canceling the task, we forward that to the caller of loadImage(_:completion:).
  6. The data task is stored in the running requests dictionary using the UUID that was created in step 2. This UUID is then returned to the caller.

Note that steps 3 through 5 all take place in the data task’s completion handler. This means that the order in which the listed steps execute isn’t linear. Step 1 and 2 are executed first, then step 6 and then 3 through 5.

Now that we have logic to load our images, let’s at some logic that allows us to cancel in-progress image downloads too:

func cancelLoad(_ uuid: UUID) {
  runningRequests[uuid]?.cancel()
  runningRequests.removeValue(forKey: uuid)
}

This method receives a UUID, uses it to find a running data task and cancels that task. It also removes the task from the running tasks dictionary, if it exists. Fairly straightforward, right?

Let’s see how you would use this loader in a table view’s cellForRowAtIndexPath method:

// 1
let token = loader.loadImage(imageUrl) { result in
  do {
    // 2
    let image = try result.get()
    // 3
    DispatchQueue.main.async {
      cell.cellImageView.image = image
    }
  } catch {
    // 4
    print(error)
  }
}

// 5
cell.onReuse = {
  if let token = token {
    self.loader.cancelLoad(token)
  }
}

Let’s go through the preceding code step by step again:

  1. The image loader’s loadImage(_:completion:) method is called, and the UUID returned by the loader is stored in a constant.
  2. In the completion handler for loadImage(_:completion:), we extract the result from the completion’s result argument.
  3. If we successfully extracted an image, we dispatch to the main queue and set the fetched image on the cell’s imageView property. Not sure what dispatching to the main queue is? Read more in this post
  4. If something went wrong, print the error. You might want to do something else here in your app.
  5. I’ll show you an example of my cell subclass shortly. The important bit is that we use the UUID that we received from loadImage(_:completion:) to cancel the loader’s load operation for that UUID.

Note that we do this in the cellForRowAt method. This means that every time we’re asked for a cell to show in our list, this method is called for that cell. So the load and cancel are pretty tightly coupled to the cell’s life cycle which is exactly what we want in this case. Let’s see what onReuse is in a sample cell:

class ImageCell: UITableViewCell {
  @IBOutlet var cellImageView: UIImageView!
  var onReuse: () -> Void = {}

  override func prepareForReuse() {
    super.prepareForReuse()
    onReuse()
    cellImageView.image = nil
  }
}

The onReuse property is a closure that we call when the cell’s prepareForReuse method is called. We also remove the current image from the cell in prepareForReuse so it doesn’t show an old image while loading a new one. Cells are reused quite often so doing the appropriate cleanup in prepareForReuse is crucial to prevent artifacts from old data on a cell from showing up when you don’t want to.

If you implement all of this in your app, you’ll have a decent strategy for loading images. You would probably want to add a listener for memory warnings that are emitted by your app’s Notification Center, and maybe you would want to cache images to disk as well as memory too, but I don’t think that fits nicely into the scope of this article for now. Keep these two features in mind though if you want to implement your own image loader. Especially listening for memory warnings is important since your app might be killed by the OS if it consumes too much memory by storing images in the in-memory cache.

Enhancing UIImageView to create a beautiful image loading API

Before we implement the fancy helpers, let’s refactor our cell and cellForRowAt method so they already contain the code we want to write. The prepareForReuse method is going to look as follows:

override func prepareForReuse() {
  cellImageView.image = nil
  cellImageView.cancelImageLoad()
}

This will set the current image to nil and tells the image view to stop loading the image it was loading. All of the image loading code in cellForRowAt should be replaced with the following:

cell.cellImageView.loadImage(at: imageUrl)

Yes, all of that code we had before is now a single line.

To make this new way of loading and canceling work, we’re going to implement a special image loader class called UIImageLoader. It will be a singleton object that manages loading for all UIImageView instances in your app which means that you end up using a single cache for your entire app. Normally you might not want that, but in this case, I think it makes sense. The following code outlines the skeleton of the UIImageLoader:

class UIImageLoader {
  static let loader = UIImageLoader()

  private let imageLoader = ImageLoader()
  private var uuidMap = [UIImageView: UUID]()

  private init() {}

  func load(_ url: URL, for imageView: UIImageView) {

  }

  func cancel(for imageView: UIImageView) {

  }
}

The loader itself is a static instance, and it uses the ImageLoader from the previous section to actually load the images and cache them. We also have a dictionary of [UIImageView: UUID] to keep track of currently active image loading tasks. We map these based on the UIImageView so we can connect individual task identifiers to UIImageView instances.

The implementation for the load(_:for:) method looks as follows:

func load(_ url: URL, for imageView: UIImageView) {
  // 1
  let token = imageLoader.loadImage(url) { result in
    // 2
    defer { self.uuidMap.removeValue(forKey: imageView) }
    do {
      // 3
      let image = try result.get()
      DispatchQueue.main.async {
        imageView.image = image
      }
    } catch {
      // handle the error
    }
  }

  // 4
  if let token = token {
    uuidMap[imageView] = token
  }
}

Step by step, this code does the following:

  1. We initiate the image load using the URL that was passed too load(_:for:).
  2. When the load is completed, we need to clean up the uuidMap by removing the UIImageView for which we’re loading the image from the dictionary.
  3. This is similar to what was done in cellForRowAt before. The image is extracted from the result and set on the image view itself.
  4. Lastly, if we received a token from the image loader, we keep it around in the [UIImageView: UUID] dictionary so we can reference it later if the load has to be canceled.

The cancel(for:) method has the following implementation:

func cancel(for imageView: UIImageView) {
  if let uuid = uuidMap[imageView] {
    imageLoader.cancelLoad(uuid)
    uuidMap.removeValue(forKey: imageView)
  }
}

If we have an active download for the passed image view, it’s canceled and removed from the uuidMap. Very similar to what you’ve seen before.

All we need to do now is add an extension to UIImageView to add the loadImage(at:) and cancelImageLoad() method you saw earlier:

extension UIImageView {
  func loadImage(at url: URL) {
    UIImageLoader.loader.load(url, for: self)
  }

  func cancelImageLoad() {
    UIImageLoader.loader.cancel(for: self)
  }
}

Both methods pass self to the image loader. Since the extension methods are added to instances of UIImageView, this helps the image loader to connect the URL that it loads to the UIImageView instance that we want to show the image, leaving us with a very simple and easy to use API! Cool stuff, right?

What’s even better is that this new strategy can also be used for images that are not in a table view cell or collection view cell. It can be used for literally any image view in your app!

In summary

Asynchronously loading data can be a tough problem on its own. When paired with the fleeting nature of table view (and collection view) cells, you run into a whole new range of issues. In this post, you saw how you can use URLSession and a very simple in-memory cache to implement a smart mechanism to start, finish and cancel image downloads.

After creating a simple mechanism, you saw how to create an extra loader object and some extensions for UIImageView to create a very straightforward and easy to use API to load images from URLs directly into your image views.

Keep in mind that the implementations I’ve shown you here aren’t production-ready. You’ll need to do some work in terms of memory management and possibly add a disk cache to make these objects ready for prime time.

If you have any questions about this topic, have feedback or anything else, don’t hesitate to shoot me a message on Twitter.

Appropriately using DispatchQueue.main

Lots of iOS developers eventually run into code that calls upon DispatchQueue.main. It's often clear that this is done to update the UI, but I've seen more than a handful of cases where developers use DispatchQueue.main as an attempt to get their code to work if the UI doesn't update as they expect, or if they run into crashes they don't understand. For that reason, I would like to dedicate this post to the question "When should I use DispatchQueue.main? And Why?".

Understanding what the main dispatch queue does

In iOS, we use dispatch queues to perform work in parallel. This means that you can have several dispatch queues running at the same time, and they can all be performing tasks simultaneously. In general, dispatch queues will only perform one task at a time in a first-in, first-out kind of fashion. They can, however, be configured to schedule work concurrently.

The main dispatch queue is a queue that runs one task at a time. It's also the queue that performs all layout updates. If somebody talks about the importance of not blocking the main thread, what they're really saying is that they don't want to keep the main dispatch queue busy for too long. If you keep the main queue busy too long, you will notice that your application's scroll performance becomes choppy, animations stutter and buttons become unresponsive.

The reason for this is that the main queue is responsible for everything UI related, but like we already established, the main queue is a serial queue. So if the main queue is busy doing something, it can't respond to user input or draw new frames of your animation.

A lot of code in iOS is code that can take a while to run. For example, making a network request is a good example of code that is very slow to execute. Once the network call is sent off to the server, the code has to wait for a response. While waiting, that queue isn't doing anything else. When the response comes back a couple of seconds later, the queue can process the results and move on to the next task. If you would perform this work on the main queue, your app wouldn't be able to draw any UI or respond to user input for the entire duration of the network request.

We can summarise this whole section in just a short sentence. The main queue is responsible for drawing UI and responding to user input. In the next section, we'll take a closer look at when we should use DispatchQueue.main and what that does exactly.

Using DispatchQueue.main in practice

Sticking with the example of making a network call, you can assume that network calls are made on their own queue, away from the main queue. Before we continue, I want to refresh your memory and show you what the code for a network call looks like:

URLSession.shared.dataTask(with: someURL) { data, response, error in 

}

The data task is created with a completion closure which is called when the network call completes. Since the data that's returned by the server might still need to be processed, iOS calls your completion closure on a background queue. Usually, this is great. I don't think there's any situation where you won't want to do any processing to the data that you receive from the server at all. Sometimes you might have to do more processing than other times, but no processing at all is very rare. Once you're done processing the data however, you'll probably want to update your UI.

Since you know you're not on the main queue when handling a network response, you'll need to use DipatchQueue.main to make sure your UI updates on the main queue. The following code is an example of reloading a table view on the main queue.

DispatchQueue.main.async {
  self.tableView.reload()
}

This code looks simple enough, right? But what's really going on here?

DispatchQueue.main is an instance of DispatchQueue. All dispatch queues can schedule their work to be executed sync or async. Typically you will want to schedule work async because scheduling your work synchronously would halt the execution of the current thread, wait for the target thread execute the closure that you pass to sync, and then resume the current thread. Using async allows the current thread to resume while the target thread can schedule and perform your closure when needed. Let's look at an example:

func executesAsync() {
  var result = 0

  DispatchQueue.global().async {
    result = 10 * 10
  }

  print(result) // 0
}

func executesSync() {
  var result = 0

  DispatchQueue.global().sync {
    result = 10 * 10
  }

  print(result) // 100
}

Both of the preceding functions look very similar. The main difference here is that executesAsync dispatches to the main queue asynchronously, causing result to be printed before, result is updated. The executesSync function dispatches to the main queue synchronously, which results in the execution of executesSync to be paused until the closure passed to DispatchQueue.main.sync finishes executing. This means that result is updated when print is called.

Think about the preceding example in the context of reloading a table view. If we would use sync instead of async, the executing of the network call completion closure is paused while the main thread reloads the table view. Once the table view is reloaded, the execution of the closure is resumed. By using async, the completion closure continues its execution and the table view will reload whenever the main queue has time to do so. This, hopefully, is pretty much instantaneously because if it takes a while it's probably a symptom of blocking the main thread.

Knowing when to use DispatchQueue.main

Now that you know what the main queue is, and you've seen an example of how and when DispatchQueue.main is used, how do you know when you should use DispatchQueue.main in your project?

The simplest answer is to always use it when you're updating UI in a delegate method or completion closure because you don't control how or when that code is called. In addition, Xcode will crash your app if it detects that you're doing UI work away from the main thread. While this is a very convenient feature that has helped me prevent bugs every now and then, it's not reliable 100% of the time and there are better ways to make sure your code runs on the main thread when it has to.

Remember to always dispatch your queue to the main queue asynchronously using DispatchQueue.main.async to avoid blocking the current thread. And potentially even deadlocking your app, which can happen if you call DispatchQueue.main.sync from code that is already on the main queue. Dispatching to the main queue with async does not carry this same risk, even if you're already on the main queue.

Let's look at one last example. If you fetch a user's current push notification permissions or request their contacts, you know that operation runs asynchronously and it might take a while. If you want to update the UI in the completion closure that's used for these operations, it's best to explicitly make sure your UI updates are done on the main queue by wrapping your UI updates in a DispatchQueue.main.async block.

In Summary

Writing applications that use multiple queues can be really complicated. As a general rule, keep in mind that the main queue is reserved for UI work. That doesn't mean that all non-UI work has to go off the main queue, but it does mean that all the UI work most be on the main queue and all other work can be somewhere else if you want it to be. For example, if you know an operation might take a while to complete.

In other words, the short answer to the question from the beginning of this article, you should use DispatchQueue.main to send UI related work from non-main queues to the main queue.

If you have questions about this article, feedback, suggestions or anything else, feel free to reach out to me on Twitter

Changes to location access in iOS 13

If you're working on an app that requires access to a user's location, even when your user has sent your app to the background, you might have noticed that when you ask the user for the appropriate permission, iOS 13 shows a different permissions dialog than you might expect. In iOS 12 and below, when you ask for so-called always permissions, the user can choose to allow this, allow location access only in the foreground or they can decide to now allow location access at all. In iOS 13, the user can choose to allow access once, while in use or not at all. The allow always option is missing from the permissions dialog completely.

In this post, you will learn what changes were made to location access in iOS 13, why there is no more allow always option and lastly you'll learn what allow once means for your app. We have a lot to cover so let's dive right in.

Asking for background location permissions in iOS 13

The basic rules and principles of asking for location permissions in applications haven't changed since iOS 12. So all of the code you wrote to ask a user for location permissions should still work the same in iOS 13 as it did in iOS 12. The major differences in location permissions are user-facing. In particular, Apple has doubled down on security and user-friendliness when it comes to background location access. A user's location is extremely privacy-sensitive data and as a developer, you should treat a user's location with extreme caution. For this reason, Apple decided that accessing a user's location in the background deserves a special, context-sensitive prompt rather than a prompt that presented while your app is in the foreground.

If you want to access a user's location in the background, you can ask them for this permission using the following code:

let locationManager = CLLocationManager()

func askAlwaysPermission() {
  locationManager.requestAlwaysAuthorization()
}

This code will cause the location permission dialog to pop up if the current CLAuthorizationStatus is .notDetermined. The user can now choose to deny location access, allow once or to allow access while in use. If the user chooses access while in use, your location manager's delegate will be informed of the choice and the current authorization status will be .authorizedAlways.

But wait. The user chooses when in use! Why are we now able to always access the user's location?

Because Apple is making background location access more user-centered, your app is tricked into thinking it has access to the user's background even if the app is in the background by giving it provisional authorization. You are expected to handle this scenario just like you would normally. Set up your geofencing, start listening for location updates and more. When your app is eventually backgrounded and tries to use the user's location, iOS will wait for an appropriate moment to inform the user that you want to use their location in the background. The user can then choose to either allow this or to deny it.

Note that your app is not aware of this interaction and location events in the background are not delivered to your app until the user has explicitly granted you permission to access their location in the background. This new experience for the user means that you must ensure that your user understands why you need access to their location in the background. If you're building an app that gives a user suggestions for great coffee places near their current location, you probably don't need background location access. All you really need to know is where the user is while using your app so you can give them good suggestions that are nearby, in this example, it's very unlikely that the user will understand why the background permission dialog pops-up and they will deny access.

However, if you're a home automation app that uses geofences to execute a certain home automation when a user enters or leaves a certain area, it makes sense for you to need the always access permission so you can execute a specific automation even if the user isn't actively using your app.

Speaking of geofences and similar APIs that require always authorization in iOS 12, iOS 13 allows you to set up geofences, listen for significant location changes and monitor even if your app only has when in use permission. This allows you to monitor geofences and more as long as your app is active. Usually, your app is active when it's foregrounded but there are exceptions.

If you have an app where you don't really need always authorization but you do want to allow your user to send your app to the background while you access their location, for example, if you're building some kind of scavenger hunt app where a user must enter a geofence that you've set up, you can set the allowsBackgroundLocationUpdates property on your location manager to true and your app will show the blue activity indicator on the user's status bar. While this activity indicator is present, your app remains active and location-related events are delivered to your app.

All in all, you shouldn't have to change much, if anything at all, for the new location permission strategy in iOS 13. Your app's location permissions will change according to the user's preferences just like they always have. The biggest change is in the UI and the fact that the user will not be prompted for always authorization until you actually try to use their location in the background.

Let's look at another change in iOS 13 that enables users to allow your app to access the user's location once.

Understanding what it means when a user allows your app to access their location once

Along with provisional authorization for accessing the user's location in the background, users can now choose to allow your app to access their location once. If a user chooses this option when the location permissions dialog appears, your app will be given the .authorizedWhenInUse authorization status. This means that your app can't detect whether you will always be able to access the user's location when they're using your app, or if you can access their location only for the current session. This, again, is a change that Apple made to improve the user's experience and privacy.

If a user chooses the allow once permission option, your app can access their current location until your app is moved to the background and becomes inactive. If your app becomes active again, you have to ask for location permission again.

It's recommended that you don't ask for permission as soon as the app launches. Instead, think about why you asked for location permission in the first place. The user probably was trying to do something with your app where it made sense to access their location. You should wait for the next moment where it makes sense to access the user's location rather than asking them for permission immediately when your app returns to the foreground.

If you think your app should have access to the user's location even if the app is backgrounded for a while, you can set the location manager's allowsBackgroundLocationUpdates to true and your app's session remains active until the user decides to stop it. This would be appropriate for an app that tracks hikes or runs where a user would expect you to continue actively tracking their location even while their phone is in their pocket.

Similar to provisional authorization, this change shouldn't impact your code too much. If you're already dealing with your user's locations in a careful and considerate way chances are that everything in your will work perfectly fine with the new allow once permission.

In Summary

Apple's efforts to ensure that your user's data is safe and protected are always ongoing. This means that they sometimes make changes to how iOS works that impact our apps in surprising ways and I think that location access in iOS 13 is no exception to this. It can be very surprising when you're developing your app on iOS 13 and things don't work like you're used to.

In this blog post, you learned what changes Apple has made to location permissions, and how they impact your code. You learned that apps now get provisional access to a user's location in the background and that provisional access can be converted to permanent access if you attempt to use a user's location and the background and they allow it. You also learned that your app won't receive any background location events until the user has explicitly allowed this.

In addition to provisional location access, you learned about one-time location access. You saw that your app will think it has the while in use permissions and that those permissions will be gone the next time the user launches your app. This means that you should ensure to ask for location permissions when it makes sense rather than doing this immediately. Poorly timed location permission dialogs are far more likely to result in a negative response from a user than a well thought out experience.

If you have any questions or feedback for about this post or any other post on my blog, make sure to reach out on Twitter.

Using launch arguments for easier Core Data debugging

If you use Core Data in your apps, you might be aware that the larger and more complicated your set up becomes, the harder it is to debug. It's at this point where you might start to get frustrated with Core Data and its black-box kind of implementation. You might think that you simply have to trust that Core Data will do the ideal thing for your app.

Furthermore, you might have a set up with multiple managed object contexts, each confined to its own thread. And when your app crashes sometimes, you think it's related to Core Data in one way or the other, but you're not quite sure how to debug it. Especially because your app only crashes sometimes rather than all the time.

In this post, I want to show you some Core Data related Launch Arguments that will help you debug and validate your Core Data related code. Let's start by using Launch Arguments to see what Core Data does under the hood. Next, we'll see how to detect most of your threading problems in Core Data.

Knowing what Core Data does under the hood

Sometimes when you use Core Data you want to open the underlying SQLite file to see whether your data is actually stored as expected, or maybe you want to inspect the database structure. To do this, you need to know where Core Data has stored your SQLite file, this can be especially challenging when you're running on the simulator. Because let's be honest, we don't know what the UUID of our simulator is and we most certainly don't want to have to figure out its location every time we need to find our SQLite file.

Luckily, you can use a Launch Argument to get Core Data to log some information to the console. To add a Launch Argument, use to top menu in Xcode to go to Product -> Scheme -> Edit Scheme... (cmd + >). Select the Run configuration and go to the Arguments tab as shown in the following screenshot:

Example of the schema window

To get Core Data to log information to the console, click the + button under the Launch Arguments and add the following argument:

-com.apple.CoreData.SQLDebug 1

The result should look as follows:

Screenshot of Core Data debug flags in scheme editor

If you run your Core Data app after setting this Launch Argument, Core Data will start logging basic information like where the backing SQLite file is stored, and what queries are executed.

You can increase the log level all the way up to level 4, at that point Core Data will log pretty much everything you might want to know about what's going on under the hood and more. For example, this might help you notice that Core Data is performing lots of SQLite queries to fetch object relationships. And based on that discovery you might decide that certain fetch requests should automatically fetch certain relationships by setting your fetch request's relationshipKeyPathsForPrefetching property.

In case you're curious, the following list describes the different Core Data SQLDebug log levels:

  1. SQL statements and their execution time
  2. Values that are bound in the statement
  3. Fetched managed object IDs
  4. SQLite EXPLAIN statement

These four log levels give you a lot of information that you can use to improve your apps. Of course, the usefulness of certain log levels like level four depends entirely on your knowledge of SQLite. But even if you're not well versed in SQLite, I recommend to take a look at all of the log levels sometimes, they can produce some interesting outputs.

Detecting threading problems in Core Data

One of the biggest frustrations you might have with Core Data is random crashes due to threading problems. You're supposed to use managed object context and managed objects only on the threads that they were created on, and violating this rule might crash your app. However, usually your app won't crash and everything is fine. But then every now and then a random crash pops up. You can tell that it's Core Data related but you might not be sure where the error is coming from exactly.

Luckily, the Core Data team has thought of a way to help you get rid of these crashes. In Xcode, you can add the -com.apple.CoreData.ConcurrencyDebug 1 Launch Argument to run Core Data in an extra strict mode. Whenever Core Data encounters a threading violation, your app will immediately crash and Xcode will point out the exact line where the violation occurred.

I can recommend everybody to use this Launch Argument in development because it will help you catch threading problems early, and forces you to fix them right away rather than getting some nasty surprises when your app is already published to the App Store.

In Summary

While you can’t eliminate all bugs and performance issues with debug flags, it does help to have some tools available that you can use to make your problems more visible. Whether it's making you app crash if you break Core Data's threading rules, or gaining insights into the different SQLite queries Core Data does under the hood, it's always good to understand how your code behaves under the hood.

I hope these debug flags will help save you loads of time, just like they do for me. If you have questions, feedback or simply want to reach out to me, don't hesitate to contact me on Twitter

Adding haptic feedback to your app with CoreHaptics

One of iOS 13's more subtle, yet amazingly fun and powerful frameworks is CoreHaptics. With this framework, you can add tactile feedback to your app. When implemented correctly, this kind of feedback will delight and amaze your users. It will make your app feel alive like it's physically responsive to the touch input from your user. If you want to see what haptic feedback feels like in a real app, check out the CocoaHub app. It uses haptic feedback when you switch tabs in the app and in my opinion, this feels really nice.

In this blog post you will learn about the following:

  • Understanding the different types of haptic feedback
  • Adding haptic feedback to your app
  • Using AHAP files to define your haptics

By the end of this post, you should be able to add beautiful, elegant haptic experiences to your app that will feel natural and exciting to your users. Because when implemented correctly, haptics can really take any touch to the next level.

Understanding the different types of haptic feedback

Ever since the iPhone 6s, we have been able to add haptics to apps. Haptic feedback is perceived as tiny little nudges or taps that you get from your phone when something happens. It's almost like a very short vibration, except it seems to have a little bit more force to it. The same type of technology that's found in the iPhone 6s and up can be found in the Apple watch and in Apple's trackpads. If you have a MacBook (pro) and you click the trackpad, nothing actually clicks. That's the taptic engine giving you haptic feedback.

The haptics that exist on iOS 12, and in your MacBook's trackpad are fairly limited in what you can with them. With iOS 13 Apple has made a huge change to the haptic capabilities we have. Instead of very simple haptics that can't express much more than that clicking feeling, we can almost make the user feel sound through haptics. This is all enabled through the CoreHaptics framework.

In this framework, we have two key types of haptic feedback, continuous and transient. Transient haptic feedback is like a tap, or a nudge and is typically used when you tap a button, when you need the user's attention or if you want to enrich the visual experience of two objects bumping into each other with some nice haptic feedback. For this kind of haptic feedback, you can configure its intensity to specify how intense the tap should be, and its sharpness. A sharp haptic will feel more urgent to the user, while a less sharp haptic will feel very calm. If this sounds abstract, or strange, I recommend that you come back to this section after playing with haptics a bit. You'll realize that sharp and intense are actually very clever ways to describe what certain haptic feedback feels like.

Continuous haptic feedback is more like a vibration pattern. It still has that typical haptic click feeling, except there are many of them in rapid succession. For a continuous event, you can specify exactly the same parameters that are available for transient feedback, except you can also set the event's duration to determine how long the haptic feedback should keep going for.

It's also possible to define how certain characteristics of a haptic event should change over time. For example, to create an increasing and then decreasing continuous haptic, you would define a single haptic event and apply dynamic parameters or a parameter curve to have the haptic fade in and out over time. I will show an example of both dynamic parameters and parameter curves in the next section.

In addition to tactile feedback, CoreHaptics also has special audio haptic event types. Audio haptic can either be a custom audio file that is played or a generated waveform that is configured similar to a haptic event, except it's played as audio rather than a haptic pulse. You can configure parameters like the audio's pitch, volume and decay to create some very interesting audio effects to accompany your haptic experiences.

Adding haptics to your app

All haptic feedback by CoreHaptics is played through an instance of CHHapticEngine. You can create an instance of the haptic engine as follows:

let engine = try CHHapticEngine()

Note that you should define the haptic engine somewhere where the instance will stay around for as long as you need it. You should avoid creating an engine every time you want to play haptic feedback. Instead, make it a property on the object that will end up managing your haptics implementation so it stays around for a longer time.

You should also set the engine's stoppedHandler and its resetHandler. The stoppedHandler is called in case the haptic engine is stopped due to external factors or if it's finished playing your haptic events. The resetHandler is called if something went wrong and the engine has to be reset. In the reset handler, you will typically want immediately start the engine again.

Before you can play haptic feedback, you must start the haptic engine by calling its start method:

do {
  try engine.start()
} catch {
  // failed to start the engine
}

Once the engine is ready to play haptics, we can define our haptic pattern, ask the haptic engine to create an instance of CHHapticPatternPlayer and then play the haptics. Let's create a simple transient haptic event:

let event = CHHapticEvent(eventType: .hapticTransient, parameters: [
  CHHapticEventParameter(parameterID: .hapticSharpness, value: 0.5),
  CHHapticEventParameter(parameterID: .hapticIntensity, value: 1)
], relativeTime: 0)

do {
  let pattern = try CHHapticPattern(events: [event], parameters: [])
  let player = try engine.makePlayer(with: pattern)
  try player?.start(atTime: CHHapticTimeImmediate)
} catch {
  // something went wrong
}

The code above defines a haptic event. Haptic events are the building blocks that can be used to define a haptic pattern. In this case, we have a pattern that only contains a single haptic event. It is possible to, for example, play a transient haptic event first, then play a continuous haptic for a second, and then play another transient haptic event. You can use the relativeTime parameter on the CHHapticEvent initializer to specify an offset for each event. So if you want to play several haptic taps in succession, you would use the relativeTime to make sure each haptic starts when needed rather than playing the entire pattern all at once.

Once the pattern is defined, we can create an instance of CHHapticPattern. The parameters property in the pattern's initializer is an array of CHHapticDynamicParameter items that define how the haptic pattern's intensity, sharpness, and other characteristics should be manipulated over time. I'll show you an example shortly.

Once the pattern is created, we can ask the haptic engine for a player, and we can then play our haptic pattern on the device. You can specify a delay for the player's start time if needed. We use CHHapticTimeImmediate in this example, but you can specify any TimeInterval (or Double) you want. Fairly straightforward, right? In the following examples, I will only show the steps to create the CHHapticPattern. All steps involving obtaining and starting a player are identical for all haptic patterns.

Let's see an example of building a haptic pattern as described earlier where we have a transient event, then a continuous one, and then another transient one:

let events = [
  CHHapticEvent(eventType: .hapticTransient, parameters: [
    CHHapticEventParameter(parameterID: .hapticSharpness, value: 0.5),
    CHHapticEventParameter(parameterID: .hapticIntensity, value: 1)
  ], relativeTime: 0),
  CHHapticEvent(eventType: .hapticContinuous, parameters: [
    CHHapticEventParameter(parameterID: .hapticSharpness, value: 1),
    CHHapticEventParameter(parameterID: .hapticIntensity, value: 0.7),
  ], relativeTime: 0.1, duration: 0.5),
  CHHapticEvent(eventType: .hapticTransient, parameters: [
    CHHapticEventParameter(parameterID: .hapticSharpness, value: 0.5),
    CHHapticEventParameter(parameterID: .hapticIntensity, value: 1)
  ], relativeTime: 0.7)
]

let pattern = try CHHapticPattern(events: events, parameters: [])

Note that the second haptic event, the continuous one, defines its duration while the transient events don't. This is because a transient haptic event is a single event, that doesn't have a duration. If you try running this pattern on a device, you will feel a short tap, then a sharp buzz, followed by another short tap. Cool, right!

Let's see how we can use parameters to create an interesting continuous haptic pattern.

let events = [
  CHHapticEvent(eventType: .hapticContinuous, parameters: [
    CHHapticEventParameter(parameterID: .hapticSharpness, value: 1),
    CHHapticEventParameter(parameterID: .hapticIntensity, value: 0.8),
  ], relativeTime: 0.1, duration: 3)
]

let parameters = [
  CHHapticDynamicParameter(parameterID: .hapticIntensityControl, value: 0.3, relativeTime: 0),
  CHHapticDynamicParameter(parameterID: .hapticIntensityControl, value: 1, relativeTime: 1),
  CHHapticDynamicParameter(parameterID: .hapticIntensityControl, value: 0.5, relativeTime: 2),
  CHHapticDynamicParameter(parameterID: .hapticIntensityControl, value: 1, relativeTime: 2.5)]

let pattern = try CHHapticPattern(events: events, parameters: parameters)

Note that the value property of a dynamic parameter is used as a multiplier for the value specified on the parameter it manipulates. So in this case setting a value of 1 on the dynamic pattern equals an intensity of 0.8. A value of 0.5 equals an intensity of 0.4, etc.

If you play this you will notice that the changes for the dynamic parameters are very abrupt. After one second we jump to a higher intensity. One second later, the pattern jumps to a lower intensity and so forth. If you want to apply a more streamlined transition, we can use CHHapticParameterCurve instead of CHHapticDynamicParameter. Let's see how:

let events = [
  CHHapticEvent(eventType: .hapticContinuous, parameters: [
    CHHapticEventParameter(parameterID: .hapticSharpness, value: 1),
    CHHapticEventParameter(parameterID: .hapticIntensity, value: 0.8),
  ], relativeTime: 0.1, duration: 3)
]

let dynamicParameters = [
  CHHapticParameterCurve(parameterID: .hapticIntensityControl,
                         controlPoints: [.init(relativeTime: 0, value: 0),
                                         .init(relativeTime: 1, value: 1),
                                         .init(relativeTime: 2, value: 0.5),
                                         .init(relativeTime: 3, value: 1)],
                         relativeTime: 0)]

let pattern = try CHHapticPattern(events: events, parameterCurves: dynamicParameters)

Everything looks very similar except we define an array of CHHapticParameterCurve instead of CHHapticDynamicParameter. Note that there is a controlPoints argument on the CHHapticParameterCurve initializer. This is where we specify how we want our pattern to change over time. We create instances of CHHapticParameterCurve.ControlPoint to define how the values change over time. Note that I used .init as a shorthand because the real type name is a bit long to type. If you run this pattern on a device, you should notice that the animations are much smoother.

Before we take a look at using AHAP files for haptic, let's also quickly define a custom audio haptic:

let events = [
CHHapticEvent(eventType: .audioContinuous, parameters: [
  CHHapticEventParameter(parameterID: .audioPitch, value: 0.3),
  CHHapticEventParameter(parameterID: .audioVolume, value: 0.6),
  CHHapticEventParameter(parameterID: .decayTime, value: 0.1),
  CHHapticEventParameter(parameterID: .sustained, value: 0)
], relativeTime: 0)
]

let pattern = try CHHapticPattern(events: events, parameters: [])

The process of creating an audio haptic is very similar to creating a tactile haptic. Try running this on your device and you should hear a short beep-like sound. Combining this with a sharp haptic with medium intensity makes for a very interesting effect. Try playing around with combining audio and tactile haptic to create feedback that delights your users.

Using AHAP files to define your haptics

One last thing I want to cover in this already fairly long Quick Tip is using AHAP files to define your haptic in files. Let's look at a very brief example of what the contents of an AHAP file look like:

{
  "Version": 1.0,
  "Metadata": {
    "Project" : "Sample",
    "Created" : "12-11-2019",
    "Description" : "Quick haptic sample"
  },
  "Pattern": [
    { "Event":
      {
        "Time": 0.0,
        "EventType": "HapticTransient",
        "EventParameters": [
          { "ParameterID": "HapticIntensity", "ParameterValue": 1 },
          { "ParameterID": "HapticSharpness", "ParameterValue": 0.2 }]
      }
    }
  ]
}

You might notice that the pattern itself looks very similar to the patterns we defined in code. The big difference is that we now have a JSON-like file called an AHAP file that we can edit and update outside of our app. We could even fetch these AHAP files from a remote server to dynamically update the haptics in an app. Let's see how you can play an AHAP file in your app:

guard let path = Bundle.main.path(forResource: "myAhapFile", ofType: "ahap") else {
  return
}

try engine.playPattern(from: URL(fileURLWithPath: path))

All that we need to do to play an AHAP file is load it from the bundle, and pass it to the haptic engine. It will then read the file, create the player and play the pattern specified in your file.

In Summary

First, my apologies for making yet another Quick Tip that's way longer than I intended. There are just so many cool things you can do with haptics and I wanted to make sure that you have all the tools needed to get started with haptics in your own apps. If you need some more inspiration, be sure to check out this WWDC video from 2019 it has many beautiful examples of how haptics improve the user experience.

Also, make sure to check out the documentation for the CoreHaptics framework. There is a lot of good information available, and even a couple of sample apps that will help you to explore haptics and hopefully inspire you to integrate awesome haptic experiences in your app.

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

Deciding where to store data

Developers often need to store data on behalf of their users. Sometimes it’s a small amount simple data, other times it’s a lot of complex data. Maybe it’s highly sensitive data or maybe it’s less sensitive or even publicly available data. Choosing where to store this data is often not trivial, especially if you might be unaware of the options that are available to you, or the security considerations you need to keep in mind.

In this week’s blog post I will show you several storage options that are available to you as an iOS developer, and I'll explain the pros and cons of every storage method.

In this post, you will learn about the following storage mechanisms:

  • UserDefaults
  • Files on disk
  • The Keychain
  • Databases (like CoreData and SQLite)

Disclaimer:
Please note that I am not a security or encryption expert. For this reason, I will only cover every storage mechanism in its “normal”, basic usage. I won’t cover how you can best encrypt data to increase security for any of the listed storage mechanisms. In general, you should at least consider any data that you store on a device accessible by attackers that have access to a physical device.

With that disclaimer out of the way, let’s start exploring storage options on iOS, shall we?

Utilizing UserDefaults storage

Possibly the most well-known, easiest to use storage on iOS is the user defaults store. You typically use this storage for simple user preferences or to store simple values that help improve the user’s experience. In my blog post on optimizing your app’s App Store reviews, user defaults are used to track several metrics like the number of launches, when the user first launched the app and more to figure out the best moment to show the user a review prompt.

What should be stored in UserDefaults

You can store a fairly large amount of data in user defaults but it’s not encouraged to store large blobs of data, images and other large data structures in user defaults. On tvOS, the user defaults store is even limited to a maximum 1MB of data and Apple recommends not exceeding 512KB. This amount of storage is a lot of you only store simple data like booleans, numbers or strings because they're small and very well-suited for key-value storage. Anything larger will fill up your store rapidly.

UserDefaults and security

You should never store any sensitive data in user defaults. This storage is not encrypted at all so if an app other than your own obtains access to your user defaults store, your user’s data is compromised.

For example, simple preferences that can’t be used to identify your users are okay to store in user defaults. However, a user’s email address, password and other, similar data, should be stored somewhere more secure.

Using the UserDefaults store

If you wish to use the user defaults store, you do so through the UserDefaults class. You can use the default user store that is always available to your app, or you can create your own user defaults store with its own unique identifier. I always recommend the latter since it makes it somewhat easier to swap your user defaults store out for a different one and it allows you to separate your own user defaults from those that can be accessed by external dependencies that you might include in your app.

Reading from user defaults is really fast because your app's preferences are loaded into memory when your app launches. This means that you don't have to access the underlying disk storage at all to retrieve values using the UserDefaults class.

You can create and use your own user defaults store as follows:

let myDefaults = UserDefaults(suiteName: "com.donnywals.myapp")
myDefaults?.set(true, forKey: "com.donnywals.myapp.user-is-awesome")
let isUserAwesome = myDefaults?.bool(forKey: "com.donnywals.myapp.user-is-awesome")

Note that you might want to use Swift 5’s powerful property wrappers or a wrapper around UserDefaults to make accessing your user defaults a little bit cleaner since it’s easy to make mistakes while typing your user defaults keys. Especially if they’re quite long or complex like they are in the example that you just read.

Storing files on disk

Sometimes you need more than the limited storage user defaults gives you. If this is the case, you might want to look into storing files on the user’s file system. The file system typically has a large amount of storage available, but it can be relatively slow to read large amounts of data from. As with everything, this is a tradeoff and only by measuring and testing will you know whether you need something quicker than disk access, like for instance an SQLite store or in-memory storage.

What should be stored on disk

Typically, you will use disk storage for larger amounts of data. For example images, videos or large JSON files are suited to be stored on disk. These kinds of files are known as binary data and they have no properties that you might want to query in a database for example. Documents that the user creates like text files or PDFs are also typically stored on disk if possible. It's not uncommon for developers to make data structures that they want to store on disk conform to Codable so they can easily convert their objects to Data, which can then be written to a file on the file system. Using disk storage to store your Codable objects is especially nice if you want to create a cache of responses that you receive from the network, or if you just want to make sure certain objects persist across app launches.

Disk storage and security

Without any custom encryption, disk storage is very insecure. Apple encrypts devices when they are locked but as soon as the device is unlocked and your app is active, all bets are off. You should avoid storing sensitive data like usernames, passwords, and more in a file that you write to disk at all costs.

Writing files to disk

Most commonly, you will write files to the documents directory, or if the data is used as a cache and can easily be recreated, the caches directory. The following example shows how to write data to the documents directory:

do {
  let fileManager = FileManager.default
  let docs = try fileManager.url(for: .documentDirectory,
                                 in: .userDomainMask,
                                 appropriateFor: nil, create: false)
  let path = docs.appendingPathComponent("myFile.txt")
  let data = "Hello, world!".data(using: .utf8)!
  fileManager.createFile(atPath: path.absoluteString, contents: data, attributes: nil)
} catch {
  // handle error
}

The preceding code snippet creates a file called myFile.txt in the documents directory with Hello, world! as its contents. You can use any kind of Data as the value of contents. For example, you might want to encode a struct into JSON using a JSONEncoder or grab some image data instead.

Using the keychain to store data

The third storage mechanism to consider is the keychain. This is the place where iOS stores all kinds of sensitive user data like usernames, passwords, certificates and more. If you have secure data that you need to store somewhere, the keychain should be your place to go.

What should be stored in the keychain

The keychain is intended to store your user’s sensitive, secret data. I personally use the keychain to store things like OAuth tokens that authenticate the user against a web service. Apple themselves use the keychain for much more than just tokens, they even store entire security certificates in the keychain.

The keychain and security

On iOS, the keychain is probably the best-secured place for you to store data. However, like everything on a device, the keychain can be cracked and you should keep that in mind. Even though Apple is so confident in the keychain that they use it to store username and password combinations, I’m a little bit too paranoid for that. If I can avoid storing passwords, I will. I’d much rather store a token that has no special meaning to the user than storing (and potentially losing) something as sensitive as their username and password.

Reading and writing to the keychain

The Keychain’s APIs are unfortunately very non-trivial to navigate and use. The good news is that there are wrappers available on CocoaPods, Carthage and Swift Package Manager that make using the Keychain much, much easier. I personally like KeychainAccess by kishikawakatsumi a lot.

Database

The last storage option I’ll cover in this comparison is using a database. Databases are very good at efficiently storing large amounts of structured data. And they’re not just good at storing data, they’re also very good at retrieving and filtering data. So whenever you have a larger set of data that you want to filter, store or expand upon later, a database that's accessed using plain SQLite, or a database wrapper/manager like Realm or CoreData might be ideal for you. I’m not going to compare the performance and usage of every one of these options since in the end, they’re all good at the same thing that all databases are good at.

What should be stored in a database

If you have large objects of structured data like a user’s to-do list or metadata for documents the user creates in your app, data from your user’s weekly running exercises you’re probably going to need a database. If any of these objects have large binary blobs attached to them, like images, you might want to consider mixing disk storage and database storage by writing the large blob of data to disk and storing the path to your data in the database.

Databases and security

Databases on iOS are stored on the device, often in the documents directory. This means that you should consider any data that’s written to your databases compromised unless you’ve applied your own layer of encryption. As always, be very careful with what you store and avoid storing any sensitive data like usernames, passwords, and access token. The Keychain is a better fit for these kinds of data.

Reading and writing to a database

Depending on the database of your choice you will use SQL queries, predicates or something else to access and filter the data in your database. Keep in mind that databases often aren’t thread-safe so you need to be careful when reading and writing data from different threads.

In summary

In this blog post, you’ve learned a lot about different storage options on iOS. You learned that the Keychain is the most secure place to store user secrets. User defaults are great for simple key-value pairs that represent user preferences. File storage is great for storing large blobs of data and lastly, databases should be used to store structured data.

Security-wise, it’s good to reiterate that any data stored on the device should be considered compromised, it’s really just a matter of how complicated it is to extract and decrypt data. Always ask yourself whether you really need to store something that’s user-sensitive.

I hope this blog post will help you to make informed decisions about data storage. If you have questions, doubts, feedback or comments don’t hesitate to reach out to me on Twitter.

Thanks to Ahmed Khalaf for suggesting that I add notes about the max storage for user defaults and that it's loaded into memory when your app launched. And to Vadim Bulavin for pointing out that I omitted that User Defaults storage is only limited to 1MB on tvOS.

Updating your apps with silent push notifications

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

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

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

Configuring your app for silent push notifications

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

Screenshot of background modes enabled

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

UIApplication.shared.registerForRemoteNotifications()

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

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

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

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

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

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

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

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

Configuring your server for silent push notifications

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

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

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

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

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

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

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

In summary

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

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

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