Building a Card Memory Game with Swift and SwiftUI
Published: 2020-08-10 13:26:00

---
title: Building a Card Memory Game with Swift and SwiftUI
published: true
description: 
tags: 
//cover_image: https://direct_url_to_image.jpg
---
> Post also published in: [DanMiranda.io](https://danmiranda.io/content/building-a-card-memory-game-in-swift)


I decided I wanted to start learning Swift and SwiftUI.

I'll use this [Stanford's course](https://cs193p.sites.stanford.edu/) to learn it, and I'll also document what I'm learning or building through the material.

I already have this [simple post about the MVVM architecture](https://danmiranda.io/content/mvvm-architecture) that it's used to build applications using Swift and SwiftUI.

## Creating the project

Open your Xcode and choose the **Create a new Xcode project.**

![XCode first step creating a project](https://dev-to-uploads.s3.amazonaws.com/i/98ag46044rxwbueut8x0.png)

Then **Single View App** cause we'll start with the basics barebones for a project. But you can see we have some interesting options to bootstrap an application with some pre-configured structure.

![Choosing single view](https://dev-to-uploads.s3.amazonaws.com/i/ly1clanxqt9v0p4apyx3.png)

Now setup some project's details, like name, choosing your team, organization name and so on. 

Just pay attention to the highlighted below. Make sure to choose **Swift** for language and **SwiftUI** for User interface.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/cd3mtppu77beupvdamdn.png)

After that, you'll be asked where you want to save your project and if you want to initiate the project with source control. (Git initiated)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/t5lfnmto5t7opms1fhju.png)


Now that we created our project, we can take a look at the file `ContentView.swift` .

```swift
import SwiftUI

struct ContentView: View {    
    var body: some View {
        Text("Hello world")
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```

We have the SwiftUI import at the very top of the file so we can use UI elements from SwiftUI such as `Text` like it's done in the example.

The block of code below is used to create the live preview shown in the Canvas (that's probably positioned at the right side of your code editor.

We won't change this code for now. Just leave it as it is.

```swift
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/bir39jin1i28kkv6fgy3.png)

This preview will only work if you're using OS Catalina or a newer version. If you still don't see the preview, just follow this:

Click where it's highlighted at the top right corner of your editor and then make sure the **Canvas** option is checked.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/2csg5b8tk7ogna8njjnt.png)

Before we start creating our elements, let's see a little bit about UI elements.

If you already know this and want to keep working on the project, you can [skip to this part](https://www.notion.so/Building-a-Card-Memory-Game-7fd60fb9797b4959bd645d86055844fc#207ba92b862e470ab52bd83610730c5c).

## Creating an UI Element

You can create an UI element by declaring a new `struct`:

```swift
struct CardView: View {}
```

Here we are creating a new struct and naming it as `CardView`. Next, by using `: View` we are declaring that our `CardView` struct will **behave** like a `View`

Note that when declaring a struct that will behave like a view, we must declare a `body` variable that we use to build our interface or the UI element.

```swift
var body: some View {}
```

Here we are declaring a variable called `body` as it is required and declaring its type which is `some View` .  

Now, what exactly is `some View` ?

When we type a variable as `some View` we are declaring that this variable will hold a value that's is of a View type, such as:

- Text
- HStack
- ZStack
- RoundedRectangle

All these elements mentioned above comply with the View protocol, which is why they are valid returns for something that's declared as holding a value of type of `some View`.

But why exactly do we specify the variable as of  `some View` instead of `Text` or `RoundedRectangle` for example.

Well, that can be done in case your body will only have one element of the specified type.

For example, we can declare a body that will hold a `Text`type, like this:

```swift
struct CustomText: View {
    var body: Text {
        Text("Hello my custom and amazing text element")
    }
}

// optionally, you can explictly write the return statement

struct CustomText: View {
    var body: Text {
        return Text("Hello my custom and amazing text element")
    }
}
```

But, bear in mind that usually some of your UI elements will consistent of a number of different other elements, like `Texts`, `RoundedRectangles` , `ZStack` and so on.

```swift
struct CustomText: View {
    var body: Text {
        ZStack {
            Text("Hello my custom and amazing text element")
        }
    }
}
```

If you try this,  Xcode will throw an error, stating: `Cannot convert return expression of type 'ZStack<Text>' to return type 'Text'`

So, in this case, you type your variable `body` as of type  `some View`. This is called `opaque types`

## Basic UI Elements (Building Blocks)

### ZStack

This element serves as an alignment element which will align all of its children in the Z-axis. Each of the elements will be placed on top of each other.

```swift
struct ZStackExample: View {
    var body: some View {
        ZStack {
            Text("Orange text")
            Text("Green text")
            Text("Blue text")
        }
    }
}
```

The very last element will be on top of all other preceding elements and so on.

![ZStack illustrated example](https://dev-to-uploads.s3.amazonaws.com/i/i3ye0x5h2fmd6yrsxx1b.png)


### HStack

An element used to align the children elements in the horizontal axis (x-axis)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/hmlfl2hquyadyiaz6mu4.png)


## Creating our CardView

After getting a grasp of View elements that will help us build our UIs, we can continue building our card game. 

For now, we will create something simple as a Card which will contain a string element. In our case we will use emojis for these string elements.

```swift
struct CardView: View {    
    var body: some View {
        ZStack {
          RoundedRectangle(cornerRadius: 10.0).fill().foregroundColor(Color.white)
          RoundedRectangle(cornerRadius: 10.0).stroke().foregroundColor(Color.orange)
          Text("👻")
        }
    }
}
```

We start by creating a new `Struct` called `CardView` that will behave like a `View`

```swift
struct CardView: View {}
```

Remember, when declaring a struct that will behave like a View we need to declare a variable called `body` which will hold a value that complies with the View protocol.

```swift
var body: some View {}
```

Now, to compose the card component, we'll need three layers. One that will hold a blank rectangle. The other which will hold a rectangle with an orange border and the final layer, the emoji which will be shown.

Since we need to place all of them on top of each other, we need the `ZStack` element.

So inside of the `body` variable we do:

```swift
ZStack {
	RoundedRectangle(cornerRadius: 10.0).fill().foregroundColor(Color.white)
	RoundedRectangle(cornerRadius: 10.0).stroke().foregroundColor(Color.orange)
	Text("👻")
}
```

So, we added two `RoundedRectangle` elements and the `Text` element.

One of the parameters that the `RoundedRectangle` accepts is the `cornerRadius` which we use to determine the border radius for that rectangle.

We can also add some modifiers to style the rectangle even more, like:

- `fill()` which will fill the whole element with a color. We can choose the color that will be used to fill by applying another modifier, called `foregroundColor` that receives one unlabelled parameters that will set the color. We access available colours by using the constant `Color` followed by a colour name. `Color.white`
- `stroke()` will just create a border stroke around the element and we can also choose its colour with the same `foregroundColor` modifier we used to set the fill color.

Wrapping all up we now have this:

```swift
struct CardView: View {    
    var body: some View {
        ZStack {
          RoundedRectangle(cornerRadius: 10.0).fill().foregroundColor(Color.white)
          RoundedRectangle(cornerRadius: 10.0).stroke().foregroundColor(Color.orange)
          Text("👻")
        }
    }
}
```

We may have something like this for now:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/3huh3teoipysmpupaz35.png)

It would be interesting to add some padding , to give more space between the card and the main screen.

We can do that by simply adding the `padding()` modifier to the `ZStack`.

```swift
ZStack {
  RoundedRectangle(cornerRadius: 10.0).fill().foregroundColor(Color.white)
  RoundedRectangle(cornerRadius: 10.0).stroke().foregroundColor(Color.orange)
  Text("👻")
}.padding(10) // ADD THIS
```

Let's now use the `HStack` element to place more than one card, all placed horizontally. 

In the main View of our app, the `ContentView` struct we start by placing the `HStack`

```swift
struct ContentView: View {
    var body: some View {
        HStack {
            AnotherCardView()
            AnotherCardView()
            AnotherCardView()
        }
    }
}
```

Just to see as an example, let's place three cards inside our `HStack.`

You now have something like this:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ciyq9qxx0f0huup43zcb.png)

As the number of displayed cards will vary in the future, let's implement a simple loop to render a number of cards, instead of having to manually place them in our code.

```swift
struct ContentView: View {
    var body: some View {
        HStack {
            ForEach(0..<4) {
                index in AnotherCardView()
            }
        }
    }
}
```

Now that we have the basics we can start building our **Model** and a **ModelView.**

## Building our Model

The model is the source of truth for all the data that runs across the app.

Let's start by creating a new Swift file, and just in this case (as it's a simple project) let's call it `Model.swift`.

Create a struct called `CardGame` that will have a generic type, or as called in Swift an opaque type.

```swift
struct CardGame<CardContent> {}
```

This opaque that we are calling `CardContent` is what will determine what type of content our card will hold. This content can be of string, number, or whatever, and in our case will be a string, since we'll be placing emojis inside the cards.

Now, what variables will we need to build this game?

Probably a variable that will hold the cards, that we can call `cards` and the number of pairs of cards that will be rendered in the screen.

```swift
struct CardGame<CardContent> {
	var cards: Array<Card>
	var numberOfPairs: Int
}
```

Take note that the `cards` variable will hold an Array that will contain a card type. But what exactly is this `Card`? Well, let's create it right now.

Create a new struct called `Card`.

```swift
struct Card: Identifiable {}
```

> The `Identifiable` protocol will be discussed later.

Now let's think about the Memory game itself and how it works.

We need to be able to make cards have their faces with their content facing "down" and once we choose one the card flips and the content is shown.

We also need to "match" cards once 2 cards with the same content are flipped up.

And finally, the card need to hold the content, in our case our emoji. So we need three variables for now:

- isFaceUp
- isMatched
- content

So:

```swift
struct Card: Identifiable {
	var isFaceUp: Bool = false
	var isMatched: Bool = false
	var content: ???

	var id: Int
}
```

Let's forget about the `id` variable for now, because we only declared it now since `Identifiable` protocol requires it. We will make sense for it later on.

Now, se that I "typed" content with `???`. That it is because I wanted to highlight that the `CardContent` opaque type we declared for our main struct (the `CardGame`) comes into play.

The `CardContent` type is what's going to tell what type of content our card holds. Be it a number or a string for example. So our variable `content` inside our `Card` struct will hold a value of this type:

```swift
var content: CardContent
```

Let's also make a simple method that we will further use to choose the clicked card.

```swift
func chooseCard(card: Card) {
  print("Chosen card \(card)")
}
```

For now let's just print the chosen card.

See the `\()` syntax inside the string? This is how we **interpolate** a variable value inside a string, in our case the `card` variable.

The only thing left now is to create an **initializer** to attribute values to our variables `cards` and `numberOfPairs`.

```swift
init(numberOfPairsOfCards: Int, contentFactory: (Int) -> CardContent) {
        cards = []
        numberOfPairs = numberOfPairsOfCards
        
        for pairIndex in 0..<numberOfPairsOfCards {
            let content = contentFactory(pairIndex)
            
            // append two cards (a pair) to the array of cards
            cards.append(Card(content: content, id: pairIndex * 2))
            cards.append(Card(content: content, id: pairIndex * 2+1))
        }

				cards.shuffle()
    }
```

What's happening here?

We set `cards` to be an empty array at the beginning and the `numberOfPairs` to be equal to the value passed in `numberOfParisOfCards` when initializing our struct.

```swift
cards = []
numberOfPairs = numberOfPairsOfCards
```

Now we loop over the `numberOfPairs` using the `for _ in` .

```swift
for pairIndex in 0..<numberOfPairsOfCards {
  let content = contentFactory(pairIndex)
  
  // append two cards (a pair) to the array of cards
  cards.append(Card(content: content, id: pairIndex * 2))
  cards.append(Card(content: content, id: pairIndex * 2+1))
}
```

`pairIndex` will hold the current value contained in the iteration which we will use to invoke our `contentFactory` function, that will return something of `CardContent` type that will put in the `content` variable we just declared.

We declared the `content` variable with `let` since this value will be a constant.

Having the `content` set, we can append to our `cards` variable a pair of cards.

```swift
  cards.append(Card(content: content, id: pairIndex * 2))
  cards.append(Card(content: content, id: pairIndex * 2+1))
```

## Building our ModelView

Now we'll start creating our `ModelView` which is the "entity" that will act as a middle man in the communication between the `Model` and the `View`.

We could build more than one `ModelView` depending on the types of Card games we want. As we want a memory game of cards that will have emojis "printed" on them, we'll create a ModelView called `EmojiMemoryGame`.

Start by creating a class:

```swift
class EmojiMemoryGame {}
```

But why a class and not a struct?

First, let's recall what a `ModelView` is essentially. It will act as a portal of communication between the Model and View. 

But suppose that as an application grows, the number of different views will also grow and probably the number of `Views` trying to communicate with the data contained in the `Model` will also grow.

In this case, we may have different `View` referencing to the same `ModelView`. 

Different from `Struct` , `Classes` leave in the heap, they have pointers pointing to their position in the memory.

So anytime a new `View` creates a new instance of a `ModelView` which was declared as `class` it is only pointing to a location in the memory.

If we declare our `ModelView` as a `Struct` each time a View needs to use the ModelView to communicate with the Model, it will do so by creating a new instance in the memory.

Now let's create the access (a "door") to our model:

```swift
class EmojiMemoryGame {
    private var model: CardGame<String>
}
```

We are making our `model` var as `private` to avoid that `Views` can directly control what's inside of model.

By declaring the reference to Model as private, we force ourselves to write the "intents" to modify the Model's data or write our own references inside our ModelView that will reference the values inside the Model.

> Remember: In MVVM pattern, the `View` is not supposed to directly access what's inside the `Model`. Every communication between `Model` and `View` must pass between the `ModelView`

At this point you might be seeing XCode throwing you an error saying that `Class 'EmojiMemoryGame' has no  initializers`.

Let's fix this:

 

```swift
class EmojiMemoryGame {
	private var model: CardGame<String> = EmojiMemoryGame.createMemoyGame()

	static func createMemoryGame() -> CardGame<String> {
        let emojis = ["👻", "🧟‍♂️", "🧙🏻‍♂️", "🎃", "🕸"]
        
        let randomNumberOfPairs = Int.random(in:2...5)
        
        
        return CardGame<String>(numberOfPairsOfCards: randomNumberOfPairs) {
            pairIndex in emojis[pairIndex]
        }
    }
}
```

What we are doing here is, first we created a static method called `createMemoyGame()` that will return a `CardGame` of `Strings`: `CardGame<String>`

Then we declare a fixed array of five emojis:

```swift
let emojis = ["👻", "🧟‍♂️", "🧙🏻‍♂️", "🎃", "🕸"]
```

And declare a random number of pairs of cards the game will have once it starts:

```swift
let randomNumberOfPairs = Int.random(in:2...5)
```

And in the end, we return a new instance of `CardGame`

```swift
return CardGame<String>(numberOfPairsOfCards: randomNumberOfPairs) {
  pairIndex in emojis[pairIndex]
}
```

But where is this syntax coming from?

Remember when e declared the initializer for our `Model`?

```swift
init(numberOfPairsOfCards: Int, contentFactory: (Int) -> CardContent) { }
```

Our `Model`'s initializer requires two parameters:

- `numberOfPairsOfCars` which is simply an `Int` telling how many pairs of cards the game will have
- `contentFactory` which is a `function` that will generate our card content.

Now let's look back at what our `createMemoryGame()` method is returning:

```swift
return CardGame<String>(numberOfPairsOfCards: randomNumberOfPairs) {
  pairIndex in emojis[pairIndex]
}
```

We are returning a new instance of the `Struct` `CardGame<String>` by passing the following arguments:

- `numberOfPairsOfCards` which will be the random generated number we assigned to `randomNumberOfPairs`

Now, where's the second required argument, `contentFactory`?

It's right here:

```swift
{ pairIndex in emojis[pairIndex] }
```

This is the syntax of a **[Swift closure](https://docs.swift.org/swift-book/LanguageGuide/Closures.html).** I won't explain what these are here because they deserve a post of its own. There's a reference to closure documentation at the end of this.

But for now, just keep in mind that:

`pairIndex` is the `Int` argument that our `contentFactory` argument requires 

```swift
(Int) -> CardContent
```

and that what is after the `in` keyword is what will be returned from this `closure` which is essentially an inline function.

As we declared that our `CardGame` will be of `String` type, like this: `CardGame<String>` we are returning one of the emojis from the array, based on the `pairIndex` argument.

```swift
emojis[pairIndex]
```

Now we just need to expose our `Model`'s variables, `cards` and `numberOfPairs` and create a simple intent to "choose" a card (for now it will only print a message in the console)

```swift
var cards: Array<CardGame<String>.Card> {
  model.cards
}
    
var pairs: Int {
    model.numberOfPairs
}
```

```swift
func choose(card: CardGame<String>.Card) {
    model.chooseCard(card: card)
}
```

At the end you should have something like this:

```swift
class EmojiMemoryGame {
    private var model: CardGame<String> = EmojiMemoryGame.createMemoryGame()
    
    static func createMemoryGame() -> CardGame<String> {
        let emojis = ["👻", "🧟‍♂️", "🧙🏻‍♂️", "🎃", "🕸"]
        
        let randomNumberOfPairs = Int.random(in:2...5)
        
        
        return CardGame<String>(numberOfPairsOfCards: randomNumberOfPairs) {
            pairIndex in emojis[pairIndex]
        }
    }
    
    // MARK: - Acces to model
    
    // this will expose cards from model to be used by the View (ContentView)
    var cards: Array<CardGame<String>.Card> {
        model.cards
    }
    
    var pairs: Int {
        model.numberOfPairs
    }
    
    // MARK: - Intent(s)
    
    // this will expose methods to be used by the View to interact with the Model's cards
    func choose(card: CardGame<String>.Card) {
        model.chooseCard(card: card)
    }
}
```

> Side note: You can add this decorated comments with `// MARK: - SOME_NAME` to create little sections in your code, which will help you navigate in your code when it starts to grow in lines of code.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/kccs8cfh912zc6ezrfl9.png)

Clicking in any of these will navigate to that block of code

## Adapting our View

Now that we have our `Model` and our `ViewModel` created, we can start integrating our `View`.

Inside our `ContentView` struct, now back at our `View` file (in our case it's called `ContentView.swift`, let's add a reference to our `ModelView`:

```swift
struct ContentView: View {
	var emojiGame: EmojiMemoryGame
}
```

We will be back here in a moment. For now let's go to our `CardView` struct.

Now we need to declare two variables to this struct.

```swift
var card: CardGame<String>.Card
var numberOfPairs: Int
```

We will add a few modifications to the current code of `CardView`. First is that we will evaluate if the card should be facing up or down. Second, for now, as everything is being aligned horizontally we need to choose a different font size in case the game has 5 pairs of cards.

```swift
var body: some View {
        ZStack {
            if card.isFaceUp {
                RoundedRectangle(cornerRadius: 10).foregroundColor(Color.white)
                RoundedRectangle(cornerRadius: 10).stroke().fill(Color.orange)
                Text(card.content).font(numberOfPairs == 5 ? Font.title : Font.largeTitle)
            } else {
                RoundedRectangle(cornerRadius: 10).foregroundColor(Color.orange)
            }
        }
    }
```

So, inside our `ZStack` we check if the `card.isFaceUp` is `true` or `false`. Depending on that we render different elements

We also changed this:

```swift
Text("👻")
```

To this:

```swift
Text(card.content)
```

We also added the `font` modifier to the `Text` element.

```swift
.font(numberOfPairs == 5 ? Font.title : Font.largeTitle)
```

As previously said, if the `numberOfPairs` is 5 we render if a lightly smaller font then when the game has 4 or less pairs.

Now let's go back to our `ContentView` struct and modify the current `body`.

```swift
var body: some View {
        HStack {
            ForEach(emojiGame.cards) { card in
                CardView(card: card, numberOfPairs: self.emojiGame.pairs).onTapGesture {
                    self.emojiGame.choose(card: card)
                    }.aspectRatio(0.66, contentMode: .fit)
            }
        }.padding(10)
    }
```

Now, before we continue, remember that `Identifiable` thing we declared in our `Card` struct inside our model?

```swift
struct Card: Identifiable {
        var isFaceUp: Bool = true
        var isMatched: Bool = false
        var content: CardContent
        
        var id: Int
    }
```

If we hadn't done this and we tried to iterate over the cards variable, as we just did, XCode would throw the following error:

`Generic parameter ID could not be inferred`.

That is when the `Identifiable` protocol comes into play. We need give a way for the iteration in `ForEach` to differentiate each card in the iteration, which requires a unique ID for each element.

So now, in each iteration over cards, we grab a new variable called `card` and return a new `CardView`, passing its required parameters, the card and the number of pairs.

We also added this modifier `aspectRatio` to better fit the cards horizontally, specially when we have 5 pairs (10 cards in total).

The last thing we did is, add this `onTapGesture` to our `CardView` so any time a user presses a card, we call our "intent" method `chooseCard`, passing the tapped card.

Now, before we rebuild our app to see the results, you may have noticed that in `ContentView_Previews` XCode is throwing an error, saying that we are missing a parameter for our `ContentView` and in fact we are.

Since we declared this `var emojiGame: EmojiMemoryGame` inside our `ContentView` struct we need to pass this argument:

```swift
ContentView(emojiGame: EmojiMemoryGame())
```

We still have on last thing. This same `ContentView` struct  also used in `SceneDelegate.swift`.

Inside `func scene` (probably in line 23) you have this declaration:

```swift
let contentView = ContentView()
```

Since now `ContentView` requires an `emojiGame` we just to do the same we just did in our `ContentView_Previews:`

```swift
let contentView = ContentView(emojiGame: EmojiMemoryGame())
```

## That's it

Now, you can build by clicking on the play button at the top left area of Xcode, and you may have something like this:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/96buvrn8r18kg4m355cx.png)

You may end up with a different number of cards since we randomize the number of pairs.

## What's next?

There's still a lot to go. We need to add the reactive part of the app, that will react to us choosing cards and flipping them over.


## References

- Struct (and Classes)

[Structures and Classes - The Swift Programming Language (Swift 5.3)](https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html)

- View

[Apple Developer Documentation](https://developer.apple.com/documentation/swiftui/view)

- HStack

[Apple Developer Documentation](https://developer.apple.com/documentation/swiftui/hstack)

- ZStack

[Apple Developer Documentation](https://developer.apple.com/documentation/swiftui/zstack)

- RoundedRectangle

[Apple Developer Documentation](https://developer.apple.com/documentation/swiftui/roundedrectangle)

- Text

[Apple Developer Documentation](https://developer.apple.com/documentation/swiftui/text)

- Opaque types

[Opaque Types - The Swift Programming Language (Swift 5.3)](https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html#:~:text=An%20opaque%20type%20refers%20to,that%20conforms%20to%20the%20protocol)

- Closures

[Closures - The Swift Programming Language (Swift 5.3)](https://docs.swift.org/swift-book/LanguageGuide/Closures.html)