Running! from VCVCVC - ios

I have been trying for the last 18 months taking all my code out of the ViewController to make it more like MVC should. ( Hence the title )
I've made a small step on my own by being able to do the following within one Class object, but now I want to break it down further
A very generic example: one file contains the data
struct Data {
var x = 0
var y = 0
}
and one file for the operation
class Adder {
var myObject = MyClass()
var z = 1
func addThem() {
z = myObject.x + myObject.y
}
}
now for the salient parts of the ViewController:
var data = Data()
var adder = Adder()
#IBAction func buttonPressed(sender: UIButton) {
// user input via textfield
data.x = Int(numeralOne.text!)!
data.y = Int(numeralTwo.text!)!
adder.addThem() // *
answerLabel.text = String(adder.z)
}
Ultimately I'd like to omit the line commented with the asterisk. I thought OO's encapsulation of (data) away from (adder) allows for adder.z to just automatically update in the background without involving the ViewController. That way a subtractor class (say) can operate on the same two struct properties.
My question? How can correctly referencing them from the VC.
PS. if i include return statements in the function it makes no difference.

It's not clear from your code exactly what the relationship is between the Data struct and MyClass but if a MyClass object has the values of x & y that you need then the following will work
class MyClass {
var x = 1
var y = 2
}
class Adder {
var myObject = MyClass()
var z: Int {
return myObject.x + myObject.y
}
}
let adder = Adder()
print(adder.z)

Related

How to change property value with static method?

In this simple game there is a class Fighter whose purpose is to make two fighters fight. The one who looses health below 0, it looses the game.
In order to fight there is a static method fight (..) which iterates till one fighter wins the game, supported by another non static method attack (..)
object Fighter health should change as two objects fight during the game using the methods fight(...) and attack (...). The problem is it always prints the same Fighter health, and the game never ends. I don´t see where the issue is
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let david = Fighter(name: "David", health: 100, damage: 30, defense: 10, initiative: 80)
let goliath = Fighter(name: "Goliath", health: 300, damage: 60, defense: 14, initiative: 90)
let myFight1 = Fighter.fight(fighter1: david, fighter2: goliath) // always executing same Fighters
print(myFight1)
}
}
import Foundation
struct Fighter {
var name: String
var health: Double
var damage: Int
var defense: Int
var initiative: Int
init (name: String, health: Double, damage: Int, defense: Int, initiative: Int) {
self.name = name
self.health = health
self.damage = damage
self.defense = defense
self.initiative = initiative
}
init (name: String, health: Double, damage: Int, defense: Int) {
self.name = name
self.health = health
self.damage = damage
self.defense = defense
self.initiative = 0
}
static func fight(fighter1: Fighter, fighter2: Fighter) -> Fighter {
let f1 = fighter1
let f2 = fighter2
if f1.health == f2.health {
return f1
}
if f2.initiative > f1.initiative {
f2.attack(f: f1)
}
var i = 0
while f1.health > 0 {
i += 1
print("--> i: \(i)")
f1.attack(f: f2 )
if f2.health <= 0 {
return f1
}
f2.attack(f: f1)
}
return f2
}
func attack(f: Fighter) -> Void {
var g = f
g.health = g.health - Double(g.damage * (1 - g.defense / 100))
print(g)
}
}
You are using a struct for Fighter which is a value type in Swift.
The most basic distinguishing feature of a value type is that copying — the effect of assignment, initialization, and argument passing —
creates an independent instance with its own unique copy of its data
Solution: Change Fighter to a class and you are good to go.
Output of the print statements: (Second print statement changed to print(g.name, g.health))
David 70.0
--> i: 1
Goliath 240.0
David 40.0
--> i: 2
Goliath 180.0
David 10.0
--> i: 3
Goliath 120.0
David -20.0
For more reading: Value and Reference Types
After calling the method func attack(f: Fighter) -> Void every time, the properties of the Fighter who is being attacked are not getting updated. So while loop is not going to break at any point.
Please replace the code below.
static func fight(fighter1: Fighter, fighter2: Fighter) -> Fighter {
var f1 = fighter1
var f2 = fighter2
if f1.health == f2.health {
return f1
}
if f2.initiative > f1.initiative {
f1 = f2.attack(f: f1)
}
var i = 0
while f1.health > 0 {
i += 1
print("--> i: \(i)")
f2 = f1.attack(f: f2 )
if f2.health <= 0 {
return f1
}
f1 = f2.attack(f: f1)
}
return f2
}
func attack( f: Fighter) -> Fighter {
var g = f
g.health = g.health - Double(g.damage * (1 - g.defense / 100))
print(g)
return g
}
When you're saying...
var g = f
...you're actually creating a copy of that object, not a reference. So, when you're changing 'health' property, you changing it in the copy.
There are 2 simple solutions:
1) Change struct to class, 'cause classes are being referenced, unlike structs, which is just copying.
2) Replace original object with its modified copy (g)
As Rakesha notes, structs are value types, so your attack code doesn't actually modify anything:
func attack(f: Fighter) -> Void {
var g = f // Make a mutable copy of `f` called `g`
g.health = g.health - Double(g.damage * (1 - g.defense / 100)) // Modify g
print(g) // Print g
// Throw g away
}
(Side note: I think g.damage is incorrect here; I think you probably meant self.damage.)
Nothing there actually modifies f. There are several ways to address this. One is to use classes, which introduces subtle mutable state. I don't think I'd do that here. By "subtle mutable state" I mean that you expect attack to modify f, but nothing in the signature says that it does that, so the caller may be surprised.
Instead, you have several ways to implement this that make your mutations explicit on structs. You can make attack explicitly modify f:
func attack(f: inout Fighter) {
f.health = f.health - Double(damage * (1 - f.defense / 100))
}
Or you could turn it around and modify yourself when attacked by someone else:
mutating func attackedBy(f: Fighter) {
health = health - Double(f.damage * (1 - defense / 100)
}

How to add the elements with the same ending number in the same array? (Swift 3)

I have my code below and I am attempting to create new arrays, in which the numbers in the elements increment by 1 whenever the user adds a book. For instance, when the user already has 1 book added and when he adds another one the array should read as ["bookTitle2,"bookAuthor2", "bookPublisher2", "bookNumOfPages2"]
let bookDetails = ["bookTitle", "bookAuthor", "bookPublisher", "bookNumOfPages"]
var bookDetail = ""
var bookNumber = Int()
var bookNumArray = [Int]()
if bookNumArray.contains(bookNumber) {
print("Book already exists")
} else {
while bookNumber < 2 {
bookNumber += 1
bookNumArray.append(bookNumber)
for detail in bookDetails {
bookDetail = "\(detail)" + String(bookNumber)
let newBookArray = [bookDetail]
print(newBookArray)
}
}
}
When I run the code above, this shows up instead:
["bookTitle1"]
["bookAuthor1"]
["bookPublisher1"]
["bookNumOfPages1"]
["bookTitle2"]
["bookAuthor2"]
["bookPublisher2"]
["bookNumOfPages2"]
So I want all the strings that end with 1 in one array and those that end in 2 in another array.
When you do:
for detail in bookDetails {
bookDetail = "\(detail)" + String(bookNumber)
let newBookArray = [bookDetail]
print(newBookArray)
}
For every iteration you are creating a new array with the current detail e.g bookTitle into an array of your string var bookDetail declared outside of this loop's scope.
Also note that newBookArray is a local variable, so it will be destroyed when it gets out of the loop. You would need an array of bookDetail to store the newBookArray.
let bookDetails = ["bookTitle", "bookAuthor", "bookPublisher", "bookNumOfPages"]
var bookDetailArray : [Array<String>] = [] //Your array to store all the bookDetails
var bookDetail : [String] = [] //Your array to store a bookDetail
var bookNumber = Int()
var bookNumArray = [Int]()
Then you can do:
bookDetail.removeAll() //Clear all objects before appending new one
for detail in bookDetails {
bookDetail.append("\(detail) + String(bookNumber)")
}
bookDetailArray.append(bookDetail)
Just a suggestion: As other people said, a dictionary or a class for the bookDetail properties would be a better model in your case. Read up on Object-oriented programming if you ever plan to use a class.
I would do a dictionary or create a class bookDetails with properties like bookTitle, bookAuthor, etc. And then I would create an array of the instances of this class.
If you want to do it your way, Why not create a two-way array, something like:
var arrayFinal = [[""]]
var bookNumber = 0
// Whenever the action is triggered
bookNumber += 1
var bookDetails = ["bookTitle", "bookAuthor", "bookPublisher", "bookNumOfPages"]
for detail in bookDetails
{
detail = "\(detail) +\(bookNumber)"
}
arrayFinal.add(bookDetails)
Or something like that...

Objects of same class have the same properties even though declared differently when pushing segue

I've been stuck trying to fix an issue in my application that prevents me from getting my desired result.
When pushing segue to the next view controller, I passed two objects of the same class to var original:[Person] = [] and var edited:[Person] = []
if (segue.identifier == "showSummary") {
let vc = segue.destination as! SummaryViewController
vc.original = myObject
vc.edited = Bill.calculateBill(myObject, switchServiceCharge.isOn, switchGST.isOn)
vc.gst = (switchServiceCharge.isOn, switchGST.isOn)
}
Before calculateBill, the original value of a property inside is 11.
The calculateBill is a class function in another class Bill:
class func calculateBill(_ bill: [Person],_ svcCharge: Bool,_ GST: Bool) -> [Person] {
var mutableBill = bill
for i in 0 ..< mutableBill.count {
for j in 0 ..< mutableBill[i].items.count {
let tax = Bill.getGSTForIndividual(mutableBill[i].items[j].itemPrice, svcCharge, GST)
mutableBill[i].items[j].itemPrice += tax
}
}
return mutableBill
}
class func getGSTForIndividual(_ individualAmt: Decimal,_ svcCharge: Bool,_ GST: Bool) -> Decimal {
var taxCost : Decimal = 0.00
let SERVICE_CHARGE = Bill.getServiceCharge() //0.10
let GOODS_SERVICE_TAX = Bill.getGST() //0.07
if (svcCharge && GST) {
taxCost = individualAmt * SERVICE_CHARGE
taxCost = taxCost + ((individualAmt + taxCost) * GOODS_SERVICE_TAX)
}
else if (!svcCharge && GST) {
taxCost = individualAmt * GOODS_SERVICE_TAX
}
else if (svcCharge && !GST) {
taxCost = individualAmt * SERVICE_CHARGE
}
else {
taxCost = 0.00
}
return taxCost
}
When I did a print() to test whether the properties inside are different, they both yield the same results somehow...
print(original[0].items[0].itemPrice) //12.947000000000000123904, originally 11
print(edited[0].items[0].itemPrice) //12.947000000000000123904
What exactly is going on and why do both the objects have the same properties even though I have declared them differently?
I'm guessing that Person is probably declared as a class, not a struct. This means that it's a reference type, and when you:
var mutableBill = bill
you're not actually making a copy of bill, but rather another reference to the same object. So, when you change the properties on mutableBill, you also change the properties on bill, since they both point to the same object.
The solution is to make an actual copy of bill. The two options are either to declare Person as a struct, in which case you will always get a copy, or else to make an initializer on Person that takes another Person and initializes itself using the passed-in Person's properties:
init(person: Person) { ... }

How to observe a change in a class's property from another class

I've got a question on property observers. There's some example code below. What I want is for the property Analysis.hasChanged to be updated to true if a.value is changed. Is there a way I can do this?
class Number {
var value: Double
init(numberValue: Double) {
self.value = NumberValue
}
}
class Analysis {
var a: Number
var hasChanged = false
init(inputNumber: Number) {
self.a = inputNumber
}
}
testNumber = Number(numberValue: 4)
testAnalysis = Analysis(inputNumber: testNumber)
print(testAnalysis.hasChanged) // will print "false"
testNumber.value = 10
print(testAnalysis.hasChanged) // will still print "false", but I want it to print "true"
In the end, I want the user to be able to be notified if any of their analyses use numbers that have been changed so that they can update the results of the analyses if they choose.
You can use the built-in property observers provided by Swift.
Every time you set a new value, the didSet will be called. You just need to attach the closure, wrapping the desired behaviour, to the Number class
class Number {
var valueDidChangeClosure: (()->())?
var value: Double {
didSet {
//won't call the valueDidChangeClosure
//if the value was changed from 10 to 10 for example..
if oldValue != value {
valueDidChangeClosure?()
}
}
}
init(numberValue: Double) {
self.value = numberValue
}
}
class Analysis {
var a: Number
var hasChanged = false
init(inputNumber: Number) {
self.a = inputNumber
self.a.valueDidChangeClosure = {
self.hasChanged = true
}
}
}
let testNumber = Number(numberValue: 4)
let testAnalysis = Analysis(inputNumber: testNumber)
print(testAnalysis.hasChanged) // will print "false"
testNumber.value = 10
print(testAnalysis.hasChanged) // will print "true"
I would do something like this, I apologize in advance if I have some syntax wrong (I usually use C/C++, think of this as more psudo code since you'd have to have a way to copy Number classes, etc.).
class Number {
var value: Double
init(numberValue: Double) {
self.value = NumberValue
}
}
class Analysis {
var a: Number
var _a: Number
bool hasChanged() {
if (a != _a) {
_a = a
return true;
}
return false;
}
init(inputNumber: Number) {
self.a = inputNumber
self._a = self.a
}
}
testNumber = Number(numberValue: 4)
testAnalysis = Analysis(inputNumber: testNumber)
print(testAnalysis.hasChanged()) // will print "false"
testNumber.value = 10
print(testAnalysis.hasChanged()) // will still print "false", but I want it to print "true"
In the end, I want the user to be able to be notified if any of their analyses use numbers that have been changed so that they can update the results of the analyses if they choose.
I don't know if this really addresses that question, I based my answer off of the code you provided. So there may be additional functionality if you want there to be some triggering method (instead of calling .hasChanged()).
Comparing doubles (and any other floating point type) with '=' or '!=' is not a good idea.
Use epsilon function instead.
Details: jessesquires.com/blog/floating-point-swift-ulp-and-epsilon/

Why does the value in this dictionary change?

I've been trying to debug something in my code and came across this. You can put this directly in playground.
import UIKit
class testObj {
var prop1: Int?
}
var testObjInst = testObj()
var myDic : [String : testObj] = [:]
testObjInst.prop1 = 1
myDic["A"] = testObjInst
testObjInst.prop1 = 2
myDic["B"] = testObjInst
testObjInst.prop1 = 3
myDic["C"] = testObjInst
print(myDic["A"]?.prop1) //prints 3
if let myVal = myDic["A"] {
myVal.prop1 = 5
}
print(myDic["A"]?.prop1) //prints 5
How is the myVal variable changing the value for myDic["A"]? Shouldn't myVal be assigned to the result of calling myDic["A"] and the return of this call would ultimately be a new instance of the object?
Edit 1: My segues are performed like this:
if segue.identifier == segueIDs.air {
if let vc = segue.destination as? PointsTableViewController {
//these are the dictionaries.
vc.rewardProgramsDic = rewardProgramsDic
}
}
The issue I've been getting is when a property was set in the destination viewController, when I would press back and print the values in the rewardProgramsDic the values would have changed. I tried setting breakpoints on the rewardProgramsDic as well as using didSet to try and catch the change but neither of those is called when a property is updated in the destination viewController.
Edit 2:
In the originating viewController:
var rewardProgramsDic: [String: IndividualRewardProgram] = [:]
In the destination tableViewController
var rewardProgramsDic: [String: IndividualRewardProgram] = [:]
Here, you are getting such result because you are using Class.
Class is reference types & reference types are not copied when they
are assigned to a variable or constant, or when they are passed to a
function.
Mean by updating object value, will updates all the instances where they actual assigned. Here in above example,
testObjInst.prop1 = 1
myDic["A"] = testObjInst // myDic["A"]?.prop1 :- 1
testObjInst.prop1 = 2
myDic["B"] = testObjInst // myDic["B"]?.prop1 :- 2 & myDic["A"]?.prop1 :- 2
testObjInst.prop1 = 3
myDic["C"] = testObjInst // myDic["C"]?.prop1 :- 3 & myDic["B"]?.prop1 :- 3 & myDic["A"]?.prop1 :- 3
if let myVal = myDic["A"] { //So Here, myDic["A"]?.prop1 :- 3
//Here, myDic["A"] class instance is assigned to myVal object,
//So changing value in myVal object directly reflect in all object.
myVal.prop1 = 5 // myDic["A"]?.prop1 :- 5 & myDic["B"]?.prop1 :- 5 & myDic["C"]?.prop1 :- 5
}
Using this function:
func address<T: AnyObject>(o: T) -> Int {
return unsafeBitCast(o, to: Int.self)
}
and calling it the parent view controller, like this:
print(NSString(format: "%p", address(o: rewardProgramsDic["ID1"]!)))
and then calling this same function in the destination view controller, I confirmed that the parent view controller and the destination view controller's variables were pointing to the same place in memory. This explains why I was getting the cascading down change explained with my conversation with #Nirav.
Thank you both for your responses.

Resources