How to check if two date ranges overlap in Swift

Dates in Swift can be compared to each other. This allows you to check whether one date comes before or after another date:

if dateOne > dateTwo {
  print("dateOne comes after dateTwo")
} else if dateOne < dateTwo {
  print("dateOne comes before dateTwo")
} else if dateOne == dateTwo {
  print("dateOne is equal to dateTwo")
}

You can also use dates in Swift to create ranges. And when you have one range, you can check whether it overlaps with another date. Let's look at an example:

struct Meeting {
  let startDate: Date
  let endDate: Date
}

func doMeetingsOverlap(_ meetingOne: Meeting, _ meetingTwo: Meeting) -> Bool {
  let leftRange = meetingOne.startDate ... meetingOne.endDate
  let rightRange = meetingTwo.startDate ... meetingTwo.endDate

  return leftRange.overlaps(rightRange)
}

In this sample code, we create a closed range using an object's start and end dates. This is done for two objects that we wanted to compare, and we then use ClosedRange's overlaps(_:) method to check if one date range has overlap with the other date range. Nice and simple!

If you have any questions about this tip, or if you have feedback for me, make sure to send me a Tweet.

Tips to ask better questions

As developers, we all get stuck sometimes. When this happens we start searching for solutions on Google, or we ask questions on Stackoverflow, on the Swift forums, the iOS Developers Slack community or other places. Over the past couple of years, I have been actively trying to help people solve problems they were stuck on and while doing that, I noticed that the art of asking good questions is not an easy one. In today's Quick tip I would like to offer you five tips that I think are important to help you get better at asking questions which will ultimately help you get better answers.

1. Provide context

When you're asking somebody a question, they are often not in the same mindset as you. Whether it's a colleague working on a different feature than you are, or somebody across the world who has never heard of you, or your project, you will need to get them on your page. I have seen people as questions like "Why doesn't my notification fire?" or "How do I fix a layout bug?" and it's really hard to answer questions like these without any context. A brief introduction that explains what you're trying to achieve or what you're working on goes a long way. For example, instead of writing the following question:

Why doesn't my notification fire?

You could write the following:

I'm working on an app that implements recurring local notifications. I have asked the user for notification permissions, and I have confirmed that permissions are granted in Settings but when I try to schedule my notifications, they never seem to be delivered. Any ideas why this might happen?

The latter version of this question provides far more information than the former and anybody who wants to help you is now in the same frame of mind as you are. A little bit of context really goes a long way for anybody who's trying to help you squash that pesky bug you're stuck on.

2. Explain what you have already tried

Far too often have I seen people ask questions that were framed decently, and then discard certain ideas or solutions that are offered because they have already tried those solutions. In programming, there is often more than one way to get from A to B, so when you're stuck on a problem or asking a question, it's really important to share the things you have already tried. This saves the person who's helping you a lot of time because they don't have to type out their ideas if they already know that they won't work for you. Additionally, you might be on the right track with what you've tried, and your problem is related to one of the solutions you've tried rather than the problem you're trying to solve.

Consider a scenario where you're trying to decode a JSON response and you just can't get it to work. If you only ask the following, you're unlikely to get a good answer:

I'm fetching data from the network, and I'm trying to decode this data into a struct. I can get the data just fine but how can I decode the JSON?

After all, you've already tried to decode the response! Instead, you might ask the following:

I'm fetching data from the network, and I'm trying to decode this data into a struct. I can get the data just fine but I can't seem to decode the JSON. I've already tried to write custom coding keys but I'm still seeing decoding errors.

This question still lacks some information which I'll address in my next tip, but the important part here is that anybody who reads this question knows that the person asking this question knows about JSONDecoder and CodingKeys. In other words, they know to put on their helping-you-to-debug-your-JSON-decoding-hats rather than their teaching-you-how-to-decode-JSON-hats.

3. Share your errors

Providing context and some solutions you may have tried is great. But in many cases, anybody who wants to help you still misses a crucial piece of information; the error message. If the person helping you doesn't know what errors you encounter (if any), it's really hard to help you debug a problem. Similarly, if you don't get an error but an unexpected outcome, it's equally important to share the unexpected outcome. Consider the following question:

I'm trying to decode some JSON data into a struct but I'm seeing an error. Any ideas?

Anybody who's willing to help you will follow up with a counter-question:

What's the error?

To streamline the process of getting help, and increasing the quality of potential answers, include the error in your question straight away:

I'm trying to decode some JSON data into a struct but I'm seeing the following error:
Swift.DecodingError.dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.})))
Any ideas?

The person reading this question now immediately knows that your problem is not with the JSON parsing logic. It's with the data you're trying to decode. This is extremely valuable information to have when you're helping somebody. Error messages in the Xcode console often contain crucial bits and pieces of information that point towards the root cause of an issue.

4. Show some code (in text please)

This tip does not always apply, but it often does. If you have an error, question or problem that involves code you have already written, it's very useful to share that code in your question. Keep in mind that whoever is reading your question does not want to go through your whole codebase just to figure out what you're asking. Distill your code to the relevant bits rather than sharing all of your code. Ideally, you would even reproduce your problem with as little code as possible. Sometimes distilling a problem down to the minimum amount of code needed to reveal the problem you have already helps you fix your own problem.

When you share your code, it's okay to replace certain names if you feel like they would give away what you're working on. Just make sure to pick sensible names. Names like Foo, Bar and Baz are common in sample code, but also horrible to read and they confuse the heck out of many people. Also, don't share your code as an image if possible. When you share code, the person who's reading it will often want to copy and paste your code into an editor so they can test it, modify it otherwise try it out somehow. Sharing a screenshot of your code prevents the reader of your question from doing this (essential) experimentation and makes it likely that they decide to not look into your question any further.

5. Get to the point

If you follow all the previous tips, you have a good question. But if you go overboard, you will have a lengthy, wordy question that is buried in details and burdened with background information. When you ask your question, make sure it's easy to read, and as short as possible without omitting any information. Consider the following example:

I was handed a design from our designer and it features some weird user interface components. If you look at the attached image, you'll see a navigation bar, an image, a button, and some text. When I tap the button, the text should fade out and then the next page should appear. The navigation bar should also move up. It's absolutely essential that this works smoothly. How can I handle the button tap and kick off the transition that my designer wrote?

This example might seem like an exaggeration but I have seen questions that were written like this. Instead of writing a question like the one above, something like this is far more valuable:

My designer coded an animation for me and I need to kick off this animation when a button is tapped. I have a function that's called when I tap the button, but I'm not entirely sure how to start the animation inside of this function. I have attached the animation code and my button handler.

The rewritten question is to the point and clearly communicates what you're asking from the reader with just enough detail for them to understand where you're coming from, and where you want to go.

In summary

Asking questions can be hard, especially for people whose first language isn't English. Regardless, I think that the five tips I shared in this article should be very helpful for anybody who asks questions. The more effort you put into writing a good question, the more likely you are to get a good answer, and the quicker you will get to the answer. In some cases, not all five tips can be applied to a question, but I have found that in most cases, you can adopt them in your question one way or the other.

Do you have any extra tips to ask good questions? Or do you have feedback for me? Come find me on Twitter! I love hearing from you.

Understanding Combine’s publishers and subscribers

In my previous post about Combine, I introduced you to Functional Reactive Programming (FRP) and I've shown you can subscribe to Combine publishers using the sink(receiveCompletion:receiveValue:) method. I also showed you how you can transform the output of publishers using some of its built-in functions like map and collect. This week I want to focus more on how Combine publishers and subscribers work under the hood.

By the end of today's post, you should have a clear picture of what Combine's core components are, and how they relate to each other. The topics covered in today's post are the following:

  • Learning how publishers and subscribers are tied together
  • Creating a custom subscriber
  • Writing a custom publisher

Learning how publishers and subscribers are tied together

In last week's post I explained that publishers send values to their subscribers, and that a publisher can emit one or more values, but that they only emit a single completion (or error) event. This explanation was good enough for an introduction, but the reality is a little bit more nuanced.

A publisher/subscriber relationship in Combine is solidified in a third object, the subscription. When a subscriber is created and subscribes to a publisher, the publisher will create a subscription object and it passes a reference to the subscription to the subscriber. The subscriber will then request a number of values from the subscription in order to begin receiving those values. This is done by calling request(_:) on the subscription. The number of values that a subscriber wants to receive is communicated using a Subscribers.Demand instance. A demand can be any of the following values:

  • Subscribers.Demand.none
  • Subscribers.Demand.unlimited
  • Subscribers.Demand.max(Int)

When you create a subscription by calling sink on a Publisher, the created subscriber calls request(_:) on its subscription with a value of Subscribers.Demand.unlimited because it wants to receive all values that are published, no matter how many. In most cases, this is exactly what you want, but sometimes a subscriber might want to limit the number of items it takes from a publisher. If you want to limit the number of items received from a publisher, you can call request with Subscribers.Demand.max(Int) where Int is replaced with the number of items that should be received. As new values come in, a subscriber can update the number of values it wants to receive from the publisher. Note that this number can only be incremented. This means that a subscriber that is created with a demand of .unlimited, it will always receive all values from the publisher it's subscribed to. If you call request(_:) with a demand of .max(1) and subsequently, call it with a demand of .max(10), the total number of items sent to the subscriber is 11 since demands are always additive.

When a new value is generated for a publisher, it's the subscription's job to mediate between the publisher and the subscriber and to make sure that subscribers don't receive more values than they request. Subscriptions are also responsible for retaining and releasing subscribers. The following diagram visualizes the complex relationship between subscribers, publishers, and subscriptions:

The subscriber flow

As you can see in the image above, the publisher doesn't really do an awful lot. It's mostly the subscription and the subscriber that interact with each other. In many cases, you will not need the publisher itself once a subscription is established, but this is not a given. The subscriber will ask the subscription for values, and the subscription will take care of sending these values to the subscriber. This does not mean that the subscription must do all of the work, that it must generate the values on its own, or that you can't use the publisher to generate values at all. You're free to implement your logic as you see fit as long as your implementation fits the contract that is established by the way objects relate to each other in Combine. For example, it's perfectly valid for a subscription to ask a publisher for the "next" value if that makes sense. It's equally valid for a subscription to execute a URLRequest and forward its result to a subscriber without consulting the publisher at all as long as it doesn't break the established contract.

To deepen your understanding of how publishers, subscriptions, and subscribers relate to each other I will show you how you can implement your own version of a URLSession.DataTaskPublisher. I briefly showed how you can use DataTaskPublisher in an earlier post I wrote about supporting low data mode in your app. My purpose today is not to teach you how to properly do networking in Combine. If you want to learn more about networking in Combine, you can check out this post. My purpose today is merely to show you how you can create your own publisher, subscription and subscriber triad from scratch. We'll start implementing a custom subscriber. After doing that I will show you how to implement a custom publisher and subscription.

Creating a custom subscriber

In the previous section, I explained that subscribers request (or demand) a number of values from a subscription object. You also learned that a publisher receives a subscriber, creates a subscription and ties the subscription and subscriber together. When you implement your own subscriber, part of this process happens outside of the subscriber, but there is also some work you need to do in your custom subscriber. Before we implement our own subscriber, let's look at the protocol requirements of the Subscriber protocol:

public protocol Subscriber : CustomCombineIdentifierConvertible {

  associatedtype Input
  associatedtype Failure : Error

  func receive(subscription: Subscription)
  func receive(_ input: Self.Input) -> Subscribers.Demand
  func receive(completion: Subscribers.Completion<Self.Failure>)
}

The Subscriber protocol has two associated types, Input and Failure. The concrete values for these associated types must match those of the publisher that it wants to subscribe to. If a subscriber expects Int as its Input, it can't subscribe to a publisher that has String as its Output. The same is true for the Failure associated type.

The three different flavors of receive that are required by the protocol are all used at a different time in the subscription lifecycle. The first, receive(subscription:) is called when a publisher creates and assigns a subscription object to the subscriber. At that point, the subscriber communicates its initial demand to the subscription. I will demonstrate this in a moment.

Next, receive(_:) is called every time the subscription pushes a new object to the subscriber. The subscriber returns a Subscription.Demand to the subscription to communicate the number of items it wants to receive now. As mentioned before, this demand is added to any demands that were sent to the subscription earlier. When the final value is generated, the subscription will call receive(completion:) with the result of the sequence. This method is only called once and concludes the stream of new values.

Before we implement a custom subscriber for the data task publisher that I mentioned at the end of the previous section, I want to show you a simple subscriber that takes a sequence of Int objects:

class IntSubscriber: Subscriber {
  typealias Input = Int
  typealias Failure = Never

  func receive(subscription: Subscription) {
    print("Received subscription")
    subscription.request(.max(1))
  }

  func receive(_ input: Input) -> Subscribers.Demand {
    print("Received input: \(input)")
    return .none
  }

  func receive(completion: Subscribers.Completion<Never>) {
    print("Received completion: \(completion)")
  }
}

(0...6).publisher.subscribe(IntSubscriber())

The typealias declarations in the code above specify that this subscriber takes Int as its input, and because Never is the failure type, we don't expect publishers that this subscriber subscribes to emit failures. This subscriber will only work on publishers that emit Int values, and no errors.

In the receive(subscription:) method, the subscriber receives a subscription and immediately requests a demand of .max(1). This means that this subscriber wants to receive a single value. The receive(_:) method receives a value and then returns .none. This means that we don't want to alter the demand of this publisher when we receive a new value. If we would return a demand of .max(1) here, we would increase the demand by one, and receive the next value. The last method, receive(completion:) is called when the subscriber finishes.

If you put this code in a playground, you'll find that the output is the following:

Received subscription
Received input: 0

As expected we only receive a single value here. What's interesting is that we never receive a completion event. By calling subscription.request(.max(1)), the subscriber explicitly communicates that it wants to receive a single event. No more, no less. Once the subscription has pushed that single value to the subscriber and the demand is not bumped in response to receiving that single value, the subscriber is discarded and the subscription is invalidated. If we want to receive all the values that are published in the preceding example, we need to update the receive(subscription:) method as follows:

func receive(subscription: Subscription) {
  print("Received subscription")
  subscription.request(.unlimited)
}

By requesting an unlimited number of values, all values are pushed from the subscription to the subscriber until the publisher sends a completion event. Makes sense, right? Let's look at the subscriber that's used in the next section when I show you how to build a custom version of a DataTaskPublisher:

// Skeleton for a subscriber, we'll finish it later in the article

class DecodableDataTaskSubscriber<Input: Decodable>: Subscriber {
  typealias Failure = Error

  func receive(subscription: Subscription) {
    print("Received subscription")
    subscription.request(.unlimited)
  }

  func receive(_ input: Input) -> Subscribers.Demand {
    print("Received value: \(input)")
    return .none
  }

  func receive(completion: Subscribers.Completion<Error>) {
    print("Received completion \(completion)")
  }
}

This subscriber is very similar to the IntSubscriber. The main difference is that the Input is defined as a generic type instead of a typealias. The Failure type is Error instead of Never because network requests can fail.

Note:
The subscriber in this code snippet is not yet in a properly usable state, I will show you how to finish and use this subscriber later. First, I want to show you how to create your own publisher and subscription objects.

Writing a custom publisher

Before we continue, I want to make sure you understand that writing a custom publisher is not something that you'll likely do in practice. It's also something that Apple does not recommend you do. In most cases, the default publishers and subjects that Combine exposes are more than good enough. In fact, I'm sure that the implementation I'm about to show you is not perfect, and that's okay. The purpose of the code in this section is to give you an idea of how Combine works under the hood by implementing a rudimentary publisher, not to write a clever, usable publisher that you might want to use in your projects.

Like I mentioned before, in this post I will show you a custom version of a DataTaskPublisher. This custom publisher will automatically decode Data into a Decodable model. The first step in building a custom publisher is to define the publisher itself. Before we do this, let's look at the Publisher protocol:

public protocol Publisher {

  associatedtype Output
  associatedtype Failure : Error

  func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input
}

The most interesting part of the Publisher protocol is the receive(subscriber:) method. This method constrains the generic subscriber S by requiring that S conforms to the Subscriber protocol and it ensures that the Failure and Output for the Publisher match the Failure and Input of the Subscriber. This receive(subscriber:) method is called immediately when the subscribe(subscriber:) method is called on the publisher. In the receive(subscriber:) method you are expected to create a Subscription object and pass it to the subscriber.

The following code defines the custom data task publisher I wanted to show you:

extension URLSession {
  struct DecodedDataTaskPublisher<Output: Decodable>: Publisher {
    typealias Failure = Error

    let urlRequest: URLRequest

    func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
      let subscription = DecodedDataTaskSubscription(urlRequest: urlRequest, subscriber: subscriber)
      subscriber.receive(subscription: subscription)
    }
  }

  func decodedDataTaskPublisher<Output: Decodable>(for urlRequest: URLRequest) -> DecodedDataTaskPublisher<Output> {
    return DecodedDataTaskPublisher<Output>(urlRequest: urlRequest)
  }
}

The preceding code defines a DecodedDataTaskPublisher<Output, Decodable> struct in an extension on URLSession. It also defines a convenience method that enables users of this custom publisher to create a DecodedDataTaskPublisher<Output, Decodable> in a way that's similar to how a normal DataTaskPublisher is created. The DecodedDataTaskPublisher has a property that holds the URLRequest that should be executed. In the receive(subscriber:) method, an instance of DecodedDataTaskSubscription is created. The subscription object receives the URLRequest and the subscriber, and the subscription is passed to the subscriber's receive(subscription:) method that you saw in the previous section.

All that's left to complete our publisher, subscription and subscriber triad, is to write the subscription object. Let's look at the Subscription protocol first:

public protocol Subscription : Cancellable, CustomCombineIdentifierConvertible {
  func request(_ demand: Subscribers.Demand)
}

The Subscription protocol itself only requires a request(_:) method to be implemented. Because Subscription inherits from the Cancellable protocol, subscriptions are also required to implement a cancel() method that's used to free up any resources that the subscription holds on to, like the subscriber itself for example. The request(_:) method is used by the subscriber to communicate the number of values the subscriber wants to receive and when you write a custom subscription it's your responsibility to honor this demand correctly and to not send more values than requested by the subscriber.

Now that you know a bit about the Subscription protocol, let's look at the implementation of the DecodedDataTaskSubscription object:

extension URLSession.DecodedDataTaskPublisher {
  class DecodedDataTaskSubscription<Output: Decodable, S: Subscriber>: Subscription
    where S.Input == Output, S.Failure == Error {

    private let urlRequest: URLRequest
    private var subscriber: S?

    init(urlRequest: URLRequest, subscriber: S) {
      self.urlRequest = urlRequest
      self.subscriber = subscriber
    }

    func request(_ demand: Subscribers.Demand) {
      if demand > 0 {
        URLSession.shared.dataTask(with: urlRequest) { [weak self] data, response, error in
          defer { self?.cancel() }

          if let data = data {
            do {
              let result = try JSONDecoder().decode(Output.self, from: data)
              self?.subscriber?.receive(result)
              self?.subscriber?.receive(completion: .finished)
            } catch {
              self?.subscriber?.receive(completion: .failure(error))
            }
          } else if let error = error {
            self?.subscriber?.receive(completion: .failure(error))
          }
        }.resume()
      }
    }

    func cancel() {
      subscriber = nil
    }
  }
}

There's a lot of code in the preceding snippet. The most important part of this code is the request(_:) method. If this method is called with a demand that's larger than zero, a regular data task is created and kicked off. The completion handler for this data task uses the defer statement to call cancel on the subscription after the response from the data task is handled. We do this because once the data task is completed, the subscription will emit no further values because its job is done.

If the data task succeeded, and the callback receives data, an attempt is made to decode the data into the Output type. If this succeeds, the resulting object is passed to the subscriber so it can receive and handle the decoded response. Immediately after, we send a completion event to indicate that we finished successfully. If the decoding fails, or if we receive an error instead of data, we complete the subscription with .failure(error) so the subscriber can handle the error.

Before we attempt to use our custom subscriber, publisher and subscription together, let's try to use just the publisher and subscription first. We can do this by subscribing to the custom publisher using sink:

struct SomeModel: Decodable {}
var cancellable: AnyCancellable?

func makeTheRequest() {
  let request = URLRequest(url: URL(string: "https://www.donnywals.com")!)
  let publisher: URLSession.DecodedDataTaskPublisher<SomeModel> = URLSession.shared.decodedDataTaskPublisher(for: request)
  cancellable = publisher.sink(receiveCompletion: { completion in
    print("Received completion: \(completion)")
  }, receiveValue: { value in
    print("Received value: \(value)")
  })
}

makeTheRequest()

In the preceding example, I wrapped the code to make a request using the custom publisher in a function to simulate real-world usage. If you remove cancellable = from the code above, you will notice that the AnyCancellable returned by sink is not retained, and the completion and value closures are never called. If you run the above code as-is, the completion closure is called with a failure event. This is expected because the endpoint https://www.donnywals.com does not return a valid JSON response. While it's pretty neat that our custom publisher and subscription seem to work as intended, let's try to use it with our custom subscriber instead of sink:

struct SomeModel: Decodable {}
var cancellable: AnyCancellable?

func makeTheRequest() {
  let request = URLRequest(url: URL(string: "https://www.donnywals.com")!)
  let publisher: URLSession.DecodedDataTaskPublisher<SomeModel> = URLSession.shared.decodedDataTaskPublisher(for: request)
  let subscriber = DecodableDataTaskSubscriber<SomeModel>()
  publisher.subscribe(subscriber)
}

makeTheRequest()

If you run the code above, you'll find that only the Received subscription statement from the custom subscriber is printed. It appears that the subscriber is deallocated before it receives any events from the subscription. To confirm this, add the following code to DecodableDataTaskSubscriber:

deinit {
  Swift.print("deinit subscriber")
}

This will show you when the subscriber is deallocated, and if you run the code again you'll see that deinit subscriber is printed immediately after Received subscription. The reason for this is simple and complex at the same time.

When you call subscribe(_:) on a publisher, it creates a subscription and passes this subscription to the subscriber. It does not retain the subscription it creates, which means that the subscription will be deallocated once the publishers' subscribe(_:) method finishes unless another object retains the subscription.

When the subscription is initialized, it receives the subscriber object that it will push values to and the subscription stores this subscriber object for future reference. The subscriber itself only accesses the subscription once when its receive(subscription:) method is called. In our current implementation, it does not retain the subscription at all. And since the subscription itself is also not held on to outside of makeRequest, both the subscription and the subscriber are both deallocated by the time makeRequest exits.

Based on the description above, we need an object to hold on to our subscription to prevent it from being deallocated. Preferably, this object is Cancellable so it can be used to cancel the subscription if needed. Based on the following excerpt from Apple's documentation on Subscriber, the subscriber itself is a good candidate for retaining the subscription and canceling it later:

You connect a subscriber to a publisher by calling the publisher’s subscribe(_:) method. After making this call, the publisher invokes the subscriber’s receive(subscription:) method. This gives the subscriber a Subscription instance, which it uses to demand elements from the publisher, and to optionally cancel the subscription.

Update the DecodableDataTaskSubscriber so it's implementation looks as follows:

class DecodableDataTaskSubscriber<Input: Decodable>: Subscriber, Cancellable {
  typealias Failure = Error

  var subscription: Subscription?

  func receive(subscription: Subscription) {
    print("Received subscription")
    self.subscription = subscription
    subscription.request(.unlimited)
  }

  func receive(_ input: Input) -> Subscribers.Demand {
    print("Received value: \(input)")
    return .none
  }

  func receive(completion: Subscribers.Completion<Error>) {
    print("Received completion \(completion)")
    cancel()
  }

  func cancel() {
    subscription?.cancel()
    subscription = nil
  }
}

By conforming DecodableDataTaskSubscriber to Cancellable and holding on to the subscription it receives in receive(subscription:) we created somewhat of a retain cycle which keeps all objects alive. The subscriber holds on to its subscription and the subscription holds on to the subscriber. By canceling the subscription and setting it to nil when the subscriber's cancel method, we make sure to break the cycle when needed. Surprisingly enough, I have found that Subscribers.Sink seems to work in the same way. The major difference is that when you use sink to subscribe to a publisher, the Subscribers.Sink is immediately wrapped in an AnyCancellable instance that, when deallocated, calls cancel() on the cancellable object it wraps which immediately breaks any retain cycles that may have existed.

With the code above, you have completed our triad of a custom publisher, subscription and subscriber. That's a huge achievement! Even though you won't be building all of these components yourself in most cases, it's good to know what happens on the inside and I hope you now know a lot more about Combine and what it does behind the curtain.

In summary

Today's post is a long one, but it's also an important one. I hope that I was able to show you how publishers, subscribers, and subscriptions work in the Combine framework, and what role they fulfill. All publishers that you use in Combine are built by composing the same building blocks in one way or the other. What's important is that you understand what these building blocks are, and what role they fulfill.

To summarize, when a subscriber wants to subscribe to a publisher, the publisher creates a subscription. The subscriber is passed to this subscription. The subscription is then passed to the subscriber so the subscriber can ask for an initial number of items. The subscription will then push values to the subscriber, and the subscriber can adjust its demand every time a value is received. If the subscriber asks for a number of items that is greater than, or equal to the number of items pushed by the subscription, it will eventually receive a completion or error event. If the subscriber demands fewer values than the subscription will push, the subscriber will not receive a completion event.

Since this post is quite long and complex, I would recommend that you let this sink in for a moment. If you're confused or overwhelmed, that's okay. Come back to this post again at a later time. And most importantly, try to follow along with the code presented in this post. Put it in a Playground and experiment with it. It will eventually make sense.

if you have any questions for me, or if you have feedback, make sure to let me know on Twitter.

Removing a specific object from an Array in Swift

Arrays in Swift are powerful and they come with many built-in capabilities. One of these capabilities is the ability to remove objects. If you want to remove a single, specific object from an Array and you know its index, you can use remove(at:) to delete that object:

var array = [1, 2, 3]
array.remove(at: 0) // array is now [2, 3]

If you don't know the object's position in the Array and the Array's elements conform to Equatable, you can look up the first index of the object you're looking for with firstIndex(of:) and you can then proceed to delete the item at that position:

var array = ["hello", "world"]
if let index = array.firstIndex(of: "hello") {
  array.remove(at: index) // array is now ["world"]
}

Note that this will only remove a single object, so if you have an array with repeating values you will only remove the first match:

var array = ["hello", "world", "hello"]
if let index = array.firstIndex(of: "hello") {
  array.remove(at: index) // array is now ["world", "hello"]
}

If you want to remove all matches for an object, you can use the closure based removeAll(where:):

var array = ["hello", "world"]
array.removeAll { value in
  return value == "hello"
}
// array is now ["world"]

The closure you pass to removeAll(where:) is executed for every element in the Array. When you return true from the closure, the element is removed from the Array. When you return false, the element remains in the Array.

Performance of removing elements from an array

While using removeAll(where:) is convenient, its performance is worse than using firstIndex(of:) to find an index and then removing the element using remove(at:) since firstIndex(of:) stops iterating your array as soon as a match is found while removeAll(where:) keeps iterating until it processed all items in your array. So while removeAll(where:) is more convenient to use, firstIndex(of:) alongside remove(at:) are better for performance.

If you have any questions about this tip, or if you spot inaccuracies or have feedback for me, don't hesitate to reach out on X or Threads!

How to filter an Array in Swift?

When you have an Array of elements, and you want to drop all elements that don't match specific criteria from the Array, you're looking for Array's filter(isIncluded:) method. Let's say you have an array of words and you only want to keep the words that are longer than three characters:

let words = ["hello", "world", "this", "is", "a", "list", "of", "strings"]
let filtered = words.filter { word in
  return word.count >= 3
} // filtered is ["hello", "world", "this", "list", "strings"]

The filter(isIncluded:) method takes a closure that is executed for every element in the source Array. If you return true from this closure, the element will be included in a new, filtered Array. If you return false, the element is ignored and won't be included in the new Array. When you apply a filter on an Array, the original Array is not modified. Instead, filter(isIncluded:) creates a new Array with only the elements you want.

You can perform any kind of calculation, comparison or computation in the closure that you use, just keep in mind that the more work you in your closure, the slower your filter might become. Ideally, you should make sure that you do as little work as possible when filtering an Array.

If you have any questions about this tip, or if you have feedback for me, don't hesitate to send me a Tweet!

Five tips to write better todos in Xcode

We all write the dreaded // TODO: and // FIXME: comments every once in a while. Sometimes we do it because we know our code can be better but we're not sure how, other times we don't have the time to write an optimal solution because of deadlines, and other times we just want to move on to more interesting problems to solve and we just slap a // TODO: on our code and call it a day.

In today's post, I would like to give you five tips to make sure your todos eventually get fixed and don't end up haunting your code unnoticed until the end of times.

1. Sign your todos with a date and your name

The first tip is possibly the most important in my opinion. If you leave a todo in your code, sign it with your name and with the current date. This makes it really clear to you and anybody on your team how long the todo has been there for, and how who left the todo there. The purpose is not to play the blame game or to point fingers but more to ask the original todo writer if the todo is still relevant, and whether there is a Jira ticket, Trello ticket or any other kind of ticket in whatever system you use to track work that must be done.

It's also possible to extract the todo author and date from git but this can sometimes be problematic, especially if code marked with the todo is moved around, or a typo is fixed because the todo will then carry an entire history. It's much more convenient to have the basic information about the todo item available at a glance.

2. Mark your todo with #warning

If your project has very few warnings and you tend to pay attention to the warnings that Xcode shows in your project, it's a good idea to make your todos show up as compiler warnings. You can do this by writing your todos as follows:

#warning("TODO: donnywals, 2020-01-08 - Improve this algorithm, I'm sure we can get this to O(1) somehow.")

When you write your todo as a #warning rather than a comment, Xcode will treat it as a compiler warning. This makes your todos stand out even more, and when combined with tip 1 your todos will be easily visible because they show up as warnings and you have all the context you might night right there in the warning.

3. Use SwiftLint to mark todos as warnings

An alternative to using #warning is to use SwiftLint. SwiftLint is a tool that helps you and your team write nicely formatted Swift code by analyzing your code and showing compiler warnings when you violate any of its rules. SwiftLint has a rule that flags any occurrences of a // TODO comment in your as compiler warnings.

If you don't like using #warning for your todos because you prefer comments, but also want to be warned about any todos in your code then SwiftLint is the way to go.

4. Remove todos if they don't get done for a while

To avoid everlasting todo items that never get fixed, make sure to go through and clean up your todos every once in a while. If you mark your todos with an author and a date, it's easy to find out which todos have been lingering for far too long and you can quickly reach out to your teammates to find out what the status of a todo is and whether it's still relevant.

A good approach here is to set aside an hour or so every couple of weeks to go through the project I'm working on to see if there are any todos that have been around for more than a month and where I don't know what their status is. I have noticed that when a todo has been around for more than a month or two, they have often become irrelevant, or I realize that implementing the todo hasn't been prioritized yet. This often leads to the todo getting prioritized, or removing it because the todo is no longer relevant.

5. Avoid todos altogether

This one is cheating a bit, but the best way to have better todos is just to avoid them completely. Whenever you find yourself writing a todo, ask yourself why. You'll often find that the answer is that you just want to move on to the next thing or because you're not sure how to do the thing you want to do, and for some reason, you decided that you don't want to figure it out right now. In many cases, you might just as well go ahead and do what needs to be done right away and avoid writing the todo altogether.

If you're reviewing a PR from one of your teammates and you spot a todo in their code, it's absolutely okay to ask them why the todo is there and try to convince them to take care of the todo now rather than at some time in the future. If the todo is reasonable, try to find out if the todo has been added to your ticketing system to make sure the work is prioritized eventually so the todo can be marked as completed.

In summary

I hope that with this list of ways to get better at writing todo items you can improve your codebase a little bit. Todos are inevitable in most codebases, but it's important that manage them well to avoid having lots of hidden todos in your code that nobody remembers or cares about anymore.

If you have any more tips on how you handle todos in your projects, make sure to let me know!

Getting started with Combine

The Combine framework. Silently introduced, yet hugely important for iOS. It didn't get any attention during the big Keynote at WWDC 2019, but as soon as folks were in the sessions they knew that Combine was going to be huge. It implements a Functional Reactive Programming (FRP) paradigm that's similar to that of Rx which is implemented by RxSwift, except it's made by Apple and has native support on all Apple platforms as long as they are running iOS 13+, iPadOS 13+, macOS 10.15+, watchOS 6+ or tvOS 13+.

The fact that Apple created their own FRP framework is a big deal, and it gives off a signal to the developer community. SwiftUI makes heavy use of Combine and Apple has integrated Combine in several existing APIs as well. And since Combine is created and owned by Apple, it can be used without any third-party dependencies, and we can rest assured that Apple will continue to support Combine for the foreseeable future.

In today's post, I would like to help you get started with Combine and show you the basics of what it is, how it works, and what it can do. You will learn the following topics:

  • Understanding what Functional Reactive Programming is
  • Understanding publishers and subscribers
  • Transforming publishers

There is a lot to cover in this post, so make sure you're comfortable, grab yourself something to drink and put on your learning hat.

Understanding what Functional Reactive Programming is

In the world of FRP, your code is written in a way where data flows from one place to the other automatically through subscriptions. It uses the building blocks of Functional Programming like, for example, the ability to map one dataflow into another. FRP is particularly useful in applications that have data that changes over time.

For example, if you have a label that displays the value of a slider, you can use FRP to push the value of your slider through a stream, or publisher which will then send the new value of the slider to all subscribers of the stream, which could be the label that shows the slider value, or anything else.

In addition to driving UI updates, FRP is also incredibly useful in asynchronous programming. Consider a network request. When you make the request, you expect to get a result back eventually. Usually, you would pass a completion closure to your request which is then executed when the request is finished. In FRP, the method you call to make a request would return a publisher that will publish a result once the request is finished. The benefit here is that if you want to transform the result of the network request, or maybe chain it together with another request, your code will typically be easier to reason about than a heavily nested tree of completion closures.

I won't cover networking or UI updates in today's post. Instead, we'll go over some more basic examples of publishers and transforming values to prepare them for display. I will cover networking and UI updates in future posts.

Understanding publishers and subscribers

A Combine publisher is an object that sends values to its subscribers over time. Sometimes this is a single value, and other times a publisher can transmit multiple values or no values at all. While a publisher can publish a variable number of values, it can only emit a single completion or error event. Since it's common to represent the flow of a publisher as a so-called marble diagram, let's examine one now.

Example marble diagrams. Top exits normally, bottom with arrow

The image above contains two marble diagrams. Each diagram is shown as an arrow. Each arrow represents a publisher. The circles, or marbles, on each line, represent the values that the publisher emits. The top arrow has a line at the end. This line represents a completion event. After this line, the publisher will no longer publish any new values.

The bottom diagram ends with a cross. The cross represents an error event. Errors end the stream of values, similar to a completion event. In other words, something went wrong and the publisher will now no longer publish any new events. Every publisher in the Combine framework uses these same rules, with no exceptions. Even publishers that publish only a single value must publish a completion event after publishing their single value.

Subscribing to a simple publisher

We can model the stream of values that are published by a publisher as an array. In fact, we can even use arrays to drive simple publishers. Let's create a simple publisher that publishes a list of integers:

[1, 2, 3]
  .publisher
  .sink(receiveCompletion: { completion in
    switch completion {
    case .failure(let error):
      print("Something went wrong: \(error)")
    case .finished:
      print("Received Completion")
    }
  }, receiveValue: { value in
    print("Received value \(value)")
  })

There is a lot to unpack in the snippet above. The Combine framework adds a publisher property to Array. We can use this property to turn an array of values into a publisher that will publish all values in the array to the subscribers of the publisher.

The type of the created publisher is Publishers.Sequence<[Int], Never>. Based on this type signature, we can derive that there is a Publishers object in the Combine framework. You can look up the Publishers object in the documentation and you'll find this page. We can find the following short description for Publishers there:

A namespace for types that serve as publishers.

In other words, all the built-in publishers in the Combine framework are grouped under the Publishers enum. Each of the publishers that exist in this namespace conforms to the Publisher protocol and has a specific role. You will rarely have the need to directly create instances of the publishers contained in the Publishers enum. Instead, you'll often create them by calling methods or properties on other objects. Similar to how we created a Publishers.Sequence<[Int], Never> publisher by calling publisher on our array.

The generic types in the definition of Publishers.Sequence<[Int], Never> are [Int] and Never in the preceding example. This means that the publisher will use a sequence of type [Int] to publish values and that its failure type is Never. This tells us that the Sequence publisher will always complete successfully. This means that in the example above, the .failure case in the switch will never be hit and we can always assume success in the receiveCompletion closure of a sink where the failure type is Never. In fact, there is a special version of sink available on publishers that have a failure type of Never where you only supply a receiveValue closure.

Every publisher in Combine has an Output and a Failure type. The Output is the type of value that a publisher will push to its subscribers. In the case of our Sequence, the Output will be Int. The Failure type is Never because the publisher cannot finish with an error. Publishers that can fail will often use an object that conforms to Error as their Failure type, but you're free to specify any type you want.

Tip:
If you want to learn more about generics, check out some of the posts I have written on that topic:

You can subscribe to a publisher using the sink(receiveCompletion:receiveValue:) method. This method creates a subscriber that is subscribed to the publisher that the method was called on. It's important to note that publishers only publish values when they have subscribers. Calling sink(receiveCompletion:receiveValue:) on a publisher creates a subscriber immediately and enables the publisher to begin streaming values.

For posterity, the output of the preceding code snippet is the following:

Received value 1
Received value 2
Received value 3
Received Completion

The receiveValue closure is called whenever a new value is published by the publisher. This closure receives the latest value of the publisher as its single argument. In the completion handler, we can check whether a subscription failed with an error or if it completed normally. You can use a switch statement and pattern matching to extract the error and handle it as needed. Combine has more advanced error handling mechanisms that I won't go into in today's post. For now, it's important that you understand how publishers and subscriptions work at the surface.

Let's take a closer look at the subscription object that is created when you call sink(receiveCompletion:receiveValue:).

Keeping track of subscriptions

In the previous subsection, you learned a bit about subscribing to a publisher by calling sink(receiveCompletion:receiveValue:) on the publisher itself. In the example code, we did not store the object that's returned by sink(receiveCompletion:receiveValue:), which is perfectly fine in a Playground. However, in your applications, you need to hold on to the subscriptions you create. If you don't do this, the subscription object will be discarded as soon as the scope where you create the subscription is exited. So if you were to call sink in a function, the created subscription would cease to exist at the end of the function.

If you examine the return type of sink, you will find that it's AnyCancellable. An AnyCancellable object is a type-erased wrapper around a Cancellable subscription that you can hold on to in your view controller. You can safely hold on to your AnyCancellable objects and be assured that any subscriptions are canceled when the object that's holding on to the AnyCancellable is deallocated. You only need to call cancel yourself if you explicitly want to discard a given subscription.

Note:
If you have used RxSwift in the past, you may have worked with an object called DisposeBag, and you would have added Disposable objects to the DisposeBag. Combine does not have an equivalent of DisposeBag, but it does have an equivalent of Disposable which is the Cancellable protocol.

Consider the following example:

var subscription: AnyCancellable?

func subscribe() {
  let notification = UIApplication.keyboardDidShowNotification
  let publisher = NotificationCenter.default.publisher(for: notification)
  subscription = publisher.sink(receiveCompletion: { _ in
    print("Completion")
  }, receiveValue: { notification in
    print("Received notification: \(notification)")
  })
}

subscribe()
NotificationCenter.default.post(Notification(name: UIApplication.keyboardDidShowNotification))

In the preceding code, we use the convenient publisher(for:) method that was added to NotificationCenter to subscribe to the UIApplication.keyboardDidShowNotification notification. If you place this code in a Playground, you'll find that the print statement in the receiveValue closure is executed, but the receiveCompletion is never called. The reason for this is that NotificationCenter publisher can send an infinite number of notifications to its subscribers.

If you remove the assignment of subscription by removing subscription = before publisher.sink you will find that the receiveValue closure is never called due to the subscription being discarded as soon as the subscribe() function is done executing.

In addition to automatic cancellation of subscriptions when an AnyCancellable is deallocated, you can also explicitly cancel a subscription by calling cancel() on the AnyCancellable that contains the subscription, or directly on any object that conforms to Cancellable. To try this, you can add the following two lines after the code snippet I just showed you:

subscription?.cancel()
NotificationCenter.default.post(Notification(name: UIApplication.keyboardDidShowNotification))

If you put this in a playground, you'll find that the receiveValue closure is only called once because the subscription is canceled after the first notification is posted.

If you examine the type of object that is returned by publisher(for:), you'll find that it's a NotificationCenter.Publisher. This doesn't tell us much about the type of object and error it might publish. When you call sink(receiveCompletion:receiveValue:) on the publisher, you'll notice that the receiveCompletion closure has a single argument of type Subscribers.Completion<Never> and the receiveValue has an argument of type Notification. In other words, the Output of NotificationCenter.Publisher is Notification, and its Failure is Never. You can confirm this by looking up NotificationCenter.Publisher in the documentation and examing the Output and Failure type aliases.

Tip:
Speaking of type aliases, if you want to learn more about how and when to use them, check out my five ways to improve code with type aliases.

At this point, you know that Combine revolves around publishers and subscribers. You know that publishers only publish values when they have active subscribers and that you can quickly subscribe to publishers using the sink(receiveCompletion:receiveValue:) method. You saw two publishers in this section, a Publishers.Sequence<[Int], Never> publisher and a NotificationCenter.Publisher which publishes Notification objects and has Never as its error type. You also know that publishers will publish values until they emit either an error or a completion event. While this is really cool and useful already, let's look at another important key feature of Combine; transforming publishers.

Transforming publishers

When you subscribe to a publisher, you often don't want to use the values that it emits directly. Sometimes you'll want to format the output of a publisher so you can use it to update your UI, other times you need to extract some values from, for example, a notification for the published value to be useful.

Because Combine is a Functional Reactive Programming framework, it supports some of the foundational features that you might know from functional programming. Publishers support several transforming operators, like map or flatMap. We can use these transforming operators to transform every value that is emitted by a stream, into another value. Let's look at a marble diagram that describes how map works in Combine:

Example of a "map" marble diagram

The marble diagram above describes a publisher that emits values over time. By using the map operator on the publisher, a new publisher is created. Its type is Publisher.Map<Upstream, Output>. The Upstream generic must be another publisher, and the Output generic is the output of this new publisher. So when we use the earlier example where we subscribed to the UIApplication.keyboardDidShowNotification, we can extract the keyboard height and push that to subscribers instead of the full Notification object using the following code:

let publisher = NotificationCenter.default
  .publisher(for: notification)
  .map { (notification) -> CGFloat in
    guard let endFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
      return 0.0
    }

    return endFrame.cgRectValue.height
}

When you subscribe to the created publisher object you will receive CGFloat values rather than Notification objects. The type of publisher in the above example is Publishers.Map<NotificationCenter.Publisher, CGFloat>. In other words, its a map publisher that takes NotificationCenter.Publisher as its Upstream and CGFloat as its Output.

The general pattern in Combine is that every time you apply a transformation to a publisher, you create a new publisher that wraps the original publisher and has a new output. Sounds confusing? I know. Let me show you another example. This time we'll look at the collect operator. Let's look at the marble diagram first:

Example of a "collect" marble diagram

The pictured marble diagram shows that the collect operator takes values from a publisher, collects them into an array and sends them to its subscribers when a threshold is met. Let's look at this in a code example:

[1, 2, 3]
  .publisher
  .collect(2)
  .sink(receiveValue: { value in
    print("Received value \(value)")
  })

Note that I have omitted the receiveCompletion closure in the call to sink above. This is perfectly fine if you're not interested in completion events from a publisher, or if you know that it will never emit an error, which the Publishers.Sequence doesn't. After creating the sequence publisher, .collect(2) is called on the publisher which transforms it into a Publishers.CollectByCount publisher that wraps the original sequence publisher. This publisher uses the threshold that we supply and emits values whenever the threshold is met. The above code produces the following output in a playground:

Received value [1, 2]
Received value [3]

When a publisher completes before the threshold is met, the buffer is sent to the subscriber with the items that have been collected so far. If you don't specify a threshold at all, and call collect() on a publisher, the publisher is transformed into a Result<Success, Failure>.Publisher. This publisher uses an array of a publisher's output as the Success value of Result, and the Failure is the publisher's Failure. When the upstream publisher has completed, either with success or an error, the Result<Success, Failure>.Publisher will emit a single value that contains all values that were published, or an error.

Note that the collect() method with no threshold could cause your app to use an unbounded amount of memory if a publisher emits many events before completion. You'll usually want to specify a sensible threshold for your combine operations.

At the beginning of this section, I mentioned the flatMap operator. I'm not going to show how flatMap works in today's post. The reason is simple. Using flatMap is a fairly advanced concept where you can take a publisher that publishes other publishers, and you can use flatMap to publish the values from all of these publishers on a single new publisher. I will demonstrate flatMap in next week's post where we'll convert an existing networking layer to make use of Combine.

In summary

Today's post taught you the very basics of Combine. You learned what publishers are, and the basics of how they work. You learned that publishers push values to their subscribers until they have no more values to emit and are completed, or until they emit an error. You also learned that you can subscribe to a publisher using the sink(receiveCompletion:receiveValue:) method, and that this method creates an AnyCancellable object that contains the subscription and must be retained for as long as you need it to make sure your subscription stays alive.

After learning the basics of publishers and subscribers, I showed you the basics of how you can transform publishers of one type, into publishers of a different type, much like how map works on an array. The ability to chain together publishers, and transform them using different functions is one of the most powerful features of FRP and you will find that these transformations truly are the heart of Combine once you start using it more often.

This post is part of an ongoing series I'm planning to do where I will gradually teach you more and more about Combine using practical examples and sometimes more theoretical overviews. You can find all the posts in this series right here. This post is the first post in this series so if you're reading this post early after I published it, there probably isn't much else to read yet. Make sure to subscribe to my newsletter below, and to follow me on Twitter to be notified when I publish new content.

Swift’s typealias explained with five examples

Swift grants developers the ability to shadow certain types with an alternative name using the typealias keyword. We can use this feature to create tuples and closures that look like types, or we can use them to provide alternative names for existing objects. In this post, we'll look at five ways in which typealiases can help you write cleaner and better code.

1. Improving readability with type aliases

Perhaps this is the most obvious yet also somewhat underused way to use a typealias. When you have a type in your code that is very long or deeply nested, you could end up with rather long type declarations.

The following code snippet is an example of a rather long type name:

UICollectionViewDiffableDataSource<CardSection.Diffable, Card.Diffable>

I have made the previous declaration nice and long on purpose, but it's not far from code that I have actually written. You can make the type declaration above much shorter with a simple typealias:

typealias CardDataSource = UICollectionViewDiffableDataSource<CardSection.Diffable, Card.Diffable>

After defining this in your code, the Swift compiler will now know that whenever you write CardDataSource, you really mean UICollectionViewDiffableDataSource<CardSection.Diffable, Card.Diffable> instead. This is really neat because instead of writing the same long, hard to read type declaration over and over, you can instead use the shorter, more descriptive type alias you created.

The same idea can be applied to functions that take a completion closure:

func fetchCards(_ completion: @escaping (Result<[CardSection], Error>) -> Void) {
  // implementation
}

While the code above isn't horrible, we can make it a bit more readable with a typealias:

typealias FetchCardsHandler = (Result<[CardSection], Error>) -> Void

func fetchCards(_ completion: @escaping FetchCardsHandler) {
  // implementation
}

Reading this alternative function that uses a typealias is easier on the eyes since there is much less information to unpack in the function declaration. On the other hand, if someone is trying to figure out what the FetchCardsHandler is they will need to dig a little bit deeper than they would if the function directly exposed the closure's type signature. I'll leave it up to you to decide whether you prefer typealias or closure signatures in your functions since you could argue for either and make a compelling case in my opinion.

As you've seen in this first type, typealias can improve readability but in some cases they can hurt discoverability. In the case of the diffable data source typealias, this isn't much of a problem. In the closure case, this can be a bit more problematic. As with any technique, always consider your options carefully and when in doubt favor clarity and obviousness.

2. Communicate intention

A well-placed type alias can help developers determine what valid inputs for a certain method are, or what the inputs are used for. A good example is the TimeInterval alias that's used on iOS. Whenever you see that a certain method or property has the type TimeInterval you immediately get a sense of what kind of inputs you can use, and what will be done with them. Internally, however, a TimeInterval is nothing more than a typealias.

Another example of a type alias that communicates an intention to the user is a special ID type that's used inside of a struct:

typealias ID = Int

struct Card {
  let id: ID
  // more properties and code
}

struct CardSection {
  let id: ID
  // more properties and code
}

In the preceding snippet, you can see that the Card and CardSection objects both have an id property who's type is ID. While this doesn't look that impressive, it's pretty neat. We can now enforce that Card and CardSection use the same type as their ID by ensuring that they both use the same aliased type. In this case, both identifiers have to be integers. This can provide a lot of meaning to other developers reading this code and when used carefully can really make a big difference in your code.

3. Combining protocols

This is possibly one of the more interesting and obscure ways to use a typealias that I actually use regularly myself. I even wrote an article about it a while ago so I won't go in-depth too much. In Swift, you can use a typealias to create a placeholder for something that conforms to one or more protocols. For example, you can do the following:

typealias CommonDataSource = UICollectionViewDataSource & UITableViewDataSource

The result of the code above is that any object that claims to be a CommonDataSource must conform to both UICollectionViewDataSource and UITableViewDataSource. Since this example is cool but not very useful, let me give you another brief example:

public typealias Codable = Decodable & Encodable

Does the above look familiar? The idea of it probably does! If you declare a type to be Codable, it's both Encodable and Decodable. The way this is defined in Swift is through a typealias that looks exactly like the one in this code snippet.

4. Named tuples

In Swift, we can define something called a tuple. A tuple is a set of values that are grouped together. For example, take a look at the return type of the function below:

func divide(_ lhs: Int, _ rhs: Int) -> (result: Int, remainder: Int) {
  return (lhs/rhs, lhs % rhs)
}

The code above returns a pair of values from a function called divide. Instead of defining a struct or class to hold the two values that we'd like to return we can return a tuple of values which can be quite convenient for cases like this.

Since a tuple's definition on its own doesn't always tell us much about what the tuple represents, we can use a typealias to give a name to this tuple to make its intent clear:

typealias DivisionResultAndRemainder = (result: Int, remainder: Int)

func divide(_ lhs: Int, _ rhs: Int) -> DivisionResultAndRemainder {
  return (lhs/rhs, lhs % rhs)
}

Users of this code now understand what the returned tuple is, and how they can use it.

An interesting side effect of using tuples and type aliases like this is that you could even swap out the DivisionResultAndRemainder tuple for a struct with the same properties without making any changes to callers of divide(_:_:) which is pretty neat.

5. Defining a protocol's associated type

The fifth and last tip is a more advanced one that is used a lot in the Swift source code. Sometimes a protocol has an associated type:

protocol Sequence {
  associatedtype Element
  associatedtype Iterator: IteratorProtocol where Iterator.Element == Element

  // more code...
}

Note:
If you're not familiar with associated types or if you want to learn more, I recommend that you read the following posts on this topic:

The Swift compiler is usually pretty good at inferring what the concrete types of these associated types should be. However, this can be rather confusing, especially if the associated type is used in more than one place. The most common use of type aliases in the Swift standard library is (by far) the explicit definition of associated types:

extension ClosedRange: Sequence where Bound: Strideable, Bound.Stride: SignedInteger {
  public typealias Element = Bound
  public typealias Iterator = IndexingIterator<ClosedRange<Bound>>
}

The code above is directly from the Swift standard library. It shows how ClosedRange conforms to Sequence under certain conditions, and in the extension body, the Element and Iterator associated types from Sequence are defined. What's really interesting is that all Sequence functionality can now be assigned to ClosedRange through extensions on Sequence rather than explicitly defining them on ClosedRange.

This a great example of how protocol-oriented programming works in Swift, and it's made possible by typealias! Pretty cool, right?

In Summary

In today's quick tip you learned about five different use cases for the typealias keyword in Swift. Some of these use cases can significantly improve your code's readability while others have a more minor impact. You even learned that typealias is used to power large parts of the Swift standard library and that without typealias, protocol-oriented programming probably wouldn't look like it does today.

If you have more cool uses of typealias to improve your code, if you have questions or feedback, don't hesitate to send me a Tweet.

Reversing an Array in Swift

You can reverse an Array, and any other Collection in Swift using the reverse method. For example

var input = [1, 2, 3]
print(input) // [1, 2, 3]
input.reverse()
print(input) // [3, 2, 1]

The code above takes an array (input) and reverses it in-place using the reverse() method. This only works if your array is mutable.

If you want to reverse an immutable array that's defined as let, or if you don't want to alter the original input you can use reversed() instead of reverse():

var input = [1, 2, 3]
print(input) // [1, 2, 3]
var reversed = input.reversed()
print(input) // [1, 2, 3]
print(reversed) // ReversedCollection<Array<Int>>(_base: [1, 2, 3])

The reversed() method returns an instance of ReversedCollection. It can be used much like a regular Collection and you can convert it to a regular Array as follows:

let reversedArray = Array(reversed)
print(reversedArray) // [3, 2, 1]

It's typically advised to avoid making this final conversion because ReversedCollection doesn't compute the reversed array unless absolutely needed. Instead, it can iterate over the input array from the end to the start which is more efficient than reversing the entire array all at once. So only convert the result of reversed() to an Array explicitly if you run into problems otherwise which, in my experience, is almost never.

Year in review: 2019

It's the end of the year, and that means that we should all take a little bit of time to reflect on the past year. Some people like to reflect on their past year in a very statistical manner, they set measurable goals and they decide whether they met their goals at the end of the year. Personally, I don't really set goals per year. I simply strive to do the best I can every year. In this post, I would like to reflect on how 2019 has been for me, and I will share some of my plans for 2020 with you.

Recapping 2019

Right of the bat, 2019 was an interesting year for me. In January, I first attended dotSwift in Paris which was an amazing experience. I went to the city a day early with my girlfriend, Dorien, and we caught a concert by one of my favorite bands of all time called Architects. Paris is a beautiful city and since the conference only lasts half a day, we had plenty of time to walk around the city. After the conference, I met up with Dorien and we ran into several other people who we ended up having an amazing dinner with.

Shortly after attending dotSwift I went to Romania to speak about designing frameworks that work on multiple platforms at MobOS. The conference was amazing and I got to meet several amazing people, including Benedikt Terchete, Dave Verwer, Paul Hudson and others. When the conference was over, we went to a salt mine which was a beautiful experience and tons of fun!

A couple of weeks after delivering my framework design talk at AppDevCon in my (almost) hometown Amsterdam. At the pre-conference dinner, I met Kaya Thomas, which was really cool.

A couple of days after I spoke at AppDevCon, I got on my first ever intercontinental plane ride to Tokyo because I was invited to speak at try! Swift in Tokyo where I finally met my friend Jon-Tait after we'd been hanging out online for years! I also met a new friend at try! Swift, Nic Laughter. And of course, I met many other wonderful people too, like Natasha, Mayuko and Kristina. If you ever want to visit a conference that will blow your mind, I can recommend try! Swift Tokyo. The city is beautiful and the conference is put together so well. It's truly awesome.

After returning from Tokyo, I went to New York for the first time in my life for a couple of days in April. I went there to work for a couple of days, so, unfortunately, I didn't see much of the city. And I left my wallet on the airplane when I arrived so that wasn't great. Luckily, I came back to the United States a month later for WWDC so I got a retry in experiencing what the US is like, with the added benefit of it being at WWDC.

If you ever get a chance to visit San Jose during WWDC week, I recommend you do. I had a ticket for the conference this year which made the experience even better but I'm sure I would've had an amazing time without a ticket too. There are so many amazing people in the area during the week of WWDC, and the parties and gatherings of people are so much fun. The best part is that AltConf is right next door, so you can even attend a conference while you're there. Speaking of AltConf, I snuck out of WWDC for a moment to attend a talk my friend Nic gave called The Hidden Potential of People with ASD. I really enjoyed and you should definitely check it out if you want to learn more about autism in the workplace.

About a month after returning from WWDC I was lucky enough to attend Swift Island which, according to the Hacking with Swift community awards was the best conference of the year this year! So I was truly lucky indeed. I had a great time there and if you're ever looking for a conference that has a vacation-vibe going on, make sure to go to Swift Island.

After Swift Island, I was a guest on the iPhreaks podcast where we discussed Core Data. After that, I didn't do much until September. In September I started blogging again and I wrote a couple of posts about testing. Coincidentally, I had the opportunity to speak about testing at MobiConf in Krakow, Poland in October. Going to Poland was tons of fun, especially when I found out my friend Antoine was speaking too! At MobiConf I met several old friends, but I also made new friends and wanted to mention Aleksandra, Vadim and John in particular. Aleksandra and Vadmin, it was awesome to spend a couple of hours exploring Krakow with you before I went to the airport. And John, thanks for showing me and the gang around in Krakow's food and cocktail scene. It was a blast!

Days after I got home from Modbiconf I was invited to speak at GDG Coimbra in Coimbra, Portugal. This was another fun conference where even though I was pretty much the only iOS developer, I met awesome people. I wanted to give a special shoutout to Ivan, your talk was super funny and it was fun sitting with you at the speaker's dinner. It was also a ton of fun to meet Eugenio and I met up with Fernando who I had first met at MobiConf. Small world, right?

In December I didn't do any speaking. Instead, I worked on a project called Advent of Swift where I published one article on this blog every day. The project was a ton of fun and it brought me some very cool side-effects. For example, I was a guest on both Contravariance and Swift by Sundell. And I was chosen as the winner of the Rising Star category in the Hacking with Swift community awards. To wrap up the month of December I launched the Advent of Swift bundle which contains all Advent of Swift articles and several sample projects. It's available on Gumroad for just $1,99.

Wow. Summing my year up like this really confirms what I have been saying all along. It's been a wild year! Not just for the iOS community with SwiftUI, Combine, Xcode 11 and iOS 13, also for me personally! And a lot of this wouldn't be possible without the iOS community, all the amazing conference organizers and the amazing speakers that travel the world to talk at all the conferences.

In addition to all these people, it's also not possible without you specifically. Yes, you, the person reading this. If you wouldn't visit my website and read my articles, the last three months of this year wouldn't have been as exciting as they were, and all I can do is thank you from the bottom of my heart.

Looking ahead: 2020

No year in review is complete without at least mentioning the year after. I already mentioned that I don't do goals for the new year. But I can tell you that I plan to speak at conferences again in 2020, and I have already submitted several papers to CFPs. I can also tell you that I'm going to continue to bring you content in 2020 and for the foreseeable future I'm going to keep going with my two posts a week schedule. I enjoy that schedule and I hope you do too. And of course, I'm already thinking about what kinds of cool extra projects I can do for my blog in 2020.

I hope to see you again in 2020, and I wish you all the best and a Happy New Year!