Naked Networking with SwiftUI, gamification III

Ok, our game is getting along nicely and here is photo to prove it.

Photo of ying/yang game of ping/pong, aka whack!
Photo of ying/yang game of ping/pong, aka whack!

If you just stumbled across this article, than you need to read these ones first to get here.

Naked Networking with SwiftUI
More Naked Networking, more SwiftUI
Naked Networking and SwiftUI, the game plan
Naked Networking and SwiftUI, gamification
Naked Networking and SwiftUI, gamification II

What we need to do now is keep track of the score. We could do two simple labels, with a games won, games lost titles, but wouldn’t it be better to have a more visual indicator. Let’s put in row o’s or x’s to indicate how many games you played, how many you won and how many lost. We’ll do 7 games per round.

Once the round is over we need a means to reset things so we can start all over. But ok, getting ahead of myself.

SwiftUI

The main changes are in the ContentView.swift. We need an horizontal StackView and a list of indicators to say if we won [an o] or we lose [an x].

@State var game = 1
@State var game1 = ""
@State var game2 = ""
@State var game3 = ""
@State var game4 = ""
@State var game5 = ""
@State var game6 = ""
@State var game7 = ""
HStack {
Text(game1)
Text(game2)
Text(game3)
Text(game4)
Text(game5)
Text(game6)
Text(game7)
}

We need to use @State variables here cause we will be mutating the values within the ContentView.swift as we play the game.

I did try and build an array of these as I would in UIKit, but it seems it isn’t supported in SwiftUI at this point, so I compromised and created a function to set each of the games to the correct state as you play.

func winWin(game: Int, leader: String) {
switch self.game {
case 1:
self.game1 = leader
case 2:
self.game2 = leader
case 3:
self.game3 = leader
case 4:
self.game4 = leader
case 5:
self.game5 = leader
case 6:
self.game6 = leader
case 7:
self.game7 = leader
default:
break
}
self.game = self.game + 1
}

Each time you call it, it will set the next game in the series. Obviously you need to call this function on both clients. So we add it to the “You lose” section and “You win” one too. I am going put in the whole code block so that you can you double check you got everything we did so far in there.

....onReceive(pingPublisher) { ( data ) in
print("data ",data)
self.hit = false
self.disable = false
self.volley = ""
var countDown = Float(data)
if countDown! > 4 {
countDown = 4
}
self.youLose = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { timer in
countDown = countDown! - 0.1
self.timeToDie = String(countDown!)
if countDown! < 0.0 && !self.disable {
self.disable = true
self.youLose?.invalidate()
self.volley = "You Lose"
self.globalVariable.score = "lose"
self.winWin(game: self.game,leader: "x")
if self.model.state {
makeConnect(port: "1984", message: "win")
} else {
makeConnect(port: "4891", message: "win")
}
}
})
}
...

And for the winner.

... .onReceive(winPublisher, perform: {
self.hit = false
self.volley = "You Win"
self.disable = false
self.winWin(game: self.game,leader: "o")
self.globalVariable.score = ""
})
...

Add the sections, compile and have a go. It should work with the only draw back, nothing happens when you come to the end of a round, nor are you able to reset it. We need more. Can feel how these small projects get bigger and bigger.

Declaring who wins and who loses we can do in the winWin function. Add this code to end of it. New code in bold italic.

@State var remoteWin = 0
@State var localWin = 0
@State var localLose = 0
@State var remoteLose = 0
@State var newGame = false
....self.game = self.game + 1
if leader == "o" && !self.model.state {
remoteWin = remoteWin + 1
}
if leader == "o" && self.model.state {
localWin = localWin + 1
}
if leader == "x" && !self.model.state {
remoteLose = remoteLose + 1
}
if leader == "x" && self.model.state {
localLose = localLose + 1
}
if game == 7 {
newGame = true
if self.model.state {
self.volley = "game over yang wins " + " " + String(localWin) + " ying wins " + String(localLose)
} else {
self.volley = "game over ying wins " + " " + String(remoteWin) + " yang wins " + String(remoteLose)
}
}

But wait we need a reset. We need another protocol. Lets switch briefly back to network and add one.

Network

let gamePublisher = PassthroughSubject<Void, Never>()

We need to change our receive function too, it now has three distinct cases to deal with.

DispatchQueue.main.async {
globalVariable.score = backToString
if backToString == "ping" || backToString == "pong" {
pingPublisher.send(string2Send)
}
if backToString == "win" {
winPublisher.send()
}
if backToString == "game" {
gamePublisher.send()
}
}

I could have used a case statement here, if I end up adding any more I will. Back to SwiftUI.

SwiftUI

So we need another button. Add it under the the HStack you just put in place.

@State var newGame = false...Button(action: {
self.reset()
self.disable = false
self.remoteWin = 0
self.localWin = 0
self.volley = ""
self.newGame = false
self.game = 1
self.timeToDie = ""
self.globalVariable.score = ""
if self.model.state {
makeConnect(port: "1984", message: "game")
} else {
makeConnect(port: "4891", message: "game")
}
}) {
Text("new game").disabled(newGame ? false: true)
}.onReceive(gamePublisher) { (_) in
self.reset()
self.disable = false
self.remoteWin = 0
self.localWin = 0
self.volley = ""
self.newGame = false
self.game = 1
self.timeToDie = ""
self.globalVariable.score = ""
}

It is a bit of monster bit of code I confess. A notable bit within it, the fact that we disable or enable the button on the value of the newGame variable. I sneaked that bit of code in the previous section when you weren’t looking. You don’t want to reset a series in the middle of it, do you.

Ok, assuming you got everything in place and I haven’t left anything out, you should be able to play a game or two a even a few rounds.

Lets stop a moment take stock and review what we said we might do beyond this. We talked about more buttons, we talked animation. But as I come to the seventh article in the series I just realized we left that nasty hardcoded IP address in there. We need to fix that. This should be the subject of article seven.

Written by

Coding for 35+ years, enjoying using and learning Swift/iOS development. Writer @ Better Programming, @The StartUp, @Mac O’Clock, Level Up Coding & More

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store