Replace Conditional With Pattern-matching Refactoring
Tags: swift, ios, swiftui
Published: 2019-08-24 18:47:57

---
title: Replace Conditional With Pattern-matching Refactoring
published: true
cover_image: https://thepracticaldev.s3.amazonaws.com/i/u8sq9fvehdywgjszaurw.png
description: Use Pattern-matching and SwiftUI to build TicTacToe game
tags: swift, ios, swiftui
---

This is one of my favorite refactorings. It helps to group logic, making code easier to read and extend.

_I'm writing this blog post to use as a bookmark._

Very often, I see code which looks like the following:


```swift
func fizzbuzz(i: Int) -> String {
                
  if i % 3 != 0 {
    if i % 5 != 0 {
      return "\(i)"
    } else {
      return "Buzz"      
    }
  } else if i % 5 != 0 {
    return "Fizz"
  } else {
    return "FizzBuzz"
  }
}



for i in 1...100 {
  print(fizzbuzz(i))
}
```

It's quite a mental challenge to see all the possible states and predict what will hapen on given input number. Nested `if's` makes that part of the process even harder. 
We have to find a good way to represent the base-case and make all of the illegal cases impossible.





**Pattern matching** is technique in which we compare the values (or in our example: **tuple** values).., in a way that we could **deconstruct the complex data structure, and match it's internal values against pattern**: `SomeComplexDataStructure == (0, 0)  ?`. 





Notice that we can use `_` as wildcard symbol, 
`case(0, _)` will match all values where `i%3=0` and `i%5=anyvalue`

```swift
func fizzbuzz(i: Int) -> String {
  let someComplexDataStructure = (i % 3, i % 5) // Tuple complex data structure
                
  switch someComplexDataStructure {
    case (0, 0): "FizzBuzz"
    case (0, _): "Fizz"
    case (_, 0): "Buzz"
    case (_, _): "\(i)"
  }
}
```



### Lets up our game on another level, 

Let's go crazy and make a  **Tic Tac Toe** game, that stores all of it's internal state with **enum**'s and uses *pattern matching* to display each player moves on the board, find who is the winner,  ...



*Disclaimer: I'll use the  new SwiftUI syntax in to build the views (there is github download link bellow)*

![1](https://thepracticaldev.s3.amazonaws.com/i/r4dgck6frgekcqvy628x.png)

![2](https://thepracticaldev.s3.amazonaws.com/i/d1o6lii97z9b94zwq388.png)

![3](https://thepracticaldev.s3.amazonaws.com/i/azn49p6jqjq27v9lbppa.png)

![4](https://thepracticaldev.s3.amazonaws.com/i/u8sq9fvehdywgjszaurw.png)



Here there are the building block **types** of our game:

`Player` represents the two opponents in the game (`0` and `X`)
`Filed` represents the squares on the board: can be either empty or marked by one of the players
`GameState` represents which player turn is (`playing`), which player won the game or neither of the players have won (draw)

```swift
enum Player {
  case cross, circle
}

enum Field {
  case empty, marked(Player)
}

enum GameState {
  case playing(Player), winner(Player), draw
}
```

### SwiftUI

We are going to use SwiftUI `Views` to build the basic ui for our little game. 
SwiftUI is great way to quickly prototype interfaces, declaratively compose views and subviews... 

```swift
import SwiftUI
import PlaygroundSupport

struct App: View {
  var title: String
  var body: some View {
    VStack {
      Text(title).foregroundColor(.gray)
      Game()
    }
  }
}

PlaygroundPage.current.liveView = UIHostingController(rootView: App("TicTacToe"))
```

We have `App` component that contains the `Game` component that contains `Board > Board Row > Square`...

![lists](https://thepracticaldev.s3.amazonaws.com/i/8pvnvn1c92wd3gajuulw.png)

### SwiftUI @State binding

The most beneficial thing about SwiftUI is the realtime binding between state variable's  changes and redrawing/refreshing the component itself.

If we declare variable with `@State` modifier, every change that we make will **refresh** the subviews of our component:

```swift
struct Game: View {
  @State var board: [[Field]]
  @State var gameState: GameState
  
  mutating func restart() {
    self.board = ....
  }
  mutating func clickSquare(_ x: Int, y: Int) {
    self.board = updateBoard(board, gameState, id: "\(x) \(y)")
    self.gameState = updateGameState(board, gameState) 
  }
  
  var body: some View {
		Board(state, onRestart, clickSquare)
  }
}
```



### Board variable [[Field]]

The `board` variable is **2 dimentional matrix** with all the board squares placed by x and y axis coordinate: Every square have value of either `empty` or `marked` by some of the players (`X`,`O` )

`let y = 0, x = 1`

`board[y][x] = .marked(.cross)`

![matrix](https://thepracticaldev.s3.amazonaws.com/i/i1l6xyh4t37dv7dcmefv.png)

When we click on a square, a function will set the `board[y][x] = .marked(.cross)` , then SwiftUI will **refresh** the views and show `x` under the square we clicked (because `board` is @State variable )

### Let's go back to pattern matching!

But how we know if click on `board` in which square exatly to put `X` or should we put `O` alternatively?

I'm going to remind us the rules of  TicTacToe:



> We can place mark one of the remaing empty squares on the board
>
> We can draw `O` or `X` inside it (depending on who's player turn is)



The `updateBoard` function does this calculation and changes the `board` variable's field on x, y coordinate.



```swift
func updateBoard(_ board: [[Field]], _ gameState: GameState, id: String) -> [[Field]] {
  return board.enumerated().map { (indy, row) in
    return row.enumerated().map { (indx, field) in
      if "\(indx) \(indy)" != id { return field }	
                                 
			//if the board current field == clicked square field
                                 
      switch (gamestate, field) {
        //if square is alredy marked -> return same field 
        (.playing(_), .marked(_)) => return field,
				//if the square is empty -> modify it -> as marked by X or O player
        (.playing(let player), .empty) => return .marked(player),
        //otherwise in every other case, like when game is won, no started.. square is empty
        (_, _) => return .empty
      }
    }
  }
```


Excelent the pattern matching will handle all of the **other default cases** for us for free!



### Calculate the game winner, or next-turn player

In the next part we will calculate if the game is finished, who is the winner, or who's the next player turn is.

I'll break the `updateGameState` function in tree parts: 



1. Get the player for the next-turn

2. Flatten the board 2dimensional matrix to 1dimentional array `[[x0y0, x1y0, x2y0] [x0y1, x1y1, x2y1]]` becomes `(x0y0, x1y0, x2y0, x0y1, x1y1, x2y1)` 

3. Find if the board contains 3 marked fields in row (horizontal, vertical, diagonal), and if so - change the `gameState` to `.winner`

   

We're placing mark on square on the board (either `X` or `O`), then we calculate - do we have any series of 3 squares in row, and if we have 3 squares in row  `gameState = winner`
If we didn't finished playing the game (`gameState != winner` `gameState != draw`   we don't have winner but it's not draw either) , 
we will continue playing and `gameState = .playing(the other player)`

```swift
func updateGameState(_ board: [[Field]], _ state: GameState) -> GameState {
  // 1. 
  func nextPlayer(state: GameState) -> GameStates {
    switch state {
      case .plaing(.cross): return .plaing(.circle)
      case .plaing(.circle): return .plaing(.cross)      
      default: return state
    }
  }
	
  // 2.
  let b = board.flatmap { $0 }
  let flatBoard = (b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9])
  
  // 3.
	switch flatBoard {
    //horizontal lines
    case (.marked(.X), .marked(.X), .marked(.X), _, _, _, _, _, _): return .winner(.cross)
    case (_, _, _, .marked(.X), .marked(.X), .marked(.X), _, _, _): return .winner(.cross)
    case (_, _, _, _, _, _, .marked(.X), .marked(.X), .marked(.X)): return .winner(.cross)
		case (.marked(.O), .marked(.O), .marked(.O), _, _, _, _, _, _): return .winner(.circle)
    case (_, _, _, .marked(.O), .marked(.O), .marked(.O), _, _, _): return .winner(.circle)
    case (_, _, _, _, _, _, .marked(.O), .marked(.O), .marked(.O)): return .winner(.circle)
    //vertical lines
    case (.marked(.X), _, _, .marked(.X), _, _, .marked(.X), _, _): return .winner(.cross)
    case (_, .marked(.X), _, _, .marked(.X), _, _, .marked(.X), _): return .winner(.cross)
    case (_, _, .marked(.X), _, _, .marked(.X), _, _, .marked(.X)): return .winner(.cross)
    case (.marked(.O), _, _, .marked(.O), _, _, .marked(.O), _, _): return .winner(.circle)
    case (_, .marked(.O), _, _, .marked(.O), _, _, .marked(.O), _): return .winner(.circle)
    case (_, _, .marked(.O), _, _, .marked(.O), _, _, .marked(.O)): return .winner(.circle)    		
    // diagonal lines
    case (.marked(.X), _, _, _, .marked(.X), _, _, _, .marked(.X)): return .winner(.cross)
    case (_, _, .marked(.X), _, .marked(.X), _, .marked(.X), _, _): return .winner(.cross)
    case (.marked(.O), _, _, _, .marked(.O), _, _, _, .marked(.O)): return .winner(.circle)
    case (_, _, .marked(.O), _, .marked(.O), _, .marked(.O), _, _): return .winner(.circle)
    // default -> all fields filled -> draw
    case (.marked(_), .marked(_), .marked(_), .marked(_), .marked(.O), .marked(_), .marked(_), .marked(_), .marked(_)): return .draw
    // still unfilled fields -> switch to other player
    default: return nextPlayer(state)
  }
}
```

In effect these 2 functions (`updateGameState`, `updateBoard`) handle the all of the possible business logic cases for the game: under 100-lines of code. 



### Conclusion

This is simple and natural refactoring.

Use this refactoring when you see data being mapped to other data or behavior.

One thing to take as lesson from here: Using **data-driven**  in stead of event driven (`if/else`) architecture is almost always better. 

Not every `switch` case is the same. Some times you can't easily convert a nested `ifs` to  `switch`, take for example the  `updateGameState` function -> tuple with 9 or more values is difficult to write and maintain.