Counting beans with ReactiveCocoa 4 and an NSButton - ios

I have the following:
Two interesting classes: a ViewController and a ViewModel
A button nsButtonMorePlease:NSButton in the view of ViewController
A text box nsTextView:NSTextView in the view as well
I want the following behavior:
When you launch the program, the "count" starts at 0 and is displayed in the text box nsTextView
When you press the button nsButtonMorePlease, the count is incremented by 1 and the updated count is reflected in the nsTextView
I would like to ensure:
I use ReactiveCocoa 4 (that's the point kind of)
The model class contains numberOfBeans: MutableProperty<Int> starting at 0
The design is purely functional or close to it - that is (if I understand the term), every link in the chain mapping the event of mouse click to the MutableProperty of numberOfBeans to responding to it in the text view, is all without side effects.
Here's what I have. Fair warning: this doesn't come close to working or compiling I believe. But I do feel like maybe I want to use one of combineLatest, collect, reduce, etc. Just lost on what to do specifically. I do feel like this makes something easy quite hard.
class CandyViewModel {
private let racPropertyBeansCount: MutableProperty<Int> = MutableProperty<Int>(0)
lazy var racActionIncrementBeansCount: Action<AnyObject?, Int, NoError> = {
return Action { _ in SignalProducer<Int, NoError>(value: 1)
}
}()
var racCocoaIncrementBeansAction: CocoaAction
init() {
racCocoaIncrementBeansAction = CocoaAction.init(racActionIncrementBeansCount, input: "")
// ???
var sig = racPropertyBeansCount.producer.combineLatestWith(racActionIncrementBeansCount.)
}
}
class CandyView: NSViewController {
#IBOutlet private var guiButtonMoreCandy: NSButton!
#IBOutlet private var guiTextViewCandyCt: NSTextView!
}

class CandyViewModel {
let racPropertyBeansCount = MutableProperty<Int>(0)
let racActionIncrementBeansCount = Action<(), Int, NoError>{ _ in SignalProducer(value: 1) }
init() {
// reduce the value from the action to the mutableproperty
racPropertyBeansCount <~ racActionIncrementBeansCount.values.reduce(racPropertyBeansCount.value) { $0 + $1 }
}
}
class CandyView: NSViewController {
// define outlets
let viewModel = CandyViewModel()
func bindObservers() {
// bind the Action to the button
guiButtonMoreCandy.addTarget(viewModel.racActionIncrementBeansCount.unsafeCocoaAction, action: CocoaAction.selector, forControlEvents: .TouchUpInside)
// observe the producer of the mutableproperty
viewModel.racPropertyBeansCount.producer.startWithNext {
self.guiTextViewCandyCt.text = "\($0)"
}
}
}

Related

Observables bind 2 textfields

So now in the app i'm currently developing I decided to refactor it by moving to the MVVM design pattern. And here it is where I got to know the famous "Observables".
I managed to understand how they work and the importance of their existence when using MVVM, I've read a couple of explanations on the different techniques for the implementation. By techniques I mean:
Observables (the one I'm currently using)
Event Bus / Notification Center
FRP Techinque (ReactiveCocoa / RxSwift)
I've declared my Bindable class like this:
import UIKit
class Bindable<T> {
var value: T? {
didSet {
observer?(value)
}
}
var observer: ((T?) -> ())?
func bind(observer: #escaping (T?) -> ()) {
self.observer = observer
}
}
What I wanted to do is to bind 2 UITextField's (that are inside one of my ViewController's) with the respective ViewModel. Inside my ViewController there are 2 textfields (emailInput - passwordInput) and a 'Log In' button, that I want it to be disabled unless both textfields aren't empty.
For that I've added both textfield's this target:
emailInput.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged)
passwordInput.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged)
Then:
/// Enable / Disable --> Log In button
#objc func textFieldDidChange(_ textField: UITextField) {
if (emailInput.text == "") || (passwordInput.text == "") {
logInButton.enableButton(false)
} else {
logInButton.enableButton(true)
}
}
But my question is... How could I implement this same thing inside my ViewModel??
And is it possible to do a two-way binding using my Bindable class?
(If more code is needed to solve this, just ask me to and I'll edit the question)
Observable is used to communicate changes from the view model to the view. There is no need for your view model to use the Observable pattern in order to respond to the updates in your text fields. You can provide a simple function setCredentials(email: String, password: String). In this function you can check if those values are empty and set var loginEnabled: Bindable<Bool>. Your view observes the loginEnabled and sets the login button state accordingly.
struct ViewModel {
var loginEnabled = Bindable<Bool>()
var email = ""
var password = ""
init() {
self.loginEnabled.value = false
}
func setCredentials(email: String, password: String) {
self.email = email
self.password = password
self.loginEnabled.value = !email.isEmpty && !password.isEmpty
}
}
Then in your view controller you have something like
var viewModel: ViewModel
override func viewDidLoad {
super.viewDidLoad()
self.viewModel.loginEnabled.bind { value in
self.logInButton.isEnabled = value ?? false
}
}
#objc func textFieldDidChange(_ textField: UITextField) {
self.viewModel.setCredentials(email: self.emailInput.text ?? "", password: self.passwordInput.text ?? "")
}

RxSwift subclassing best practices

I have a view model that's being used in two flows and has gotten to the stage where it should really be split out into a super class and two subclasses. However, I'm getting confused as the best way to go about performing some subclassing.
On creation of the view model, I pass in all the interactions that could happen from the view like so:
View
class SomeViewController: UIViewController {
#IBOutlet private weak var nextButton: UIButton!
private var presenter: SomeViewModel!
override func viewDidLoad() {
super.viewDidLoad()
presenter.configure(nextButtonTapped: nextButton.rx.tap.asDriver())
}
}
Then I can handle these actions within my view model like so:
ViewModel
class SomeViewModel {
private let normalFlow: Bool
private let diposeBag = DisposeBag()
init(normalFlow: Bool) {
self.normalFlow = normalFlow
}
func configure(nextButtonTapped: Driver<Void>) {
handle(nextButtonTapped: nextButtonTapped)
// call to any other input handlers here...
}
func handle(nextButtonTapped: Driver<Void>) {
nextButtonTapped.drive(onNext: { [unowned self] in
guard self.safetyCheckOnePasses(), safetyCheckTwoPasses() else {
return
}
if normalFlow {
// do some set of actions
} else {
// do another set of actions
}
}).disposed(by: disposeBag)
}
func safetyCheckOnePasses() -> Bool {
// perform some sanity check...
return true
}
func safetyCheckTwoPasses() -> Bool {
// perform another sanity check...
return true
}
}
I'm getting confused as to what the best way to override the handle(nextButtonTapped: Driver<Void>) is because I still want those sanity checks to happen at the start of the onNext for every subclass, but I want the body after that to be different for the different subclasses. What would be the best way to go about this without duplicating code?
Rx is part of the functional paradigm and as such, subclassing is not appropriate.
Move your safetyCheckOnePasses() and safetyCheckTwoPasses() functions out of the class (or at least make them static.) That way they can be reused without needing an instance.

iOS/Swift - What is the difference between Closure/Completion blocks and Delegates/functions?

I don't clear about these two, Nowadays the world is shifting to the closure types. But I'm not clearly understanding this. Can someone explain me with a real-time example?
So a real life example of both would be something like this:
protocol TestDelegateClassDelegate: class {
func iAmDone()
}
class TestDelegateClass {
weak var delegate: TestDelegateClassDelegate?
func doStuff() {
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
self.delegate?.iAmDone()
}
}
}
class TestClosureClass {
var completion: (() -> Void)?
func doStuff() {
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
self.completion?()
}
}
}
class ViewController: UIViewController, TestDelegateClassDelegate {
func iAmDone() {
print("TestDelegateClassDelegate is done")
}
override func viewDidLoad() {
super.viewDidLoad()
let testingDelegate = TestDelegateClass()
testingDelegate.delegate = self
testingDelegate.doStuff()
let testingClosure = TestClosureClass()
testingClosure.completion = {
print("TestClosureClass is done")
}
testingClosure.doStuff()
}
}
Here we have 2 classes TestDelegateClass and TestClosureClass. Each of them have a method doStuff which waits for 3 seconds and then reports back to whoever is listening where one uses delegate procedure and the other one uses closure procedure.
Although they do nothing but wait you can easily imagine that they for instance upload an image to server and notify when they are done. So for instance you might want to have an activity indicator running while uploading is in progress and stop it when done. It would look like so:
class ViewController: UIViewController, TestDelegateClassDelegate {
#IBOutlet private var activityIndicator: UIActivityIndicatorView?
func iAmDone() {
print("TestDelegateClassDelegate is done")
activityIndicator?.stopAnimating()
}
override func viewDidLoad() {
super.viewDidLoad()
activityIndicator?.startAnimating()
let testingDelegate = TestDelegateClass()
testingDelegate.delegate = self
testingDelegate.doStuff()
activityIndicator?.startAnimating()
let testingClosure = TestClosureClass()
testingClosure.completion = {
self.activityIndicator?.stopAnimating()
print("TestClosureClass is done")
}
testingClosure.doStuff()
}
}
Naturally you would only use one of the two procedures.
You can see there is a huge difference in code. To do a delegate procedure you need to create a protocol, in this case TestDelegateClassDelegate. A protocol is what defines the interface of a listener. And since an iAmDone method is defined it must be defined in ViewController as well as long as it is defined as TestDelegateClassDelegate. Otherwise it will not compile. So anything declared as TestDelegateClassDelegate will have that method and any class can call it. In our case we have weak var delegate: TestDelegateClassDelegate?. That is why we can call delegate?.iAmDone() without caring what delegate actually is. For instance we can create another class:
class SomeClass: TestDelegateClassDelegate {
func iAmDone() {
print("Something cool happened")
}
init() {
let testingDelegate = TestDelegateClass()
testingDelegate.delegate = self
testingDelegate.doStuff()
}
}
So a good example for instance is an UITableView that uses a delegate and dataSource (both are delegates, just properties are named differently). And table view will call the methods of whatever class you set to those properties without needing to know what that class is as long as it corresponds to the given protocols.
Same could be achieved with closures. A table view could have been defined using properties giving closures like:
tableView.onNumberOfRows { section in
return 4
}
But that would most likely lead into one big mess of a code. Also closures would in this case be giving many programmers headaches due to potential memory leaks. It is not that closures are less safe or anything, they just do a lot of code you can't see which may produce retain cycles. In this specific case a most likely leak would be:
tableView.onNumberOfRows { section in
return self.dataModel.count
}
and a fix to it is simply doing
tableView.onNumberOfRows { [weak self] section in
return self?.dataModel.count ?? 0
}
which now looks overcomplicated.
I will not go into depths of closures but in the end when you have repeated call to callbacks (like in case of table view) you will need a weak link either at delegate or in closure. But when the closure is called only once (like uploading an image) there is no need for a weak link in closures (in most but not all cases).
In retrospective use closures as much as possible but avoid or use caution as soon as a closure is used as a property (which is ironically the example I gave). But you would rather do just this:
func doSomethingWithClosure(_ completion: #escaping (() -> Void)) {
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
completion()
}
}
And use it as
doSomethingWithClosure {
self.activityIndicator?.stopAnimating()
print("TestClosureClass is done")
}
This has now removed all potential risks. I hope this clears a thing or two for you.
In Swift/obj-c the term delegate is used to refer to a protocol that responds to specific selectors.
Thing about it just like calling a method on an object.
E.g.
protocol CalculatorDelegate : class { // : class so it can be made 'weak'
func onCalculation(result: Int) -> Void
}
Now if we have a Calculator class, to use the delegate we'd do something like
class Calculator() {
weak var delegate: CalculatorDelegate?
func calculate(_ a: Int, _ b: Int) -> Int {
let result = a + b
self.delegate?.onCalculation(result: result)
return result
}
}
Then in some other class (e.g. in iOS - a View Controller) we might do:
class MyClass : CalculatorDelegate {
func onCalculation(result: Int) {
print("Delegate method on calculation called with result \(result)")
}
func someButtonPress() {
let calculator = Calculator()
calculator.delegate = self
calculator.calculate(42, 66)
}
}
So you can see how the setup is quite elaborate.
Now closures are simply blocks of code that can be called in other places, so you could change all of the code like so:
class Calculator2() {
weak var delegate: CalculatorDelegate?
func calculate(_ a: Int, _ b: Int, onCalculation: (#escaping (Int) -> Void) -> Int)?) {
let result = a + b
onCalculation?(result)
return result
}
}
class MyClass {
func someButtonPress() {
let calculator = Calculator2()
calculator.calculate(42, 66, onCalculation: { (result: Int) in
print("Closure invoked with \(result)")
})
}
}
However, with closures you need to be aware that it is a lot easier to shoot yourself in the foot by capturing variables (e.g. self) strongly, which will lead to memory leaks even under ARC regime.
Closures are first-class objects so that they can be nested and passed around
Simply,
In swift, functions are primitive data types like int, double or character that is why you can pass a function in the function parameter. In swift mechanism simplify in closures syntax like lambda expression in other languages.
E.g. If you want to call rest API through URSSession or Alamofire and return response data then you should use completionHandler(it's closure).
Void closure : - {(paramter:DataType)->Void}
Return closure : - {(paramter:DataType)->DataType}
e.g. (int, int) -> (int)
https://docs.swift.org/swift-book/LanguageGuide/Closures.html

Good strategy for replacing parts of functionality in iOS ViewControllers

I have VCs in an iOS app which have quite a lot of UI controls. I would now need to replace or "mock" some of these controls when in a specific state. In some cases this would be just disabling button actions, but in some cases the actions that happen need to be replaced with something completely different.
I don't really like the idea of having this sort of check littered all around the codebase.
if condition {
...Special/disabled functionality
} else {
...Normal functionality
}
In Android, I can just subclass each Fragment/Activity and build the functionality there, and then doing the if/else when inserting Fragments or launching activities.
But on iOS with Storyboards/IBActions and Segues, UIs and VCs are really tightly coupled. You either end up duplicating UI views or adding a lot of finicky code to already large VCs.
What would be the best way to handle this in iOS?
Sample code of what I want to avoid doing:
//Before:
class SomeViewController : UIViewController {
#IBAction onSomeButton() {
checkSomeState()
doANetworkRequest(() -> {
someCompletionHandler()
updatesTheUI()
}
updateTheUIWhileLoading()
}
#IBAction onSomeOtherButton() {
checkAnotherState()
updateUI()
}
}
//After:
class SomeViewController : UIViewController {
#IBAction onSomeButton() {
if specialState {
doSomethingSimpler()
} else {
checkSomeState()
doANetworkRequest(() -> {
someCompletionHandler()
updatesTheUI()
}
updateTheUIWhileLoading()
}
}
#IBAction onSomeOtherButton() {
if specialState {
return // Do nothing
} else {
checkAnotherState()
updateUI()
}
}
}
I'd suggest using the MVVM (Model - View - ViewModel) pattern. You pass the ViewModel to your controller and delegate all actions to it. You can also use it to style your views and decide if some of them should be hidden or disabled, etc.
Let's image a shopping app in which your pro users get a 10% discount and can use a free-shipping option.
protocol PaymentScreenViewModelProtocol {
var regularPriceString: String { get }
var discountedPriceString: String? { get }
var isFreeShippingAvailable: Bool { get }
func userSelectedFreeShipping()
func buy()
}
class StandardUserPaymentScreenViewModel: PaymentScreenViewModelProtocol {
let regularPriceString: String = "20"
let discountedPriceString: String? = nil
let isFreeShippingAvailable: Bool = false
func userSelectedFreeShipping() {
// standard users cannot use free shipping!
}
func buy() {
// process buying
}
}
class ProUserPaymentScreenViewModel: PaymentScreenViewModelProtocol {
let regularPriceString: String = "20"
let discountedPriceString: String? = "18"
let isFreeShippingAvailable: Bool = true
func userSelectedFreeShipping() {
// process selection of free shipping
}
func buy() {
// process buying
}
}
class PaymentViewController: UIViewController {
#IBOutlet weak var priceLabel: UILabel!
#IBOutlet weak var discountedPriceLabel: UILabel!
#IBOutlet weak var freeShippingButton: UIButton!
var viewModel: PaymentScreenViewModelProtocol
override func viewDidLoad() {
super.viewDidLoad()
priceLabel.text = viewModel.regularPriceString
discountedPriceLabel.text = viewModel.discountedPriceString
freeShippingButton.isHidden = !viewModel.isFreeShippingAvailable
}
#IBAction func userDidPressFreeShippingButton() {
viewModel.userSelectedFreeShipping()
}
#IBAction func userDidPressBuy() {
viewModel.buy()
}
}
This approach let's you decouple your logic from your views. It's also easier to test this logic.
One thing to consider and decide is the approach as to how to inject the view model into the view controller. I can see three possibilities :
Via init - you provide a custom initializer requiring to pass the view model. This will mean you won't be able to use segue's or storyboards (you will be able to use xibs). This will let your view model be non-optional.
Via property setting with default implementation - if you provide some form of default/empty implementation of your view model you could use it as a default value for it, and set the proper implementation later (for example in prepareForSegue). This enables you to use segues, storyboards and have the view model be non-optional (it just adds the overhead of having an extra empty implementation).
Via property setting without default implementation - this basically means that your view model will need to be an optional and you will have to check for it almost everytime you access it.

Computer AI turns in a UIViewController

In a swift game using UIKit I am writing, a human player will interact with UIKit UIButtons, GUI elements to take actions.
In the game, the player will play against AI players.
But here's the thing; the human player presses buttons and interacts and the AI player does not.
Given a simple UIViewController;
class SampleViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buyBtnPressed(_ sender: UIButton) {
print ("pressed")
}
}
So what I'm trying to ascertain is, how does the AI player itself take actions and handling turns within the context of the current view controller?
I believe the best way to do this is that there should be a loop that will wait until all players have completed their respective turns.
But where does this loop go? In the view did load?
If so, won't it eat up memory, or potentially lead (if not careful) to an endless loop?
I'm finding it hard to ascertain how an AI player can take actions within the given context of a UIViewController considering GUI elements are for human interaction.
I don't mean the AI should be animating pressing buttons or interacting with the screen, I mean; I have a UIViewController, it has a view did load; what is the strategy of implementing AI turns and whether or not this should be be achieved in a "game loop" in the View did load or can this be achieved in another way?
My question is; given the context of a UIViewController; how can I code the handling of an AI player taking turns and can this be achieved with a loop or another strategy?
Many thanks
edit: Code is now added
I have written out a turn base manager using Swift playgrounds, and 2 examples one using a UIViewController and another is a loop.
code now follows;
import Foundation
import GameplayKit
class Player {
var name: String
public private(set) var isAI: Bool = false
public private(set) var turnOrder: Int = 0
init(name: String, isAI: Bool?) {
self.name = name
if let hasAI = isAI {
self.isAI = hasAI
}
}
func setTurnOrderIndex(number: Int) {
self.turnOrder = number
}
}
let p1 = Player.init(name: "Bob", isAI: false)
let p2 = Player.init(name: "Alex", isAI: true)
protocol TurnOrderManagerDelegate: NSObjectProtocol {
func turnOrderWasSet()
}
protocol TurnDelegate: class {
func turnIsCompleted()
}
class Turn: NSObject {
weak var player: Player?
weak var delegate: TurnDelegate?
public private(set) var completed: Bool = false {
didSet {
delegate?.turnIsCompleted()
}
}
init(player:Player, delegate: TurnDelegate) {
self.player = player
self.delegate = delegate
}
func setAsComplete() {
self.completed = true
}
}
class TurnOrderManager: NSObject, TurnOrderManagerDelegate, TurnDelegate {
static var instance = TurnOrderManager()
public private(set) var turnOrderIndex: Int = 0
public private(set) var turnOrder: [Turn] = [Turn]() {
didSet {
self.turnOrderWasSet()
}
}
var playerOnTurn: Player? {
let turnObj = self.turnOrder[turnOrderIndex]
return (turnObj.player)
}
var allTurnsCompleted: Bool {
let filtered = turnOrder.filter { (turnObj:Turn) -> Bool in
return (turnObj.completed)
}.count
return (filtered == turnOrder.count)
}
func setTurnOrder(players:[Player]) {
if (self.turnOrder.count == 0) {
for playerObj in players {
let turnObj = Turn.init(player: playerObj, delegate: self)
self.turnOrder.append(turnObj)
}
}
}
func turnOrderWasSet() {
for (index, turnObj) in self.turnOrder.enumerated() {
turnObj.player?.setTurnOrderIndex(number: index)
}
}
func next() {
if (turnOrderIndex < (self.turnOrder.count - 1)) {
turnOrderIndex += 1
}
else {
turnOrderIndex = 0
}
}
internal func turnIsCompleted() {
print (" - turnIsCompleted")
TurnOrderManager.instance.next()
}
}
class GameModel {
var turnOrderManager: TurnOrderManager
init() {
self.turnOrderManager = TurnOrderManager.instance
self.turnOrderManager.setTurnOrder(players:[p1,p2])
}
// other game model stuff [...]
}
class Phase1State : GKState {
var gameModel: GameModel!
init(gameModel:GameModel) {
super.init()
self.gameModel = gameModel
}
override func isValidNextState(_ stateClass: AnyClass) -> Bool
{
return false
}
override func didEnter(from previousState: GKState?) {
}
override func willExit(to nextState: GKState) {
}
// MARK: - Action
func buy() {
let index = self.gameModel.turnOrderManager.turnOrderIndex
let turn = self.gameModel.turnOrderManager.turnOrder[index]
turn.setAsComplete()
}
}
class SomeViewController: UIViewController
{
var gameModel: GameModel?
weak var gamePhase: Phase1State?
var isPhaseComplete: Bool {
return self.gameModel?.turnOrderManager.allTurnsCompleted ?? false
}
override func viewDidLoad() {
super.viewDidLoad()
self.gameModel = GameModel.init()
self.gamePhase = Phase1State.init(gameModel: self.gameModel!)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func buyButtonPressed() {
self.gamePhase?.buy()
self.finishTurn()
}
func finishTurn() {
guard let turnIndex = self.gameModel?.turnOrderManager.turnOrderIndex else {
return
}
guard let turn = self.gameModel?.turnOrderManager.turnOrder[turnIndex] else {
return
}
turn.setAsComplete()
if (self.isPhaseComplete)
{
print ("All turns are completed")
}
else {
//self.gameModel?.turnOrderManager.next()
self.gamePhase?.buy()
guard let playerOnTurn = self.gameModel?.turnOrderManager.playerOnTurn else {
print ("No player is on turn")
return
}
print ("\(playerOnTurn.name) is on turn")
if (playerOnTurn.isAI)
{
self.gamePhase?.buy()
self.finishTurn()
}
}
}
}
// EXAMPLE 1 -- first attempt ...
let vc = SomeViewController()
vc.viewDidLoad()
vc.buyButtonPressed()
// EXAMPLE 2 -- another attempt ....
let gameModel: GameModel = GameModel.init()
let gamePhase = Phase1State.init(gameModel: gameModel)
// player then takes an action
while (gameModel.turnOrderManager.allTurnsCompleted == false)
{
let turnIndex = gameModel.turnOrderManager.turnOrderIndex
let turnObj = gameModel.turnOrderManager.turnOrder[turnIndex]
guard let playerOnTurn = turnObj.player else {
break
}
print ("Player \(playerOnTurn.name) is on turn")
gamePhase.buy()
}
print ("All turns are completed, advance to next phase")
The issue is;
On the finishTurn, it only seems to work if it relies on the first player in the index is a human player. If its not, I have no idea how to make it fire the buy action.
On the second example, I use a loop; but I'm concerned using a loop could end up just looping forever.
My query is therefore clarifyed, how can I ensure my view controller will fire actions for AI players when they don't press buttons and loop through each player and execute their respective turn.
Many thanks
Further edit:
I do not know if I should have the while (gameModel.turnOrderManager.allTurnsCompleted == false) loop inside my viewDidLoad() to act like a game loop.
There is no need to specifically use Sprite Kit for this. SpriteKit would be more to do with how the UI is made rather than how the logic of the game works.
However, I would recommend looking at GameplayKit. It's a framework that contains lots of built in game logic tools. Specifically you want something like the GKDecisionTree. There are a few WWDC videos about it too. GameplayKit can be used with SpriteKit, UIKit, SSceneKit or any other game engine that you decide to use (or not).
Also, the question you're asking is a very general question about game development. Having the computer "decide" to do something is quite a complex subject.
I'd also suggest having a quick watch of this video from AI & Games and other videos from that channel.
It'll give you an idea of how to approach your problem.
Session 609 and 608 from WWDC 2015 and 2016 are prob good :D
Regarding updating the AI.
Your AI should be event driven. You have the concept of "turns" and "players". There is a point in the game at which it becomes a "player's" "turn". (Even at the very beginning of the game it is either Player 1 or Player 2's turn.
At this time there are two possibilities. Either the player is an AI, or the player is a person.
As soon as this happens there should be some sort of trigger (like a function call or something) that tells the player its turn has started.
If that player is the AI then you need to start some sort of calculation (maybe with a built in delay to make it realistic) so that it decides what to do.
Look, I'm not sure on what kind of game you're making, buy you should probably learn SpriteKit, specially SKActions. With that, you can easily control the flow of events from your game.
With that said, how is your AI implementation? Based on your code, I would begin with something like this:
class AI {
enum Decision {
case doSomething
case doAnotherThing
case dontDoAnything
}
public func decide() -> Decision {
// Decide which action the AI will take...
return .doSomething // This return is just a example!
}
public func act(on : Decision) {
// Do whatever the AI needs based on a decision...
}
}
Then, in your ViewController:
class SampleViewController: UIViewController {
var ai = AI()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buyBtnPressed(_ sender: UIButton) {
print ("pressed")
ai.act(on: ai.decide())
}
}
I hope that helps!

Resources