What’s the difference between any and some in Swift?

Protocols are an extremely important part in the Swift language, and in recent updates we've received some new capabilities around protocol and generics that allow us to be much more intentional about how we use protocols in our code. This is done through the any and some keywords.

In this post, you will learn everything you need to know about the similarities and differences between these two keywords. We'll start with an introduction of each keyword, and then you'll learn a bit more about the problems each keyword solves, and how you can decide whether you should use some or any in your code.

The some keyword

In Swift 5.1 Apple introduced the some keyword. This keyword was key in making SwiftUI work because the View protocol defines an associated type which means that the View protocol couldn't be used as a type.

The following code shows how the View protocol is defined. As you'll notice, there's an associated type Body:

protocol View {
  associatedtype Body: View
  @ViewBuilder @MainActor var body: Self.Body { get }
}

If you’d try to write var body: View instead of var body: some View you’d see the following compiler error in Swift 5.7:

Use of protocol 'View' as a type must be written 'any View’

Or in older versions of Swift you’d see the following:

protocol can only be used as a generic constraint because it has Self or associated type requirements

The some keyword fixes this by hiding the concrete associated type from whoever interacts with the object that has some Protocol as its type. More on this later.

For a full overview of the some keyword, please refer to this post.

The any keyword

In Swift 5.6, the any keyword was added to the Swift language.

While it sounds like the any keyword acts as a type erasing helper, all it really does is inform the compiler that you opt-in to using an existential (a box type that conforms to a protocol) as your type.

Code that you would originally write as:

func getObject() -> SomeProtocol {
  /* ... */
}

Should be written as follows in Swift 5.6 and above:

func getObject() -> any SomeProtocol {
  /* ... */
}

This makes it explicit that the type you return from getObject is an existential (a box type) rather than a concrete object that was resolved at compile time. Note that using any is not mandatory yet, but you should start using it. Swift 6.0 will enforce any on existentials like the one that's used in the example you just saw.

Since both any and some are applied to protocols, I want to put them side by side in this blog post to better explain the problems they solve, and how you should decide whether you should use any, some, or something else.

For a full overview of the any keyword, please refer to this post.

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.

Understanding the problems that any and some solve

To explain the problems solved by any we should look at a somewhat unified example that will allow us to cover both keywords in a way that makes sense. Imagine the following protocol that models a Pizza:

protocol Pizza {
    var size: Int { get }
    var name: String { get }
}

It’s a simple protocol but it’s all we need. In Swift 5.6 you might have written the following function to receive a Pizza:

func receivePizza(_ pizza: Pizza) {
    print("Omnomnom, that's a nice \(pizza.name)")
}

When this function is called, the receivePizza function receives a so-called box type for Pizza. In order to access the pizza name, Swift has to open up that box, grab the concrete object that implements the Pizza protocol, and then access name. This means that there are virtually no compile time optimizations on Pizza, making the receivePizza method more expensive than we’d like.

Furthermore, the following function looks pretty much the same, right?

func receivePizza<T: Pizza>(_ pizza: T) {
    print("Omnomnom, that's a nice \(pizza.name)")
}

There’s a major difference here though. The Pizza protocol isn’t used as a type here. It’s used as a constraint for T. The compiler will be able to resolve the type of T at compile time and receivePizza will receive a concrete instance of a type rather than a box type.

Because this difference isn’t always clear, the Swift team has introduced the any keyword. This keyword does not add any new functionality. Instead, it forces us to clearly communicate “this is an existential”:

func receivePizza(_ pizza: any Pizza) {
    print("Omnomnom, that's a nice \(pizza.name)")
}

The example that uses a generic <T: Pizza> does not need the any keyword because Pizza is used as a constraint and not as an existential.

Now that we have a clearer picture regarding any, let’s take a closer look at some.

In Swift, many developers have tried to write code like this:

let someCollection: Collection

Only to be faced by a compiler error to tell them that Collection has a Self or associated type requirement. In Swift 5.1 we can write some Collection to tell the compiler that anybody that accesses someCollection should not concern themselves with the specifics of the associated type and/or the Self requirement. They should just know that this thing conforms to Collection and that’s all. There's no information about the associated type, and the information about Self is not made available.

This mechanism is essential to making SwiftUI’s View protocol work.

The downside of course is that anybody that works with a some Collection, some Publisher, or some View can’t access any of the generic specializations. That problem is solved by primary associated types which you can read more about right here.

However, not all protocols have associated type requirements. For example, our Pizza protocol does not have an associated type requirement but it can benefit from some in certain cases.

Consider this receivePizza version again:

func receivePizza<T: Pizza>(_ pizza: T) {
    print("Omnomnom, that's a nice \(pizza.name)")
}

We defined a generic T to allow the compiler to optimize for a given concrete type of Pizza. The some keyword also allows the compiler to know at compile time what the underlying type for the some object will be; it just hides this from the user of the object. This is exactly what <T: Pizza> also does. We can only access on T what is exposed by Pizza. This means that we can rewrite receivePizza<T: Pizza>(_:) as follows:

func receivePizza(_ pizza: some Pizza) {
    print("Omnomnom, that's a nice \(pizza.name)")
}

We don’t need T anywhere else, so we don’t need to “create” a type to hold our pizza. We can just say “this function takes some Pizza" instead of “this function takes some Pizza that we’ll call T". Small difference, but much easier to write. And functionally equivalent.

Choosing between any and some

Once you understand the use cases for any and some, you’ll realize that it’s not a matter of choosing one over the other. They each solve their own very similar problems and there’s always a more correct choice.

Generally speaking you should prefer using some or generics over any whenever you can. You often don’t want to use a box that conforms to a protocol; you want the object that conforms to the protocol.

Or sticking with our pizza analogy, any will hand the runtime a box that says Pizza and it will need to open the box to see which pizza is inside. With some or generics, the runtime will know exactly which pizza it just got, and it’ll know immediately what to do with it (toss if it’s Hawaii, keep if it’s pepperoni).

In lots of cases you’ll find that you actually didn’t mean to use any but can make some or a generic work, and according to the Swift team, we should always prefer not using any if we can.

Making the decision in practice

Let’s illustrate this with one more example that draws heavily from my explanation of primary associated types. You’ll want to read that first to fully understand this example:

class MusicPlayer {
    var playlist: any Collection<String> = []

    func play(_ playlist: some Collection<String>) {
        self.playlist = playlist
    }
}

In this code, I use some Collection<String> instead of writing func play<T: Collection<String>>(_ playlist: T) because the generic is only used in one place.

My var playlist is an any Collection<String> and not a some Collection<String> for two reasons:

  1. There would be no way to ensure that the concrete collection that the compiler will deduce for the play method matches the concrete collection that’s deduced for var playlist; this means they might not be the same which would be a problem.
  2. The compiler can’t deduce what var playlist: some Collection<String> in the first place (try it, you’ll get a compiler error)

We could avoid any and write the following MusicPlayer:

class MusicPlayer<T: Collection<String>> {
    var playlist: T = []

    func play(_ playlist: T) {
        self.playlist = playlist
    }
}

But this will force us to always use the same type of collection for T. We could use a Set, an Array, or another Collection but we can never assign a Set to playlist if T was inferred to be an Array. With the implementation as it was before, we can:

class MusicPlayer {
    var playlist: any Collection<String> = []

    func play(_ playlist: some Collection<String>) {
        self.playlist = playlist
    }
}

By using any Collection<String> here we can start out with an Array but pass a Set to play, it’s all good as long as the passed object is a Collection with String elements.

In Summary

While some and any sound very complex (and they honestly are), they are also very powerful and important parts of Swift 5.7. It’s worth trying to understand them both because you’ll gain a much better understanding about how Swift deals with generics and protocols. Mastering these topics will really take your coding to the next level.

For now, know that some or generics should be preferred over any if it makes sense. The any keyword should only be used when you really want to use that existential or box type where you’ll need to peek into the box at runtime to see what’s inside so you can call methods and access properties on it.

Presenting a partially visible bottom sheet in SwiftUI on iOS 16

This post is up to date for Xcode 15 and newer. It supersedes a version of this post that you can find here

On iOS 15, Apple granted developers the ability to present partially visible bottom sheets using a component called UISheetPresentationController. Originally, we had to resort to using a UIHostingController to bring this component to SwiftUI.

With iOS 16, we don't have to do this anymore. You can make use of the presentationDetents view modifier to configure your sheets to be fully visible, approximately half visible, or some custom fraction of the screen's height.

To do this, you can apply the presentationDetents modifier by applying it to your sheet's content:

struct DetentsView: View {
    @State var isShowingSheet = false

    var body: some View {
        Button("Show the sheet!") {
            isShowingSheet = true
        }
        .sheet(isPresented: $isShowingSheet) {
            ZStack {
                Color(red: 0.95, green: 0.9, blue: 1)
                Text("This is my sheet. It could be a whole view, or just a text.")
            }
            .presentationDetents([.medium, .fraction(0.7)])
        }
    }
}

Here's what the sheet looks like when it's presented on an iPhone:

An example of a bottom sheet on iPhone

In this example, my sheet will initially take up about half the screen and can be expanded to 70% of the screen height. If I want to allow the user to expand my sheet to the full height of the screen I would add the .large option to the list of presentationDetents.

By default, sheets will only support the .large detent so you don't need to use the presentationDetents view modifier when you want your sheet to only support the view's full height.

Formatting dates in Swift using Date.FormatStyle on iOS 15

Working with dates isn’t easy. And showing them to your users in the correct locale hasn’t always been easy either. With iOS 15, Apple introduced a new way to convert Date objects from and to String. This new way comes in the form of the new Formatter api that replaces DateFormatter.

As any seasoned iOS developer will tell you, DateFormatter objects are expensive to create, and therefor kind of tedious to manage correctly. With the new Formatter api, we no longer need to work with DateFormatter. Instead, we can ask a date to format itself based on our requirements in a more performant, easier to use way.

In this post I will show you how you can convert Date objects to String as well as how you can extract a Date from a String.

Converting a Date to a String

The most straightforward way to convert a Date to a String is the following:

let formatted = Date().formatted() // 5/26/2022, 7:52 PM

By default, the formatted() function uses a compact configuration for our String. The way formatted() converts our Date to String takes into account the user’s current locale. For example, if my device was set to be in Dutch, the date would be formatted as 26-5-2022 19:54 which is a more appropriate formatting for the Dutch language.

However, this might not always be what we need. For example, we might want to have our date formatted as May 26 2022, 7:52 PM. We can use the following code to do that:

let formatted = Date().formatted(
    .dateTime
        .day().month(.wide).year()
        .hour().minute()
)

Let’s break this code apart a bit. The formatted function takes an object that conforms to the FormatStyle protocol as its argument. There are various ways for us to create such an object. The FormatStyle protocol has several convenient extensions that can provide us with several different formatters.

For example, when sending a Date to a server, we’ll often need to send our dates as ISO8601 compliant strings. Before I explain the code you just saw, I want to show you how to grab an ISO8601 compliant string from the current Date.

let formatted = Date().formatted(.iso8601) // 2022-05-26T18:06:55Z

Neat, huh?

Okay, back to the example from before. The .datetime formatter is used as a basis for our custom formatting. We can call various functions on the object that’s returned by the .datetime static property to select the information that we want to show.

Some of these properties, like the month, can be configured to specify how they should be formatted. In the case of .month, we can choose the .wide formatting to spell out the full month name. We could use .narrow to abbreviate the month down to a single letter, or we could use one of the other options to represent the month in different ways.

If you omit a property, like for example .year(), our formatted date will omit the year that’s embedded in the Date. And again, the underlying formatter will always automatically respect your user’s locale which is really convenient.

Another way to format the date is to by specifying how you want the date and time to be formatted respectively:

let formatted = Date().formatted(date: .complete, time: .standard) // Thursday, May 26, 2022, 8:15:28 PM

The above provides a very verbose formatted string. We can make a more compact one using the following settings:

let formatted = Date().formatted(date: .abbreviated, time: .shortened) // May 26, 2022, 8:16 PM

It’s even possible to omit the date or time entirely by using the .omitted option:

let formatted = Date().formatted(date: .abbreviated, time: .omitted) // May 26, 2022

There are tons of different combinations you could come up with so I highly recommend you explore this api some more to get a sense of how flexible it really is.

Creating a Date from a String

Converting String to Date is slightly less convenient than going from a Date to a String but it’s still not too bad. Here’s how you could cover the common case of converting an ISO8601 compliant string to a Date:

let string = "2022-05-26T18:06:55Z"
let expectedFormat = Date.ISO8601FormatStyle()
let date = try! Date(string, strategy: expectedFormat)

We make use of the Date initializer that takes a string and a formatting strategy that’s used to parse the string.

We can also use and configure an instance of FormatStyle to specify the components that we expect to be present in our date string and let the system parse it using the user’s locale:

let string = "May 26, 2022, 8:30 PM"
let expectedFormat = Date.FormatStyle()
    .month().year().day()
    .hour().minute()
let date = try! Date(string, strategy: expectedFormat)

The order of our date components doesn’t matter; they will automatically be rearranged to match the user’s locale. This is super powerful, but it does mean that we can’t use this to parse dates on devices that use a different locale than the one that matches the string’s locale. The best locale agnostic date string is ISO8601 so if you have control over the date strings that you’ll parse, make sure you use ISO8601 when possible.

Summary

In this short article, you learned how you can use iOS 15’s FormatStyle to work format Date objects. You saw how to go from Date to String, and the other way around. While FormatStyle is more convenient than DateFormatter, it’s iOS 15 only. So if you’re still supporting iOS 14 you’ll want to make sure to check out DateFormatter too.

Closures in Swift explained

Closures are a powerful programming concept that enable many different programming patterns. However, for lots of beginning programmers, closures can be tricky to use and understand. This is especially true when closures are used in an asynchronous context. For example, when they’re used as completion handlers or if they’re passed around in an app so they can be called later.

In this post, I will explain what closures are in Swift, how they work, and most importantly I will show you various examples of closures with increasing complexity. By the end of this post you will understand everything you need to know to make effective use of closures in your app.

If by the end of this post the concept of closures is still a little foreign, that’s okay. In that case, I would recommend you take a day or two to process what you’ve read and come back to this post later; closures are by no means a simple topic and it’s okay if you need to read this post more than once to fully grasp the concept.

Understanding what closures are in programming

Closures are by no means a unique concept to Swift. For example, languages like JavaScript and Python both have support for closures. A closure in programming is defined as an executable body of code that captures (or closes over) values from its environment. In some ways, you can think of a closure as an instance of a function that has access to a specific context and/or captures specific values and can be called later.

Let’s look at a code example to see what I mean by that:

var counter = 1

let myClosure = {
    print(counter)
}

myClosure() // prints 1
counter += 1
myClosure() // prints 2

In the above example, I’ve created a simple closure called myClosure that prints the current value of my counter property. Because counter and the closure exist in the same scope, my closure can read the current value of counter. If I want to run my closure, I call it like a function myClosure(). This will cause the code to print the current value of counter.

We can also capture the value of counter at the time the closure is created as follows:

var counter = 1

let myClosure = { [counter] in
    print(counter)
}

myClosure() // prints 1
counter += 1
myClosure() // prints 1

By writing [counter] in we create a capture list that takes a snapshot of the current value of counter which will cause us to ignore any changes that are made to counter. We’ll take a closer look at capture lists in a bit; for now, this is all you need to know about them.

The nice thing about a closure is that you can do all kinds of stuff with it. For example, you can pass a closure to a function:

var counter = 1

let myClosure = {
    print(counter)
}

func performClosure(_ closure: () -> Void) {
    closure()
}

performClosure(myClosure)

This example is a little silly, but it shows how closures are “portable”. In other words, they can be passed around and called whenever needed.

In Swift, a closure that’s passed to a function can be created inline:

performClosure({
    print(counter)
})

Or, when using Swift’s trailing closure syntax:

performClosure {
    print(counter)
}

Both of these examples produce the exact same output as when we passed myClosure to performClosure.

Another common use for closures comes from functional programming. In functional programming functionality is modeled using functions rather than types. This means that creating an object that will add some number to an input isn’t done by creating a struct like this:

struct AddingObject {
    let amountToAdd: Int

    func addTo(_ input: Int) -> Int {
        return input + amountToAdd
    }
}

Instead, the same functionality would be achieved through a function that returns a closure:

func addingFunction(amountToAdd: Int) -> (Int) -> Int {
    let closure = { input in 
        return amountToAdd + input 
    }

    return closure
}

The above function is just a plain function that returns an object of type (Int) -> Int. In other words, it returns a closure that takes one Int as an argument, and returns another Int. Inside of addingFunction(amountToAdd:), I create a closure that takes one argument called input, and this closure returns amountToAdd + input. So it captures whatever value we passed for amountToAdd, and it adds that value to input. The created closure is then returned.

This means that we can create a function that always adds 3 to its input as follows:

let addThree = addingFunction(amountToAdd: 3)
let output = addThree(5)
print(output) // prints 8

In this example we took a function that takes two values (the base 3, and the value 5) and we converted it into two separately callable functions. One that takes the base and returns a closure, and one that we call with the value. The act of doing this is called currying. I won’t go into currying more for now, but if you’re interested in learning more, you know what to Google for.

The nice thing in this example is that the closure that’s created and returned by addingFunction can be called as often and with as many inputs as we’d like. The result will always be that the number three is added to our input.

While not all syntax might be obvious just yet, the principle of closures should slowly start to make sense by now. A closure is nothing more than a piece of code that captures values from its scope, and can be called at a later time. Throughout this post I’ll show you more examples of closures in Swift so don’t worry if this description still is a little abstract.

Before we get to the examples, let’s take a closer look at closure syntax in Swift.

Understanding closure syntax in Swift

While closures aren’t unique to Swift, I figured it’s best to talk about syntax in a separate section. You already saw that the type of a closure in Swift uses the following shape:

() -> Void

This looks very similar to a function:

func myFunction() -> Void

Except in Swift, we don’t write -> Void after every function because every function that doesn’t return anything implicitly returns Void. For closures, we must always write down the return type even when the closure doesn’t return anything.

Another way that some folks like to write closures that return nothing is as follows:

() -> ()

Instead of -> Void or "returns Void", this type specifies -> () or "returns empty tuple". In Swift, Void is a type alias for an empty tuple. I personally prefer to write -> Void at all times because it communicates my intent much clearer, and it's generally less confusing to see () -> Void rather than () -> (). Throughout this post you won't see -> () again, but I did want to mention it since a friend pointed out that it would be useful.

A closure that takes arguments is defined as follows:

let myClosure: (Int, Int) -> Void

This code defines a closure that takes two Int arguments and returns Void. If we were to write this closure, it would look as follows:

let myClosure: (Int, Int) -> Void = { int1, int2 in 
  print(int1, int2)
}

In closures, we always write the argument names followed by in to signal the start of your closure body. The example above is actually a shorthand syntax for the following:

let myClosure: (Int, Int) -> Void = { (int1: Int, int2: Int) in 
  print(int1, int2)
}

Or if we want to be even more verbose:

let myClosure: (Int, Int) -> Void = { (int1: Int, int2: Int) -> Void in 
  print(int1, int2)
}

Luckily, Swift is smart enough to understand the types of our arguments and it’s smart enough to infer the return type of our closure from the closure body so we don’t need to specify all that. However, sometimes the compiler gets confused and you’ll find that adding types to your code can help.

With this in mind, the code from earlier should now make more sense:

func addingFunction(amountToAdd: Int) -> (Int) -> Int {
    let closure = { input in 
        return amountToAdd + input 
    }

    return closure
}

While func addingFunction(amountToAdd: Int) -> (Int) -> Int might look a little weird you now know that addingFunction returns (Int) -> Int. In other words a closure that takes an Int as its argument, and returns another Int.

Earlier, I mentioned that Swift has capture lists. Let’s take a look at those next.

Understanding capture lists in closures

A capture list in Swift specifies values to capture from its environment. Whenever you want to use a value that is not defined in the same scope as the scope that your closure is created in, or if you want to use a value that is owned by a class, you need to be explicit about it by writing a capture list.

Let’s go back to a slightly different version of our first example:

class ExampleClass {
    var counter = 1

    lazy var closure: () -> Void = {
        print(counter)
    } 
}

This code will not compile due to the following error:

Reference to property `counter` requires explicit use of `self` to make capture semantics explicit.

In other words, we’re trying to capture a property that belongs to a class and we need to be explicit in how we capture this property.

One way is to follow the example and capture self:

class ExampleClass {
    var counter = 1

    lazy var closure: () -> Void = { [self] in
        print(counter)
    } 
}

A capture list is written using brackets and contains all the values that you want to capture. Capture lists are written before argument lists.

This example has an issue because it strongly captures self. This means that self has a reference to the closure, and the closure has a strong reference to self. We can fix this in two ways:

  1. We capture self weakly
  2. We capture counter directly

In this case, the first approach is probably what we want:

class ExampleClass {
    var counter = 1

    lazy var closure: () -> Void = { [weak self] in
        guard let self = self else {
            return
        }
        print(self.counter)
    } 
}

let instance = ExampleClass()
instance.closure() // prints 1
instance.counter += 1
instance.closure() // prints 2

Note that inside of the closure I use Swift’s regular guard let syntax to unwrap self.

If I go for the second approach and capture counter, the code would look as follows:

class ExampleClass {
    var counter = 1

    lazy var closure: () -> Void = { [counter] in
        print(counter)
    } 
}

let instance = ExampleClass()
instance.closure() // prints 1
instance.counter += 1
instance.closure() // prints 1

The closure itself looks a little cleaner now, but the value of counter is captured when the lazy var closure is accessed for the first time. This means that the closure will capture whatever the value of counter is at that time. If we increment the counter before accessing the closure, the printed value will be the incremented value:

let instance = ExampleClass()
instance.counter += 1
instance.closure() // prints 2
instance.closure() // prints 2

It’s not very common to actually want to capture a value rather than self in a closure but it’s possible. The caveat to keep in mind is that a capture list will capture the current value of the captured value. In the case of self this means capturing a pointer to the instance of the class you’re working with rather than the values in the class itself.

For that reason, the example that used weak self to avoid a retain cycle did read the latest value of counter.

If you want to learn more about weak self, take a look at this post that I wrote earlier.

Next up, some real-world examples of closures in Swift that you may have seen at some point.

Higher order functions and closures

While this section title sounds really fancy, a higher order function is basically just a function that takes another function. Or in other words, a function that takes a closure as one of its arguments.

If you think this is probably an uncommon pattern in Swift, how does this look?

let strings = [1, 2, 3].map { int in 
    return "Value \(int)"
}

There’s a very good chance that you’ve written something similar before without knowing that map is a higher order function, and that you were passing it a closure. The closure that you pass to map takes a value from your array, and it returns a new value. The map function’s signature looks as follows:

func map<T>(_ transform: (Self.Element) throws -> T) rethrows -> [T]

Ignoring the generics, you can see that map takes the following closure: (Self.Element) throws -> T this should look familiar. Note that closures can throw just like functions can. And the way a closure is marked as throwing is exactly the same as it is for functions.

The map function immediately executes the closure it receives. Another example of such a function is DispatchQueue.async:

DispatchQueue.main.async {
    print("do something")
}

One of the available async function overloads on DispatchQueue is defined as follows:

func async(execute: () -> Void)

As you can see, it’s “just” a function that takes a closure; nothing special.

Defining your own function that takes a closure is fairly straightforward as you’ve seen earlier:

func performClosure(_ closure: () -> Void) {
    closure()
}

Sometimes, a function that takes a closure will store this closure or pass it elsewhere. These closures are marked with @escaping because they escape the scope that they were initially passed to. To learn more about @escaping closures, take a look at this post.

In short, whenever you want to pass a closure that you received to another function, or if you want to store your closure so it can be called later (for example, as a completion handler) you need to mark it as @escaping.

With that said, let’s see how we can use closures to inject functionality into an object.

Storing closures so they can be used later

Often when we’re writing code, we want to be able to inject some kind of abstraction or object that allows us to decouple certain aspects of our code. For example, a networking object might be able to construct URLRequests, but you might have another object that handles authentication tokens and setting the relevant authorization headers on a URLRequest.

You could inject an entire object into your Networking object, but you could also inject a closure that authenticates a URLRequest:

struct Networking {
    let authenticateRequest: (URLRequest) -> URLRequest

    func buildFeedRequest() -> URLRequest {
        let url = URL(string: "https://donnywals.com/feed")!
        let request = URLRequest(url: url)
        let authenticatedRequest = authenticateRequest(request)

        return authenticatedRequest
    }
}

The nice thing about is that you can swap out, or mock, your authentication logic without needing to mock an entire object (nor do you need a protocol with this approach).

The generated initializer for Networking looks as follows:

init(authenticateRequest: @escaping (URLRequest) -> URLRequest) {
    self.authenticateRequest = authenticateRequest
}

Notice how authenticateRequest is an @escaping closure because we store it in our struct which means that the closure outlives the scope of the initializer it’s passed to.

In your app code, you could have a TokenManager object that retrieves a token, and you can then use that token to set the authorization header on your request:

let tokenManager = TokenManager()
let networking = Networking(authenticateRequest: { urlRequest in 
    let token = tokenManager.fetchToken()
    var request = urlRequest
    request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
    return request
})

let feedRequest = networking.buildFeedRequest()
print(feedRequest.value(forHTTPHeaderField: "Authorization")) // a token

What’s cool about this code is that the closure that we pass to Networking captures the tokenManager instance so we can use it inside of the closure body. We can ask the token manager for its current token, and we can return a fully configured request from our closure.

In this example, the closure is injected as a function that can be called whenever we need to authenticate a request. The closure can be called as often as needed, and its body will be run every time we do. Just like a function is run every time you call it.

As you can see in the example, the authenticateRequest is called from within buildFeedRequest to create an authenticated URLRequest.

Storing closures and calling them later is a very powerful pattern but beware of retain cycles. Whenever an @escaping closure captures its owner strongly, you’re almost always creating a retain cycle that should be solved by weakly capturing self (since in most cases self is the owner of the closure).

When you combine what you’ve already learned, you can start reasoning about closures that are called asynchronously, for example as completion handlers.

Closures and asynchronous tasks

Before Swift had async/await, a lot of asynchronous APIs would communicate their results back in the form of completion handlers. A completion handler is nothing more than a regular closure that’s called to indicate that some piece of work has completed or produced a result.

This pattern is important because in a codebase without async/await, an asynchronous function returns before it produces a result. A common example of this is using URLSession to fetch data:

URLSession.shared.dataTask(with: feedRequest) { data, response, error in 
    // this closure is called when the data task completes
}.resume()

The completion handler that you pass to the dataTask function (in this case via trailing closure syntax) is called once the data task completes. This could take a few milliseconds, but it could also take much longer.

Because our closure is called at a later time, a completion handler like this one is always defined as @escapingbecause it escapes the scope that it was passed to.

What’s interesting is that asynchronous code is inherently complex to reason about. This is especially true when this asynchronous code uses completion handlers. However, knowing that completion handlers are just regular closures that are called once the work is done can really simplify your mental model of them.

So what does defining your own function that takes a completion handler look like then? Let’s look at a simple example:

func doSomethingSlow(_ completion: @escaping (Int) -> Void) {
    DispatchQueue.global().async {
        completion(42)
    }
}

Notice how in the above example we don’t actually store the completion closure. However, it is marked as @escaping. The reason for this is that we call the closure from another closure. This other closure is a new scope which means that it escapes the scope of our doSomethingSlow function.

If you’re not sure whether your closure should be escaping or not, just try and compile your code. The compiler will automatically detect when your non-escaping closure is, in fact, escaping and should be marked as such.

Summary

Wow! You’ve learned a lot in this post. Even though closures are a complex topic, I hope that this post has helped you understand them that much better. The more you use closures, and the more you expose yourself to them, the more confident you will feel about them. In fact, I’m sure that you’re already getting lots of exposure to closures but you just might not be consciously aware of it. For example, if you’re writing SwiftUI you’re using closures to specify the contents of your VStacks, HStacks, your Button actions, and more.

If you feel like closures didn’t quite click for you just yet, I recommend that you come back to this post in a few days. This isn’t an easy topic, and it might take a little while for it to sink in. Once the concept clicks, you’ll find yourself writing closures that take other closures while returning more closures in no time. After all, closures can be passed around, held onto, and executed whenever you feel like it.

Feel free to reach out to me on Twitter if you have any questions about this post. I’d love to find out what I could improve to make this the best guide to closures in Swift.

Debugging Network Traffic With Proxyman

Disclaimer: This post is not sponsored by Proxyman, nor am I affiliated with Proxyman in any way. I pay for my license myself, and this post is simply written as a guide to learning more about a tool that I find very important in the iOS Developer toolbox.

Networking is an essential part of modern iOS applications. Most apps I’ve worked have some kind of networking component. Sometimes the networking layer involves user authentication, token refresh flows, and more. Other times, I’ll simply need to hit one or two endpoints to fetch new data or configuration files for my app.

When this all works well, everything is great. But when I notice that things don’t work quite as expected, it’s time to start digging in and debug my network calls. The most simple method would be to convert the data that I’ve fetched from the server to a String and printing it. However, a large response will be hard to read in Xcode’s console so you might have to paste it elsewhere, like in a JSON validator.

When the response you receive is wrong, or not quite what you expected, you’ll want to take a look at the data you’ve sent to the server to make sure you’re not sending it bad data. Doing this isn’t always trivial. Especially when you’re dealing with setting HTTP headers and/or multipart form requests.

In this post, I will show you how you can gain insight into the requests that your app sends to the server as well as see the responses that the server sends to your app. To do this, we’ll use he app Proxyman.

💡 Tip: I’ve written a similar post to this one about Charles Proxy that you can read here. Over time I’ve come to prefer Proxyman for debugging because it’s a nicer app and it’s just a bit easier to set up.

Installing and configuring Proxyman

To use Proxyman, you must first download and install it. You can do this right here on their website. The download for Proxyman is free, and the free version of the app is perfectly usable to explore and learn about debugging your app through a proxy. However, Proxyman isn’t a free app and if you want to use all its features you’ll need to purchase a license. Luckily, a single license is valid forever and entitles you to a year of free updates. After that you can continue the last version that became available during the year, or buy a new license for another year of updates.

Once you’ve downloaded Proxyman, install it by opening the dmg and dragging the app to your Applications folder.

When you first launch Proxyman, it will prompt you for some setup steps and eventually Proxyman will want to install a helper tool. You can safely accept all of Proxyman’s defaults and install the helper tool.

Proxyman helper tool prompt

After setting everything up, you should immediately see network traffic appear in Proxyman’s main window. This is all of the traffic that your Mac is sending and receiving in real-time. This means that you can see any app or website’s network traffic in Proxyman.

When you click on one of the requests, you can see some details about the request. Like for example the request headers, body, and more. You can also see the response for a given request.

There is a caveat though; if the request is performed over HTTPS you won’t see anything just yet.

In the screenshot below, I’ve selected all traffic made by the Proxyman app in the left hand sidebar, and I’ve selected one of the requests in the main section to see more details. Because the request is made using HTTPS, I need to explicitly enable SSL proxying for either a specific host, or all requests made by, in this case, the Proxyman app. Once enabled, re-executing these requests would allow me to inspect their contents.

Proxyman main window

When you first allow SSL proxying for either a domain or an app, you will be prompted to install the Proxyman root certificate. Proxyman can do this automatically for you which is quite convenient. You will be prompted for your admin password so that Proxyman can update your settings and allow SSL proxying.

After enabling SSL proxying, you can select any request and repeat it to make sure that you can now inspect your request and the server’s response. Note that repeating a request isn’t always a valid thing to do so the server you’re hitting might give you an error response; this isn’t something you did wrong with your setup, if you can see the error code everything is working as intended.

Repeat Request in Proxyman

With Proxyman set up, let’s see how we can use it for our apps.

Using Proxyman with the simulator

In order to see the iOS simulator’s traffic appear in Proxyman, you’ll need to install the Proxyman certificates in the simulator. You can do this by selecting Certificates -> Install Certificate on iOS -> Simulators.... This will automatically add the needed certificates to your active iOS simulators so make sure the simulator you want to debug with is active.

Using Proxyman with the simulator

After installing the certificate, you need to reboot your simulator. Once you’ve done this, you can re-run your app and you should start seeing your simulator’s network traffic appear. If you haven’t enabled SSL proxying for the hosts you’re hitting yet, you might have to do this first as you’ve seen earlier.

Inspecting your networking traffic is done in the bottom section of the Proxyman window. On the left side you can see the request you’re sending to the server. You can inspect headers, the request body, query parameters, and anything else you might need.

On the right side of the window you can see the server’s response body and headers.

Proxyman request and response

Being able to have real-time detailed insights into the exact request and responses you’re dealing with is extremely convenient. What’s even more convenient is that you can use Proxyman on a physical device too, allowing you to connect your device to Proxyman on your mac whenever you need to quickly check what your app is doing; even for production builds!

Using Proxyman with a real device

To run Proxyman on a real device, there are a couple of things you’ll need to do. Luckily, Proxyman provides really good instructions in the Mac app.

Start by selecting Certificate -> Install Certificate on iOS -> Physical Devices....

Menu option for proxyman on real device

This will open a window with instructions that you should follow to set up Proxyman for your device.

Proxyman on device instructions

You only need to go through all of these steps once. When you’ve set up your device, all you need to do to start a debugging session is to launch Proxyman on your Mac, connect your iOS device to the network that your Mac is on, and set up the proxy settings for your device in your WiFi settings.

Sharing Proxyman logs with others

One of the main selling points of using a proxy to debug networking isn’t that it’s convenient for developers to validate the request and responses they’re creating and receiving in their apps. In my opinion one of the key selling points of Proxyman is the fact that anybody can install it to capture network traffic.

This means that your QA team can run their device through Proxyman to capture an app’s networking traffic, and include a log of network calls made by the app along with a bug report. This is extremely valuable because it makes troubleshooting network related issues infinitely easier. Having a detailed overview of network calls is so much better than “I just keep getting an unknown error”.

Furthermore, when you’re sure that you’re sending your backend the right thing but the response you’re getting is malformed, or not what you expected, you can easily get Proxyman to export a cURL command that you can send to your backend engineers to help them figure out what’s wrong.

To export a collection of requests you can select them in the main window and right click. Then select Export to see the various export options that are available. You’ll typically want to export as Proxyman Log but if the receiver of your log doesn’t have Proxyman, Charles, Postman, or another format might be more useful.

Export batch of requests

If you just want to export a cURL for a single request you can select the request you want a cURL for, right click, and select Copy cURL. This will get you a cURL request that anybody can run to repeat the request that you’re debugging.

Copy curl menu option

Sending a log or a cURL along with any bug report you have is a really good practice in my opinion. Not only does it improve the quality of your reports, it also makes the lives of your fellow engineers, and backend engineers a lot easier because they’ll have much better insights into what the app is doing with regards to networking.

In addition to capturing iOS device traffic through the Mac app, you can also capture traffic directly on your iOS device with the Proxyman for iOS app. The app can be downloaded through the App Store, and it allows you to quickly start a debugging session right on your iOS device. This can be incredibly useful when you’re not near a Mac but see some weird behavior in your app that you want to explore later. I don’t use the iOS app a lot, but I wanted to mention it anyway.

In summary

In today's tip, you learned how you can use Proxyman to inspect and debug your app’s networking traffic. Being able to debug network calls is an essential skill in the toolbox of any iOS developer in my opinion. Or more broadly speaking, anybody that’s involved in the process of building and testing apps should know how to run an app through a proxy like Proxyman to gain much better insights into an app’s networking traffic, and to build much better bug reports.

This post is merely an introduction to Proxyman, it can do a lot more than just show you request / response pairs. For example, it also allows you to throttle network speeds for specific hosts, and it even allows you to replace requests and responses with local files so you can override what your app sends and/or receives when needed for debugging.

For now, I wish you happy debugging sessions and don't hesitate to shoot me a message on Twitter if you have any questions about debugging your network calls.

The difference between checked and unsafe continuations in Swift

When you’re writing a conversion layer to transform your callback based code into code that supports async/await in Swift, you’ll typically find yourself using continuations. A continuation is a closure that you can call with the result of your asynchronous work. You have the option to pass it the output of your work, an object that conforms to Error, or you can pass it a Result.

In this post, I won’t go in-depth on showing you how to convert your callback based code to async/await (you can refer to this post if you’re interested in learning more). Instead, I’d like to explain the difference between a checked and unsafe continuation in this short post.

If you’ve worked with continuations before, you may have noticed that there are four methods that you can use to create a continuation:

  • withCheckedThrowingContinuation
  • withCheckedContinuation
  • withUnsafeThrowingContinuation
  • withUnsafeContinuation

The main thing that should stand out here is that you have the option of creating a “checked” continuation or an “unsafe” continuation. Your gut might tell you to always use the checked version because the unsafe one sounds... well... unsafe.

To figure out whether this is correct, let’s take a look at what we get with a checked continuation first.

Understanding what a checked continuation does

A checked continuation in Swift is a continuation closure that you can call with the outcome of a traditional asynchronous function that doesn’t yet use async/await, typically one with a callback closure.

This might look a bit as follows:

func validToken(_ completion: @escaping (Result<Token, Error>) -> Void) {
  // eventually calls the completion closure
}

func validTokenFromCompletion() async throws -> Token {
    return try await withCheckedThrowingContinuation { continuation in
        validToken { result in
            continuation.resume(with: result)
        }
    }
}

The code above is a very simple example of bridging the traditional validToken method into the async/await world with a continuation.

There are a couple of rules for using a continuation that you need to keep in mind:

  • You should only call the continuation’s resume once. No more, no less. Calling the resume function twice is a developer error and can lead to undefined behavior.
  • You’re responsible for retaining the continuation and calling resume on it to continue your code. Not resuming your continuation means that withCheckedThrowingContinuation will never throw an error or return a value. In other words, your code will be await-ing forever.

If you fail to do either of the two points above, that’s a developer mistake and you should fix that. Luckily, a checked continuation performs some checks to ensure that:

  • You only call resume once
  • The continuation passed to you is retained in your closure

If either of these checks fail, your app will crash with a descriptive error message to tell you what’s wrong.

Of course, there’s some overhead in performing these checks (although this overhead isn’t enormous). To get rid of this overhead, we can make use of an unsafe continuation.

Is it important that you get rid of this overhead? No, in by far the most situations I highly doubt that the overhead of checked continuations is noticeable in your apps. That said, if you do find a reason to get rid of your checked continuation in favor of an unsafe one, it’s important that you understand what an unsafe continuation does exactly.

Understanding what an unsafe continuation does

In short, an unsafe continuation works in the exact same way as a checked one, with the same rules, except it doesn’t check that you adhere to the rules. This means that mistakes will not be caught early, and you won’t get a clear description of what’s wrong in your crash log.

Instead, an unsafe closure just runs and it might crash or perform other undefined behavior when you break the rules.

That’s really all there is to it for an unsafe continuation, it doesn’t add any functionality, it simply removes all correctness checks that a checked continuation does.

Choosing between a checked and an unsafe continuation

The Swift team originally suggested that we always make use of checked continuations during development, at least until we’ve verified the correctness of our implementation. Once we know our code is correct and our checked continuation doesn’t throw up any warnings at runtime, it’s safe (enough) to switch to an unsafe continuation if you’d like.

Personally, I prefer to use checked continuations even when I know my implementation is correct. This allows me to continue working on my code, and to make changes, without having to remember to switch back and forth between checked and unsafe continuations. Members of the Swift team have, eventually, come forward stating that they thing using unsafe continuation is a mistake for most developers. We should be using checked continuations unless we're absolutely certain that switching to unsafe makes sense. For example, when checked continuations are actually giving you performance problems (they probably won't in your app).

Of course, there is some overhead involved with checked continuations and if you feel like you might benefit from using an unsafe continuation, you should always profile this first and make sure that this switch is actually providing you with a performance benefit. Making your code less safe based on assumptions is never a great idea. Personally, I have yet to find a reason to favor an unsafe continuation over a checked one.

Migrating callback based code to Swift Concurrency with continuations

Swift's async/await feature significantly enhances the readability of asynchronous code for iOS 13 and later versions. For new projects, it enables us to craft more expressive and easily understandable asynchronous code, which closely resembles synchronous code. However, adopting async/await may require substantial modifications in existing codebases, especially if their asynchronous API relies heavily on completion handler functions.

Fortunately, Swift offers built-in mechanisms that allow us to create a lightweight wrapper around traditional asynchronous code, facilitating its transition into the async/await paradigm. In this post, I'll demonstrate how to convert callback-based asynchronous code into functions compatible with async/await, using Swift's async keyword.

Converting a Callback-Based Function to Async/Await

Callback-based functions vary in structure, but typically resemble the following example:

func validToken(_ completion: @escaping (Result<Token, Error>) -> Void) {
    // Function body...
}

The validToken(_:) function above is a simplified example, taking a completion closure and using it at various points to return the outcome of fetching a valid token.

Tip: To understand more about @escaping closures, check out this post on @escaping in Swift.

To adapt our validToken function for async/await, we create an async throws version returning a Token. The method signature becomes cleaner:

func validToken() async throws -> Token {
    // ...
}

The challenge lies in integrating the existing callback-based validToken with our new async version. We achieve this through a feature known as continuations. Swift provides several types of continuations:

  • withCheckedThrowingContinuation
  • withCheckedContinuation
  • withUnsafeThrowingContinuation
  • withUnsafeContinuation

These continuations exist in checked and unsafe variants, and in throwing and non-throwing forms. For an in-depth comparison, refer to my post that compares checked and unsafe in great detail.

Here’s the revised validToken function using a checked continuation:

func validToken() async throws -> Token {
    return try await withCheckedThrowingContinuation { continuation in
        // Implementation...
    }
}

This function uses withCheckedThrowingContinuation to bridge our callback-based validToken with the async version. The continuation object, created within the function, must be used to resume execution, or the method will remain indefinitely suspended.

The callback-based validToken is invoked immediately, and once resume is called, the async validToken resumes execution. Since the Result type can contain an Error, we handle both success and failure cases accordingly.

The Swift team has simplified this pattern by introducing a version of resume that accepts a Result object:

func validToken() async throws -> Token {
    return try await withCheckedThrowingContinuation { continuation in
        validToken { result in
            continuation.resume(with: result)
        }
    }
}

This approach is more streamlined and elegant.

Remember two crucial points when working with continuations:

  1. A continuation can only be resumed once.
  2. It's your responsibility to call resume within the continuation closure; failure to do so will leave the function awaiting indefinitely.

Despite minor differences (like error handling), all four with*Continuation functions follow these same fundamental rules.

Summary

This post illustrated how to transform a callback-based function into an async function using continuations. You learned about the different types of continuations and their applications.

Continuations are an excellent tool for gradually integrating async/await into your existing codebase without a complete overhaul. I've personally used them to transition large code segments into async/await gradually, allowing for intermediate layers that support async/await in, say, view models and networking, without needing a full rewrite upfront.

In conclusion, continuations offer a straightforward and elegant solution for converting existing callback-based functions into async/await compatible ones.

Comparing lifecycle management for async sequences and publishers

In my previous post you learned about some different use cases where you might have to choose between an async sequence and Combine while also clearly seeing that async sequence are almost always better looking in the examples I’ve used, it’s time to take a more realistic look at how you might be using each mechanism in your apps.

The details on how the lifecycle of a Combine subscription or async for-loop should be handled will vary based on how you’re using them so I’ll be providing examples for two situations:

  • Managing your lifecycles in SwiftUI
  • Managing your lifecycles virtually anywhere else

We’ll start with SwiftUI since it’s by far the easiest situation to reason about.

Managing your lifecycles in SwiftUI

Apple has added a bunch of very convenient modifiers to SwiftUI that allow us to subscribe to publishers or launch an async task without worrying about the lifecycle of each too much. For the sake of having an example, let’s assume that we have an object that exists in our view that looks a bit like this:

class ExampleViewModel {
    func notificationCenterPublisher() -> AnyPublisher<UIDeviceOrientation, Never> {
        NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)
            .map { _ in UIDevice.current.orientation }
            .eraseToAnyPublisher()
    }

    func notificationCenterSequence() async -> AsyncMapSequence<NotificationCenter.Notifications, UIDeviceOrientation> {
        await NotificationCenter.default.notifications(named: UIDevice.orientationDidChangeNotification)
            .map { _ in await UIDevice.current.orientation }
    }
} 

In the SwiftUI view we’ll call each of these two functions to subscribe to the publisher as well as iterate over the async sequence. Here’s what our SwiftUI view looks like:

struct ExampleView: View {
    @State var isPortraitFromPublisher = false
    @State var isPortraitFromSequence = false

    let viewModel = ExampleViewModel()

    var body: some View {
        VStack {
            Text("Portrait from publisher: \(isPortraitFromPublisher ? "yes" : "no")")
            Text("Portrait from sequence: \(isPortraitFromSequence ? "yes" : "no")")
        }
        .task {
            let sequence = await viewModel.notificationCenterSequence()
            for await orientation in sequence {
                isPortraitFromSequence = orientation == .portrait
            }
        }
        .onReceive(viewModel.notificationCenterPublisher()) { orientation in
            isPortraitFromPublisher = orientation == .portrait
        }
    }
}

In this example I’d argue that the publisher approach is easier to understand and use than the async sequence one. Building the publisher is virtually the same as it is for the async sequence with the major difference being the return type of our publisher vs. our sequence: AnyPublisher<UIDeviceOrientation, Never> vs. AsyncMapSequence<NotificationCenter.Notifications, UIDeviceOrientation>. The async sequence actually leaks its implementation details because we have to return an AsyncMapSequence instead of something like an AnyAsyncSequence<UIDeviceOrientation> which would allow us to hide the internal details of our async sequence.

At this time it doesn’t seem like the Swift team sees any benefit in adding something like eraseToAnyAsyncSequence() to the language so we’re expected to provide fully qualified return types in situations like ours.

Using the sequence is also a little bit harder in SwiftUI than it is to use the publisher. SwiftUI’s onReceive will handle subscribing to our publisher and it will provide the publisher’s output to our onReceive closure. For the async sequence we can use task to create a new async context, obtain the sequence, and iterate over it. Not a big deal but definitely a little more complex.

When this view goes out of scope, both the Task created by task as well as the subscription created by onReceive will be cancelled. This means that we don’t need to worry about the lifecycle of our for-loop and subscription.

If you want to iterate over multiple sequences, you might be tempted to write the following:

.task {
    let sequence = await viewModel.notificationCenterSequence()
    for await orientation in sequence {
        isPortraitFromSequence = orientation == .portrait
    }

    let secondSequence = await viewModel.anotherSequence()
    for await output in secondSequence {
        // handle ouput
    }
}

Unfortunately, this setup wouldn’t have the desired outcome. The first for-loop will need to finish before the second sequence is even created. This for-loop behaves just like a regular for-loop where the loop has to finish before moving on to the next lines in your code. The fact that values are produced asynchronously does not change this. To iterate over multiple async sequences in parallel, you need multiple tasks:

.task {
    let sequence = await viewModel.notificationCenterSequence()
    for await orientation in sequence {
        isPortraitFromSequence = orientation == .portrait
    }
}
.task {
    let secondSequence = await viewModel.anotherSequence()
    for await output in secondSequence {
        // handle ouput
    }
}

In SwiftUI, Tasks relatively simple to use, and it’s relatively hard to make mistakes. But what happens if we compare publishers and async sequences lifecycles outside of SwiftUI? That’s what you’ll find out next.

Managing your lifecycles outside of SwiftUI

When you’re subscribing to publishers or iterating over async sequences outside of SwiftUI, things change a little. You suddenly need to manage the lifecycles of everything you do much more carefully, or more specifically for Combine you need to make sure you retain your cancellables to avoid having your subscriptions being torn down immediately. For async sequences you’ll want to make sure you don’t have the tasks that wrap your for-loops linger for longer than they should.

Let’s look at an example. I’m still using SwiftUI, but all the iterating and subscribing will happen in a view model instead of my view:

struct ContentView: View {
    @State var showExampleView = false

    var body: some View {
        Button("Show example") {
            showExampleView = true
        }.sheet(isPresented: $showExampleView) {
            ExampleView(viewModel: ExampleViewModel())
        }
    }
}

struct ExampleView: View {
    @ObservedObject var viewModel: ExampleViewModel
    @Environment(\.dismiss) var dismiss

    var body: some View {
        VStack(spacing: 16) {
            VStack {
                Text("Portrait from publisher: \(viewModel.isPortraitFromPublisher ? "yes" : "no")")
                Text("Portrait from sequence: \(viewModel.isPortraitFromSequence ? "yes" : "no")")
            }

            Button("Dismiss") {
                dismiss()
            }
        }.onAppear {
            viewModel.setup()
        }
    }
}

This setup allows me to present an ExampleView and then dismiss it again. When the ExampleView is presented I want to be subscribed to my notification center publisher and iterate over the notification center async sequence. However, when the view is dismissed the ExampleView and ExampleViewModel should both be deallocated and I want my subscription and the task that wraps my for-loop to be cancelled.

Here’s what my non-optimized ExampleViewModel looks like:

@MainActor
class ExampleViewModel: ObservableObject {
    @Published var isPortraitFromPublisher = false
    @Published var isPortraitFromSequence = false

    private var cancellables = Set<AnyCancellable>()

    deinit {
        print("deinit!")
    }

    func setup() {
        notificationCenterPublisher()
            .map { $0 == .portrait }
            .assign(to: &$isPortraitFromPublisher)

        Task { [weak self] in
            guard let sequence = await self?.notificationCenterSequence() else {
                return
            }
            for await orientation in sequence {
                self?.isPortraitFromSequence = orientation == .portrait
            }
        }
    }

    func notificationCenterPublisher() -> AnyPublisher<UIDeviceOrientation, Never> {
        NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)
            .map { _ in UIDevice.current.orientation }
            .eraseToAnyPublisher()
    }

    func notificationCenterSequence() -> AsyncMapSequence<NotificationCenter.Notifications, UIDeviceOrientation> {
        NotificationCenter.default.notifications(named: UIDevice.orientationDidChangeNotification)
            .map { _ in UIDevice.current.orientation }
    }
}

If you’d put the views in a project along with this view model, everything will look good on first sight. The view updates as expected and the ExampleViewModel’s deinit is called whenever we dismiss the ExampleView. Let’s make some changes to setup() to double check that both our Combine subscription and our Task are cancelled and no longer receiving values:

func setup() {
    notificationCenterPublisher()
        .map { $0 == .portrait }
        .handleEvents(receiveOutput: { _ in print("subscription received value") })
        .assign(to: &$isPortraitFromPublisher)

    Task { [weak self] in
        guard let sequence = self?.notificationCenterSequence() else {
            return
        }
        for await orientation in sequence {
            print("sequence received value")
            self?.isPortraitFromSequence = orientation == .portrait
        }
    }.store(in: &cancellables)
}

If you run the app now you’ll find that you’ll see the following output when you rotate your device or simulator after dismissing the ExampleView:

// present ExampleView and rotate
subscription received value
sequence received value
// rotate again
subscription received value
sequence received value
// dismiss
deinit!
// rotate again
sequence received value

You can see that the ExampleViewModel is deallocated and that the subscription no longer receives values after that. Unfortunately, our Task is still active and it’s still iterating over our async sequence. If you present the ExampleView again, you’ll find that you now have multiple active iterators. This is a problem because we want to cancel our Task whenever the object that contains it is deallocated, basically what Combine does with its AnyCancellable.

Luckily, we can add a simple extension on Task to piggy-back on the mechanism that makes AnyCancellable work:

extension Task {
    func store(in cancellables: inout Set<AnyCancellable>) {
        asCancellable().store(in: &cancellables)
    }

    func asCancellable() -> AnyCancellable {
        .init { self.cancel() }
    }
}

Combine’s AnyCancellable is created with a closure that’s run whenever the AnyCancellable itself will be deallocated. In this closure, the task can cancel itself which will also cancel the task that’s producing values for our for-loop. This should end the iteration as long as the task that produces values respects Swift Concurrency’s task cancellation rules.

You can now use this extension as follows:

Task { [weak self] in
    guard let sequence = self?.notificationCenterSequence() else {
        return
    }
    for await orientation in sequence {
        print("sequence received value")
        self?.isPortraitFromSequence = orientation == .portrait
    }
}.store(in: &cancellables)

If you run the app again, you’ll find that you’re no longer left with extraneous for-loops being active which is great.

Just like before, iterating over a second async sequence requires you to create a second task to hold the second iteration.

In case the task that’s producing your async values doesn’t respect task cancellation, you could update your for-loop as follows:

for await orientation in sequence {
    print("sequence received value")
    self?.isPortraitFromSequence = orientation == .portrait

    if Task.isCancelled { break }
}

This simply checks whether the task we’re currently in is cancelled, and if it is we break out of the loop. You shouldn’t need this as long as the value producing task was implemented correctly so I wouldn’t recommend adding this to every async for-loop you write.

One more option to break out of our async for loop is to check whether self still exists within each iteration and either having a return or a break in case self is no longer around:

Task { [weak self] in
    guard let sequence = self?.notificationCenterSequence() else {
        return
    }
    for await orientation in sequence {
        guard let self = self else { return }
        print("sequence received value")
        self.isPortraitFromSequence = orientation == .portrait
    }
}

The nice thing is that we don't to rely on Combine at all with this solution. The downside, however, is that we cannot have a guard let self = self as the first line in our Task because that would capture self for the duration of our Task, which means that every check for self within the for loop body results in self being around. This would be a leak again.

In the example above, we only capture the sequence before starting the loop which means that within the loop we can check for the existence of self and break out of the loop as needed.

Summary

In this post you learned a lot about how the lifecycle of a Combine subscription compares to that of a task that iterates over an async sequence. You saw that using either in a SwiftUI view modifier was pretty straightforward, and SwiftUI makes managing lifecycles easy; you don’t need to worry about it.

However, you also learned that as soon as we move our iterations and subscriptions outside of SwiftUI things get messier. You saw that Combine has good built-in mechanisms to manage lifecycles through its AnyCancellable and even its assign(to:) operator. Tasks unfortunately lack a similar mechanism which means that it’s very easy to end up with more iterators than you’re comfortable with. Luckily, we can add an extension to Task to take care of this by piggy-backing on Combine’s AnyCancellable to cancel our Task objects as soon s the object that owns the task is deallocated.

You also saw that we can leverage a guard on self within each for loop iteration to check whether self is still around, and break out of the loop if self is gone which will stop the iterations.

All in all, Combine simply provides more convenient lifecycle management out of the box when we’re using it outside of SwiftUI views. That doesn’t mean that Combine is automatically better, but it does mean that async sequences aren’t quite in a spot where they are as easy to use as Combine. With a simple extension we can improve the ergonomics of iterating over an async sequence by a lot, but I hope that the Swift team will address binding task lifecycles to the lifecycle of another object like Combine does at some point in the future.

Comparing use cases for async sequences and publishers

Swift 5.5 introduces async/await and a whole new concurrency model that includes a new protocol: AsyncSequence. This protocol allows developers to asynchronously iterate over values coming from a sequence by awaiting them. This means that the sequence can generate or obtain its values asynchronously over time, and provide these values to a for-loop as they become available.

If this sounds familiar, that’s because a Combine publisher does roughly the same thing. A publisher will obtain or generate its values (asynchronously) over time, and it will send these values to subscribers whenever they are available.

While the basis of what we can do with both AsyncSequence and Publisher sounds similar, I would like to explore some of the differences between the two mechanisms in a series of two posts. I will focus this comparison on the following topics:

  • Use cases
  • Lifecycle of a subscription / async for-loop

The post you’re reading now will focus on comparing use cases. If you want to learn more about lifecycle management, take a look at this post.

Please note that parts of this comparison will be highly opinionated or be based on my experiences. I’m trying to make sure that this comparison is fair, honest, and correct but of course my experiences and preferences will influence part of the comparison. Also note that I’m not going to speculate on the futures of either Swift Concurrency nor Combine. I’m comparing AsyncSequence to Publisher using Xcode 13.3, and with the Swift Async Algorithms package added to my project.

Let’s dive in, and take a look at some current use cases where publishers and async sequences can truly shine.

Operations that produce a single output

Our first comparison takes a closer look at operations with a single output. While this is a familiar example for most of us, it isn’t the best comparison because async sequences aren’t made for performing work that produces a single result. That’s not to say an async sequence can’t deliver only one result, it absolutely can.

However, you typically wouldn’t leverage an async sequence to make a network call; you’d await the result of a data task instead.

On the other hand, Combine doesn’t differentiate between tasks that produce a single output and tasks that produce a sequence of outputs. This means that publishers are used for operations that can emit many values as well as for values that produce a single value.

Combine’s approach to publishers can be considered a huge benefit of using them because you only have one mechanism to learn and understand; a publisher. It can also be considered a downside because you never know whether an AnyPublisher<(Data, URLResponse), Error> will emit a single value, or many values. On the other hand, let result: (Data, URLResponse) = try await getData() will always clearly produce a single result because we don’t use an async sequence to obtain a single result; we await the result of a task instead.

Even though this comparison technically compares Combine to async/await rather than async sequences, let’s take a look at an example of performing a network call with Combine vs. performing one with async/await to see which one looks more convenient.

Combine:

var cancellables = Set<AnyCancellable>()

func getData() {
    let url = URL(string: "https://donnywals.com")!
    URLSession.shared.dataTaskPublisher(for: url)
        .sink(receiveCompletion: { completion in
            if case .failure(let error) = completion {
                // handle error
            }
        }, receiveValue: { (result: (Data, URLResponse)) in
            // use result
        })
        .store(in: &cancellables)
}

Async/Await:

func getData() async {
    let url = URL(string: "https://donnywals.com")!
    do {
        let result: (Data, URLResponse) = try await URLSession.shared.data(from: url)
        // use result
    } catch {
        // handle error
    }
}

In my opinion it’s pretty clear which technology is more convenient for performing a task that produces a single result. Async/await is easier to read, easier to use, and requires far less code.

With this somewhat unfair comparison out of the way, let’s take a look at another example that allows us to more directly compare an async sequence to a publisher.

Receiving results from an operation that produces multiple values

Operations that produce multiple values come in many shapes. For example, you might be using a TaskGroup from Swift Concurrency to run several tasks asynchronously, receiving the result for each task as it becomes available. This is an example where you would use an async sequence to iterate over your TaskGroup's results. Unfortunately comparing this case to Combine doesn’t make a lot of sense because Combine doesn’t really have an equivalent to TaskGroup.

💡 Tip: to learn more about Swift Concurrency’s TaskGroup take a look at this post.

One example of an operation that will produce multiple values is observing notifications on NotificationCenter. This is a nice example because not only does NotificationCenter produce multiple values, it will do so asynchronously over a long period of time. Let’s take a look at an example where we observe changes to a user’s device orientation.

Combine:

var cancellables = Set<AnyCancellable>()

func notificationCenter() {
    NotificationCenter.default.publisher(
        for: UIDevice.orientationDidChangeNotification
    ).sink(receiveValue: { notification in
        // handle notification
    })
    .store(in: &cancellables)
}

AsyncSequence:

func notificationCenter() async {
    for await notification in await NotificationCenter.default.notifications(
        named: UIDevice.orientationDidChangeNotification
    ) {
        // handle notification
    }
}

In this case, there is a bit less of a difference than when we used async/await to obtain the result of a network call. The main difference is in how we receive values. In Combine, we use sink to subscribe to a publisher and we need to hold on to the provided cancellable so the subscription is kept alive. With our async sequence, we use a special for-loop where we write for await <value> in <sequence>. Whenever a new value becomes available, our for-loop’s body is called and we can handle the notification.

If you look at this example in isolation I don’t think there’s a very clear winner. However, when we get to the ease of use comparison you’ll notice that the comparison in this section doesn’t tell the full story in terms of the lifecycle and implications of using an async sequence in this example. The next part of this comparison will paint a better picture regarding this topic.

Let’s look at another use case where you might find yourself wondering whether you should reach for Combine or an async sequence; state observation.

Observing state

If you’re using SwiftUI in your codebase, you’re making extensive use of state observation. The mix of @Published and ObservableObject on data sources external to your view allow SwiftUI to determine when a view’s source of truth will change so it can potentially schedule a redraw of your view.

💡 Tip: If you want to learn more about how and when SwiftUI decided to redraw views, take a look at this post.

The @Published property wrapper is a special kind of property wrapper that uses Combine’s CurrentValueSubject internally to emit values right before assigning these values as the wrapped property’s current value. This means that you can subscribe to @Published using Combine’s sink to handle new values as they become available.

Unfortunately, we don’t really have a similar mechanism available that only uses Swift Concurrency. However, for the sake of the comparison, we’ll make this example work by leveraging the values property on Publisher to convert our @Published publisher into an async sequence.

Combine:

@Published var myValue = 0

func stateObserving() {
    $myValue.sink(receiveValue: { newValue in

    }).store(in: &cancellables)
}

Async sequence:

@Published var myValue = 0

func stateObserving() async {
    for await newValue in $myValue.values {
        // handle new value
    }
}

Similar to before, the async sequence version looks a little bit cleaner than the Combine version but as you’ll find in the next post, this example doesn’t quite tell the full story of using an async sequence to observe state. The lifecycle of an async sequence can, in certain case complicate our example quite a lot so I really recommend that you also check out the lifecycle comparison to gain a much better understanding of an async sequence’s lifecycle.

It’s also important to keep in mind that this example uses Combine to facilitate the actual state observation because at this time Swift Concurrency does not provide us with a built-in way to do this. However, by converting the Combine publisher to an async sequence we can get a pretty good sense of what state observation could look like if/when support for this is added to Swift.

Summary

In this post, I’ve covered three different use cases for both Combine and async sequences. It’s pretty clear that iterating over an async sequence looks much cleaner than subscribing to a publisher. There’s also no doubt that tasks with a single output like network calls look much cleaner with async/await than they do with Combine.

However, these examples aren’t quite as balanced as I would have liked them to be. In all of the Combine examples I took into account the lifecycle of the subscriptions I created because otherwise the subscriptions wouldn’t work due to the cancellable that’s returned by sink being deallocated if it’s not retained in my set of cancellables.

The async sequence versions, however, work fine without any lifecycle management but there’s a catch. Each of the functions I wrote was async which means that calling those functions must be done with an await, and the caller is suspended until the async sequence that we’re iterating over completes. In the examples of NotificationCenter and state observation the sequences never end so we’ll need to make some changes to our code to make it work without suspending the caller.

We’ll take a better look at this in the next post.

What is the “any” keyword in Swift?

With Swift 5.6, Apple added a new keyword to the Swift language: any. As you'll see in this post, usage of the any keyword looks very similar to how you use the some keyword. They're both used in front of protocol names, and they both tell us something about how that protocol is used. Once you dig deeper into what any means, you'll find that it's very different from some. In fact, you might come to the conclusion that any is somewhat of the opposite of some. In this post, you will learn everything you need to know about the any keyword in Swift as well as existentials, and what they are.

Let’s dive right into the any keyword by taking a look at its intended use in a very simple example:

protocol Networking {
    func fetchPosts() async throws -> [Post]
    // ...
}

struct PostsDataSource {
    let networking: any Networking
    // ...
}

💡Tip: If you’re not familiar with Swift’s some keyword or need a refresher, check out this post on Swift’s some keyword.

While the any keyword might look similar to the some keyword in the sense that both are used in front of a protocol, and sound like they convey a message similar to “I don’t care what’s used for this type as long as it conforms to this protocol”, they’re really not the same at all. To understand their differences, we need to take a look at what existentials are in Swift.

Understanding what an existential is in Swift

While some allows us to write code that more or less ignores, or discards, a protocol’s associated type and/or Self requirement while expecting that every returned object in a function that returns some Protocol has the same concrete type, the any keyword simply annotates that a given type is a so-called existential. While you might not know what an existential is, you've probably seen them used. For example, if we look at the "old" way of writing the PostsDataSource struct that you just saw, it would look as follows:

struct PostsDataSource {
    let networking: Networking
    // ...
}

Note that all I did is remove the any keyword. The Networking object that we use is an existential. This means that let networking is an object that conforms to Networking. The compiler doesn't know which object it will be, or what that object's type is. All the compiler knows is that there will be an object, any object, that will be assigned to let networking when we initialize PostsDataSource, and that object conforms to Networking. We're essentially only sure that we'll have a box that contains a Networking object. To know exactly which object was put in that box, we need to open that box at runtime, peek inside, and find the object.

It's important to know that existentials are relatively expensive to use because the compiler and runtime can’t pre-determine how much memory should be allocated for the concrete object that will fill in the existential. Whenever you call a method on an existential, like the networking property in the snippet you saw earlier, the runtime will have to dynamically dispatch this call to the concrete object which is slower than a static dispatch that goes directly to a concrete type.

The Swift team has determined that it’s currently too easy to reach for an existential over a concrete object. This essentially means that a lot of us are writing code that uses protocols (existentials) that harm our performance without us really being aware of it. For example, there’s nothing wrong with the old fashioned PostsDataSource you saw earlier, right?

struct PostsDataSource {
    let networking: Networking
    // ...
}

I’m sure we all have code like this, and in fact, we might even consider this best practice because we're not depending on concrete types which makes our code easier to test and maintain.

Sadly, this code uses an existential by having a property that has Networking as its type. This means that it’s not clear for the runtime how much memory should be allocated for the object that will fill in our networking property, and any calls to fetchPosts will need to be dynamically dispatched.

By introducing the any keyword, the language forces us to think about this. In Swift 5.6 annotating our let networking: Networking with any is optional; we can do this on our own terms. However, in Swift 6 it will be required to annotate existentials with the any keyword.

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.

Digging deeper into the any keyword

As I was reading the proposal for any, I realized that what the Swift team seems to want us to do, is to use generics and concrete types rather than existentials when possible. It’s especially this part from the introduction of the proposal that made this clear to me:

Despite these significant and often undesirable implications, existential types have a minimal spelling. Syntactically, the cost of using one is hidden, and the similar spelling to generic constraints has caused many programmers to confuse existential types with generics. In reality, the need for the dynamism they provided is relatively rare compared to the need for generics, but the language makes existential types too easy to reach for, especially by mistake. The cost of using existential types should not be hidden, and programmers should explicitly opt into these semantics.

So how should we be writing our PostsDataSource without depending on a concrete implementation directly? And how can we do that without using an existential since clearly existentials are less than ideal?

The easiest way would be to add a generic to our PostsDataSource and constraining it to Networkingas follows:

protocol Networking {
    func fetchPosts() async throws -> [Post]
    // ...
}

struct PostsDataSource<Network: Networking> {
    let networking: Network
    // ...
}

By writing our code like this, the compiler will know up front which type will be used to fill in the Network generic. This means that the runtime will know up-front how much memory needs to be allocated for this object, and calls to fetchPosts can be dispatched statically rather than dynamically.

💡Tip: If you’re not too familiar with generics, take a look at this article to learn more about generics in Swift and how they’re used.

When writing PostsDataSource as shown above, you don’t lose anything valuable. You can still inject different concrete implementations for testing, and you can still have different instances of PostsDataSource with different networking objects even within your app. The difference compared to the previous approach is that the runtime can more efficiently execute your code when it know the concrete types you’re using (through generics).

Alternatively, you could rewrite let networking to use some Networking instead of using a generic. To learn more about some, and how you can use it to replace generics in some situations, take a look at this post.

The only thing we’ve lost by not using any is the ability to dynamically swap out the networking implementation at runtime by assigning a new value of a different concrete type to networking (which we couldn’t do anyway because it’s defined as a let).

It's interesting to note that because we have to choose between any, some, and a generic, when we define our let networking, it's easier to choose the correct option. We could use : any Networking wherever we'd write : Networking in Swift 5.5 and earlier, and our code would work just fine but we might be using a suboptimal existential instead of a concrete type that can benefit from compile-time optimizations and static dispatch at runtime. In some cases, that's exactly what you want. You might need the flexibility that an existential provides, but often you'll find that you don't need an existential at all.

So how useful is the any keyword really? Should you be using it in Swift 5.6 already or is it better to just wait until the compiler starts enforcing any in Swift 6?

In my opinion, the any keyword will provide developers with an interesting tool that forces them to think about how they write code, and more specifically, how we use types in our code. Given that existentials have a detrimental effect on our code’s performance I’m happy to see that we need to explicitly annotate existentials with a keyword in Swift 6 onward. Especially because it’s often possible to use a generic instead of an existential without losing any benefits of using protocols. For that reason alone it’s already worth training yourself to start using any in Swift 5.6.

Note: take a look at my post comparing some and any to learn a bit more about how some can be used in place of a generic in certain situations.

Using any now in Swift 5.6 will smoothen your inevitable transition to Swift 6 where the following code would actually be a compiler error:

protocol Networking {
    func fetchPosts() async throws -> [Post]
    // ...
}

struct PostsDataSource {
    // This is an error in Swift 6 because Networking is an existential
    let networking: Networking
    // ...
}

The above code will at least need to be written using any Networking in Swift if you really need the existential Networking. In most cases however, this should prompt you to reconsider using the protocol in favor of a generic or writing some Networking in order to improve runtime performance.

Whether or not the performance gains from using generics over existentials is significant enough to make a difference in the average app remains to be seen. Being conscious of the cost of existentials in Swift is good though, and it’s definitely making me reconsider some of the code I have written.

The any keyword in Swift 5.7

In Swift 5.7 the any keyword is still not mandatory for all existentials but certain features aren't available to non-any protocols. For example, in Swift 5.7 the requirements around protocols with a Self requirement have been relaxed. Previously, if you wanted to use a protocol with an associated type of Self requirement as a type you would have to use some. This is why you have to write var body: some View in SwiftUI.

In Swift 5.7 this restriction is relaxed, but you have to write any to use an existential that has an associated type or Self requirement. The following example is an example of this:

protocol Content: Identifiable {
    var url: URL { get }
}

func useContent(_ content: any Content) {
    // ...
}

The code above requires us to use any Content because Content extends the Identifiable protocol which has an associated type (defined as associatedtype ID: Hashable). For that reason, we have to use any if we can't use some.

The same is true for protocols that use a primary associated type. Using an existential with a primary associated type already requires the any keyword in Swift 5.7.

Note that any isn't a drop in replacement for some as noted in my comparison of these two keywords. When using any, you'll always opt-in to using an existential rather than a concrete type (which is what some would provide).

Even though any won't be completely mandatory until Swift 6.0 it's interesting to see that Swift 5.7 already requires any for some of the new features that were made available with Swift 5.7. I think this reinforces the point that I made earlier in this post; try to start using any today so you're not surprised by compiler errors once Swift 6.0 drops.