Today we will try to build a TicTacToe game in less than 160 lines with SwiftUI.
SwiftUI is Apple’s new declarative framework that promises to supplant its brother UIKit in the near future.
Let’s open Xcode 11.
In these hours the beta2 came out together with macOS catalina beta2. I strongly advise you to install both of them, since we have noticed an overall remarkable improvement in performance as well as an improved Xcode responsiveness in all respects.
Once opened Xcode, we will create a new project, let’s name it TicTacToe and make sure that the tick in SwiftUI is enabled. Press next and choose a folder to store your new project.
Once the initialization phase is complete, Xcode shows the skeleton for the pre-generated project. Move to ContentView.swift and let’s start immediately shaping the logic of our game.
In the TicTacToe game, the two opponents challenge each other, each taking an X or O mark, in a 3x3 board. The winner is the one who is the first to line up three identical signs horizontally, vertically or diagonally.
The first thing to do, therefore, will be to create a model that reflects what has just been said.
Let’s write an enum that reflects the status of a box. This may be empty, occupied by us or by the computer.
To play we will need a 3x3 chessboard. So, try to define the model for a single square in the board:
The Square class adopts the BindableObject protocol. Objects that adopt this protocol are objects that notify subscribers in the SwiftUI framework when one or more properties in it change.
To do this it is necessary to adopt the protocol correctly, adding an instance that publishes an event when an object changes. This instance is called didChange:
PassthroughtSubject is a class made available by the Combine framework, which allows us to publish a notification to all applicants. The notification has two parameters, the output and a possible error.
For now, all that we want is to notify all applicants that something has changed into the Square object. So, since we are not interested in any of the two parameters, we will pass Void for the first parameter and Never for the second. The first indicates that we do not pass any output. The second indicates that there is no possibility of error.
Next, we define the property status of our box and, inside it, we’ll add a mechanism that sends a notification when we change its value:
We can move, next, on to the actual Model that define the game and the chessboard. We will call it ModelBoard:
We start with the declaration of the array variable that will host the individual pieces of the board. In the init function we are going to add 9 elements of type Square all with status .empty
At the end of the game we will need a method to bring the game back to its initial state:
Now we need to have a way to check if the game is over.
A game ends if there are no more boxes available or if one of the two players align three symbols before the other.
The getter variable thereIsAWinner will simply check all winning occurrences:
The checkIndex function takes an array of integers (indexes) as input and returns an optional SquareStatus: .home or .visitor if there’s a winner; nil otherwise:
We need a way to make a move. For our human player we will check if the selected square is empty. If yes, then we will change its status to .home
Since, we’re only implementing a Human vs Machine game, we also need to trigger the computer move at the end.
The move of artificial intelligence is really simple: we only collect a random index, check if it is empty and if this’s the case we will fill it with the .visitor flag.
Now that the model is ready, we need to interact with SwiftUI in order to build a View that could respect the Square Model. Let’s call it SquareView:
First of all, we add an @ObjectBinding property wrapper in front of the Square datasource.
This way we’re telling to SwiftUI that the SquareView is interested in changing notifications from the model. When a change will come, SwiftUI will automatically trigger a new body for the SquareView.
Inside the body we’re conditionally check if there’s a status other than .empty. If so, we write X or O accordingly.
.frame, .background and .padding are all cosmetic adjustments related to the Square.
Finally, we need to modify our ContentView in order to house the chessBoard by creating a private var for that purpouse, naming it checker:
We also define a @State property for checking if the game is finished; we need to define it as a @State property, since we would like to refresh the ContentView body once the game is completed.
Since every SquareView is the same and it’s behaviour change only based on index, we define also a buttonAction method:
This way, every SquareView will invoke the method with its specific index and the relevant code is executed with respect to the specified position.
Finally, we can build the whole body, reflecting the 9x9 SquareView(s):
One last thing, is regarding a feedback for the user: we want to notify the user with a popup when either the game is end or someone will win.
To make this, we could use the .presentation View modifier. In it’s simple form it takes:
- A conditional boolean to discern if it should appear or not
- A message parameter with a Text for the body of the alert
- A dismissButton: with an Alert.Button
That’s all folks! As promised, in less than 160 lines of code we have a fully working TicTacToe game. If we Build and Run our code our application will looks like the following:
You can download the entire project, available on GitHub at the following:
A simple SwiftUI implementation for TicTacToe game in less than 160 lines - valvoline/SwiftUI-TicTacToe
If you are interested in more SwiftUI tutorials, articles and tips feels free to check our page at: