Building a Real Time iOS multiplayer game with Swift and WebSockets (2)
Please note: this is part 2 of a 4-part tutorial. For part 1, look here.
Setup the shared library project
The shared library project will define three objects:
- A
Tiletype that contains the three valid states a tile can have:X,Oornone. - A
Playerobject that represents a player in the game. APlayerobject will only define anidfield and comparison methods. - A
Messageobject that is used for communication between the server and client regarding game state.
The Message, Player and Tile objects all conform to the Codable protocol, making it easy to encode to and decode from JSON.
The Tile Type
Code for the Tile type is really simple. We also define the strings to use when serializing to JSON or when decoding the JSON back into a Tile type.
import Foundation
public enum Tile: String, Codable {
case none = "-"
case x = "X"
case o = "O"
}
The Player Class
The Player class is slightly more complicated. It contains 2 initializers:
- An initializer to generate a new player with a random id.
- An initializer to generate a player from JSON data.
For generating a random identifier we use a UUID.
We want to be able to use a Player object as key in a dictionary for our server project. To support this, we implement the Hashable protocol.
Additionally we will assume a Player to be equal to another Player provided the identifiers are the same, so the comparison function is overridden as well. Two Player objects are assumed to be equal if the identifiers are the same.
The resulting Player class will then look as such:
import Foundation
public enum PlayerError: Error {
case creationFailed
}
public class Player: Hashable, Codable {
public let id: String
public init() {
self.id = NSUUID().uuidString
}
public init(json: [String: Any]) throws {
guard let id = json["id"] as? String else {
throw PlayerError.creationFailed
}
self.id = id
}
// MARK: - Hashable
public var hashValue: Int {
return self.id.hashValue
}
public static func == (lhs: Player, rhs: Player) -> Bool {
return lhs.id == rhs.id
}
}
The Message Class
Finally we implement the Message class.
We define the following communcation messages: join, turn, finish, and stop. A message might need additional data, for example a player object or the board state. In order to always create proper messages, we add a few factory methods:
- Whenever the game is stopped (e.g. a player left the game) we don't need to provide a board state or a player. The game will be aborted on the client.
- Whevener the game is finished we both need to know the current board state on the client and the winning player or none if the game is a draw. This way we can show an appropriate status message on the client.
- Whenever it's a player's turn to play, we need to provide both the current board state as well as the active player. The client can then allow the active player to play and prevent the other player from making a move.
- Whenever a player is joining the server, we need to provide the player, so the server can add this player to it's own internal list of players.
The resulting Message class looks as follows:
import Foundation
public enum MessageType: String, Codable {
case join = "join"
case turn = "turn"
case finish = "finish"
case stop = "stop"
}
public class Message: Codable {
public let type: MessageType
public let board: [Tile]?
public let player: Player?
private init(type: MessageType, board: [Tile]?, player: Player? = nil) {
self.type = type
self.board = board
self.player = player
}
public static func join(player: Player) -> Message {
return Message(type: .join, board: nil, player: player)
}
public static func stop() -> Message {
return Message(type: .stop, board: nil)
}
public static func turn(board: [Tile], player: Player) -> Message {
return Message(type: .turn, board: board, player: player)
}
public static func finish(board: [Tile], winningPlayer: Player?) -> Message {
return Message(type: .finish, board: board, player: winningPlayer)
}
}
Continue to part 3 of the tutorial.

Hey, great share…
I really liked this post as it is quite informative and useful. Thanks for sharing this post. Keep sharing more…
Cheers!!