This idea stemmed from the fact that majority of developers create an UI in Interface Builder without an issue, but the real struggle comes when they have to write the code and it was difficult for me to believe at first that writing code or structure is really difficult, so I decided to write these articles to demonstrate how one can write simple and yet clever code with Swift
Game – The Idea
For an easy start, let us make a simple word guessing game that will display a random word and display some options on the screen for the user to choose from. If the correct option is chosen, then the score is incremented and when all the words are exhausted, the game ends. I had started work on something like this in 2011 following Farmer Fred’s Animal Farm but then other commercial projects and work got in the way.
Structure to hold it all
The first thing we need is a structure, for this particular game, we shall try to use an enum. Many of you might go “WHAT!! an enum??” “Don’t we use a class or a structure?” That is the beauty of enums, a Swift enum is not a simple hashValue. It can have properties and methods, the only thing it cannot do is have variables or the technically correct term, “It cannot contain stored properties”. So, let’s get cracking and see how to get that to work.
Update: This code is now updated for the latest version of Swift at the end of the article here
Declaration of the Enum
enum WordGame { case frog case dog case cat case mouse case cow case chicken }
NOTE: This enum above can also be declared in a shorter way as
enum WordGame { case frog, dog, cat, mouse, cow, chicken }
but I have used the longer fashion for a reason (and a habit) that will become apparent soon.
Functions to use the enum
first, let’s return an array that contains all of the items
extension WordGame { static var allItems:[WordGame] = { return [.frog, .dog, .cat, .mouse, .cow, .chicken] } }
NOTE: In the newer version of Swift(4.2+), this is managed automatically using the CaseIterable protocol and provides the allCases like we use the allItems in this case.
NOTE: We use the keyword static because this property is available on the Enum not an instance of the enum value.
How would we use it
let allItems = WordGame.allItems let frog = WordGame.frog print(frog)
Let’s get a random word
Let’s get a random word, for that we use the function arc4random or arc4random_uniform to get a number between 0 and the number of items in the list. Then return the word at that position.
static var randomWord: WordGame { let _all = WordGame.allItems let index = Int(arc4random_uniform(UInt32(_all.count)) return _all[index] }
and we can call it using print(WordGame.randomWord)
NOTE: In swift 4.2+, there is a new function called randomElement() that returns a random element from the array or a range and saves us from getting a value from arc4random and casting it from Int32 to Int .
Extending it further
If we ran this random word function a couple of times, we would get some repeats. Secondly, what if we wanted to extend this to have a graphic representation from the enums?
We shall extend our enums to add emoji to it and add some graphics instead of a simple textual representation. We make it of type String instead so that we can add an Emoji to it.
enum WordGameEmoji:String { case frog = "🐸" case dog = "🐶" case cat = "🐱" case mouse = "🐭" case cow = "🐮" case chicken = "🐔" } extension WordGameEmoji { static var allItems: [WordGameEmoji] { return [.frog, .dog, .cat, .mouse, .cow, .chicken] } }
and then
let _alItemsEmoji = WordGameEmoji.allItems let frogEmoji = WordGameEmoji.frog print(frogEmoji) print(fromEmoji.rawValue)
NOTE: The rawValue displayed the Emoji.
Creating the emoji from a word
It is all fine and the flow from the enum value to the emoji, however there can be a scenario where we might want to create an enum value from the textual representation. For a moment, let us assume that the values were text instead of the emoji, i.e. case frog = “frog”
enums offer functionality to create an enum value from the rawValue so the code would look something like
let _frog = WordGameEmoji(rawValue: "frog") print(_frog)
we see that _frog is an Optional WordGameEmoji value. This returns a nil if the rawValue provided does not create an enum value.
Coming back to this case where we have an Emoji or say we had the name of an image it would be a bit difficult to manage this. The way would be to create a dictionary that contains the textual representation and then from that textual representation we can return the enum value.
static var itemsDictionary: [String: WordGameEmoji] { return [ "frog" : .frog, "dog" : .dog, "cat" : .cat, "mouse" : .mouse, "cow" : .cow, "chicken": .chicken, ] } static var getEmoji(with: String) -> WordGameEmoji? { if let _obj = WordGameEmoji.itemsDictionary[with] { return _obj } return nil }
and we can now get an emoji value from the textual representation as,
let mouse = WordGameEmoji(with: "mouse")
However this mouse value is an optional value and you might require to use a if let or a guard to unwrap the value safely.
Connecting the UI methods
The basics are all good and we have a couple of functions that provide a random value, allow us to create a value from text. Let us add a couple of more properties/functions that provide richer functionality
static var keys:[String] { return itemsDictionary.keys.map{String($0)} }
and say we want to connect the emoji to a UILabel on the screen, we can simply do that by passing the UILabel to a function on the emoji value as,
func updateUI(label: UILabel) { label.text = self.rawValue ?? "" }
There we have it, a couple of functions and properties on an enum and we have enough for the game logic that we started with. Now what remains is creating a storyboard UI.
That would be for another article. However in theory it is about simply creating a game class that uses the WordGameEmoji enum stucture and functions. One could also create a structure for player that could store the player name, games played, score, highscore, averageScore etc.
Updated Version for Swift 5.3
enum WordGame: String, CaseIterable { case frog = "🐸" case dog = "🐶" case cat = "🐱" case mouse = "🐭" case cow = "🐮" case chicken = "🐔" // Return a random element static var random: Self? { return self.allCases.randomElement() } var emoji: String? { return self.rawValue } var name: String { return String(describing: self).capitalized } init?(with name: String) { for obj in WordGame.allCases where name == "\(obj)" { self = obj return } return nil } }
extension WordGame { func updateUI(label: UILabel) { label.text = self.emoji ?? "" } }
Now we can simply use this to get a random emoji by simply invoking
let emoji = WordGame.random
and
let mouseEmoji = WordGame(with: "mouse")
and this has all the properties like emoji to get the emoji of the enum, the name of the case as a string and also create a new enum by name.