---
title: Combine + Diffable Data Source
teaser: 'Let''s explore two powerful native frameworks.

  '
tags: ios,swift,xcode,mobile
author: Abe Mangona
published_on: 2020-03-27
---

Combine and diffable data sources are two powerful yet often-overlooked
features that came out of WWDC 2019. Combine provides a declarative and
reactive approach to processing events, while diffable data sources make it
easier to display those events. Subscribing and reacting to events simplifies
handling state changes from data sources by reducing the code required and
making it easy to reason about. Using both of these frameworks in your app
gives you a certain level of superpowers.

In this article, we'll discover our powers by seeing how we could use both
through a game of blackjack. To make things more realistic, we are going to
be making a call to the Deck of Cards API. The app has a button which deals a
card from a deck. This triggers a download of a random card through the Deck
of Cards API.

![Animated gif demoing our blackjack app](https://images.thoughtbot.com/blog-vellum-image-uploads/pKUKMpLMQY6IIDUCw50F_blackjack_ios.gif)

Download the reference project on [GitHub](https://github.com/thoughtbot/blackjack-ios.git).

## Diffable Data Source

Datasources provide data to collectionviews and tableviews, which are
responsible for rendering each item. When data is changed the UI is updated
through `reloadData()` or `preformBatchUpdates(_:completion)`. Both of these
methods are a common source for bugs. `reloadData()` updates the entire UI,
providing bad user experience. `preformBatchUpdates(_:completion)` is useful
but easily gets complex. Diffable data sources now take care of a lot of these
issues through automatic diffing with `apply(_:animatingDifferences:)`. The
data source creates a snapshot of the current UI state which is compared to the
newly presented snapshot. It then diffs and updates the UI with a new snapshot
state.

Learn more about diffable data sources at [WWDC19 Advances in UI Data Sources](https://developer.apple.com/videos/play/wwdc2019/220/).

In our app, we will be setting up a diffable data source that works with a
`Card` object. Diffable data sources take generic types that must conform to
`Hashable`. Automatic diffing needs unique identifiers. If you're not familiar
with `Hashable`, [this article](https://nshipster.com/hashable/) explains it well.

> Card.swift

```swift
struct Card: Codable, Hashable {
    let identifier = UUID()
    let image: String
    let value: String
    let suit: String
    var points: Int {
        Int(value) ?? 10
    }

    private enum CodingKeys : String, CodingKey {
        case image, value, suit
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(identifier)
    }

    static func == (lhs: Card, rhs: Card) -> Bool {
        return lhs.identifier == rhs.identifier
    }
}
```

Typealiases are not required but can make code related to data sources and
snapshots more concise and readable. The `Section` parameter is an enum
that has default Hashable conformance. A snapshot of the data source is also
required to be given to the data source for diffing to occur through the
`DataSource.apply( _ snapshot:)` method.

> ViewController.swift

```swift
typealias DataSource = UICollectionViewDiffableDataSource<Section, Card>
typealias Snapshot = NSDiffableDataSourceSnapshot<Section, Card>
```

Setting up the data source requires only two custom methods.

> ViewController.swift

```swift
extension ViewController {

    func setUpDataSource() {
        dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView, cellProvider: { (collectionView, indexPath, card) -> UICollectionViewCell? in
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CardCell", for: indexPath) as! CardCell
            cell.imageView.sd_setImage(with: URL(string: card.image))
            return cell
        })
    }

    func updateHand(cards: [Card]) {
        var snapshot = Snapshot()
        snapshot.appendSections([.main])
        snapshot.appendItems(cards)
        dataSource.apply(snapshot, animatingDifferences: true)
    }

}
```

`setUpDataSource()` is a setup method that configures individual cells. If this
looks familiar, that's because it's something similar to what you would
normally do in the `cellForItemAt` and `cellForIRowAt` methods. A difference is
that you get the actual object in a closure instead of having to use something
like `ObjectsArray[indexPath.row]` to get a specific object. This makes things
less error-prone.

`updateHand(cards:)` is an update method that creates a snapshot instance and
appends sections and items. The snapshot is given to the data source to be
applied and that's where the diffing magic happens. If desired, the data
source can also determine the best animation to apply.

That's it! That's all that needs to be done to implement a CollectionView
diffable data source. TableView is almost identical with just different naming.

## Combine

Combine provides a declarative API to process values over time asynchronously.
The Combine framework has 3 main parts: _publishers_, _subscribers_, and
_operators_.

- A Publisher declares a type that can deliver a sequence of values over time.
- A Subscriber acts on the received elements from the publisher. The publisher
  only begins to emit elements when it has a least one subscriber.
- An Operator manipulates values emitted from upstream publishers.

Learn more on Combine at [WWDC19 Introducing Combine](https://developer.apple.com/videos/play/wwdc2019/722/).

Now that we have our UI and model set up, we need to add in the data and make API
calls. Traditionally we would use regular URL session code, but we want a more
declarative approach. Fortunately, Combine provides a few APIs to preform network
calls.

- URLSession
- JSON encoding and decoding of models that conform to Codable

Though we could write a Blackjack app that doesn't rely on an API, to
illustrate these concepts, we will rely on an API for dealing cards in our app.
We will make requests to the API to get the deck ID of a newly shuffled deck
and then deals with each new card from the deck.

> DealerService.swift

```swift
class DealerService {

    static func dealFromNewDeck() -> AnyPublisher<Deal, Error>{
        let url = URL(string: "https://deckofcardsapi.com/api/deck/new/shuffle/?deck_count=1")!
        return URLSession.shared
            .dataTaskPublisher(for: url)
            .map(\.data)
            .decode(type: Deal.self, decoder: JSONDecoder())
            .flatMap({
                return self.dealFromDeck(with: $0.deck_id)
            }).eraseToAnyPublisher()
    }

    static func dealFromDeck(with id: String) -> AnyPublisher<Deal, Error> {
        let url = URL(string: "https://deckofcardsapi.com/api/deck/\(id)/draw/?count=1")!
        return URLSession.shared.dataTaskPublisher(for: url)
            .mapError { $0 as Error }
            .map(\.data)
            .decode(type: Deal.self, decoder: JSONDecoder())
            .eraseToAnyPublisher()
    }
}
```

Both of these methods return an `AnyPublisher`, which is used to wrap a publisher
whose details are private to subscribers or other publishers.

`dealFromNewDeck()` is a `dataTaskPublisher` that maps and decodes data into an
intermediate `Deal` object. That object's `deck_id` property is then used in a
flatMap to return the `dealFromDeck(with:)` which returns a publisher. The
returned publisher exposes an instance of `AnyPublisher` to any downstream
subscriber instead of the publisher's actual type.

`dealFromDeck` is called when given a`deck_id` and directly returns a publisher
with a `Deal` intermediate value.

## Combine + Diffable Data Source

Now it's time to bring both together and witness the power. Like
most things these days you're gonna need a subscription:

> ViewController.swift

```swift
var subscription: AnyCancellable!
```

This subscription will subscribe to our publisher and react to its downstream
emissions.

> ViewController.swift

```swift
func deal() {

    let publisher = (hand.cards.isEmpty) ? DealerService.dealFromNewDeck() : DealerService.dealFromDeck(with: hand.deck_id!)

    subscription = publisher
        .sink(receiveCompletion: { completion in
            if case .failure(let err) = completion {
                print("Retrieving data failed with error \(err)")
            }
        }, receiveValue: { object in
            self.hand.deck_id = object.deck_id
            self.hand.cards.append(object.cards!.first!)
            self.updateHand(cards: self.hand.cards)
            self.updateTitles(cardsRemaining: object.remaining)
        })
}
```

The `publisher` is initialized base on whether any cards are in a hand. If
there are cards, a publisher is made from a deck with a given `deck_id`. If
there are no cards in a hand, a new publisher is made from a newly shuffled
deck with a new `deck_id`.

A subscription is made to the publisher and the sink subscriber is used
to respond to incoming events. Inside the received value closure, our hand
is assigned the current `deck_id` then is appended a card to its cards
property. With the new list of cards, the list is passed on to the
`updateHand(with:)` method to update the current snapshot and applied
to the data source. That's it! Your data source is now responsive to your
publisher's emissions.

`updateTitles(cardsRemaining:)` is a method that runs on the main thread to
update the UI.

Now, all that's left is calling `deal()` from an IBAction for the powers to come
to life. Make sure to empty the hand once you hit 21 to receive a new deck.

```swift
 @IBAction func deal(_ sender: Any) {
        if hand.score >= 21 {
            hand.cards.removeAll()
        }
        deal()
}
```

The reactive nature of combine and ease of the diffable data source makes for
a simple and powerful combination. You should consider adopting these two
frameworks in your current UIKit projects so you could potentially solve more
complex problems.
