Another day, another exercise. This time I chose the Game of Life. The rules for Game of Life can be found here. It was not a big project, so let's talk about the interesting bits, starting with the architecture. If you are interested in the sources, you can find them on GitHub.
Architecture: Model - View - Controller
There are three main classes. GOLBoard, the model, GOLView, the View and GOLViewController, the Controller.
The Game of Life starts with an initial randomly created matrix of cells. Each cell in that matrix can be in one of two states, alive or dead. So initially, one of those 2 states will be assigned to each cell on the board. Based on rules, the board evolves each round from one set of states to the next set of states. GOLBoard has everything to do just that. It contains the current board configuration and has methods to return a new board for the next round based on the game's rules.
Since the game plays itself automatically, there's is not much for the controller to do. Apart from initially asking the model for a configuration to start with, it has a timer that is fired every second. The timer handler that is then triggered, asks the model for a new board configuration, which will then be assigned to the view.
GOLView is a matrix of UIImageViews. Depending on the current cell state, the imageview shows either a filled green circle when the cell is alive or nothing when the cell is dead. When a new board is assigned, GOLView also animates the transition from one configuration to the next.
There are few things, I'd like to point out here. Like in my previous project, TicTacToe, the model doesn't mutate itself. Every round a new configuration is created based on the current one. So once a new model is created, it's constant. Having a constant model, means less side effects to deal with.
Furthermore, GOLBoard conforms to the Sequence protocol which conveniently allows to iterate over each cell in the matrix. The IteratorProtocol the Sequence is based on, returns the state of each cell including its position in X and Y coordinates. In TicTacToe a Sequence was just nice to have. In Game of life it made iterating the board easier and more elegant since the cell state was provided by the iterator as well.
Last but not least, the imageviews had to be managed in a way that allowed easy retrieval of the corresponding imageview for a given cell in the model. Since the board is accessed in X,Y coordinates, CGPoint has been extended to be hashable. Having hashable CGPoints allowed the use of a dictionary to manage all UIImageViews.
I wrote the initial project in Swift 2. When I converted it to Swift 3, I could take advantage of a few features introduced in the new version. Swift 3 allows for direct tuple comparison. So instead of awkwardly writing Tuple1.x == Tuple2.x and Tuple1.y == Tuple2.y it's now possible to just ask is Tuple1 == Tuple2? Furthermore, it always bothered me when overloading operators like '=' to keep the implementation separate from the actual class implementation. It's so much nicer to have everything now in the same class scope.
I said it once and i say it again, small projects provide us with an opportunity to play and learn. There are playgrounds and those are definitely a nice tool to run some quick tests. But working on an actual project has some definite advantages. Firstly, having a project allows working with classes that are difficult to handle in playgrounds e.g. Coredata. Secondly, being able to install your work on an actual device is just great, let alone being able to put your classes in dedicated files and folder for code separation purposes. Although latter is possible in playgrounds as well, it's just not the same since all those files are rather hidden away in specific folder in your playground project.