Changes on Array not being kept after function exits - ios

I've essentially got two methods:
private func addBagToCollection(bag : VendorBag){
var array = arrayForService(bag.type)
array.append(bag)
}
And
func arrayForService(type : BAG_TYPE) -> Array<VendorBag>{
switch type{
case .bagTypeDryCleaning:
return dcBags
case .bagTypeWashAndFold:
return wfBags
case .bagTypeLaunderedShirt:
return lsBags
case .bagTypeHangDry:
return hdBags
case .bagTypeUnknown:
return unBags
}
}
The issue is that the array being referenced in addBagToCollection isn't saving the appended items, meaning that every time that I call the method, the size of the array is 0.
I initialize all my arrays at the top of the class:
var hdBags : Array<VendorBag> = [VendorBag]()
var wfBags : Array<VendorBag> = [VendorBag]()
var lsBags : Array<VendorBag> = [VendorBag]()
var dcBags : Array<VendorBag> = [VendorBag]()
var unBags : Array<VendorBag> = [VendorBag]()
but for some reason, the arrayForService function seems to either only return a copy of the array, or a newly initialized one. How do I return a reference to that array so that all changes made to it are kept even after exiting the function?
Got some more info:
So indeed it seems like arrays are copied when returned from functions--
Swift’s Array types are implemented as structures. This means that arrays
are copied when they are assigned to a new constant or variable, or when they are passed to a function or method.
So how do I return a reference to the actual array rather than a copy of it?
Thanks!

Unlike NSArray class, Swift arrays are struct types which means they are copied when they are passed into functions.
From docs:
Note
Structures are always copied when they are passed around in your code, and do not use reference counting.
You need to use inout keyword if you want your array to be passed by reference, not by copy:
private func addBagToCollection(inout array: Array<VendorBag>, bag: VendorBag) {
array.append(bag)
}
You can call that function as follows:
addBagToCollection(&arrayForService(bag.type), bag)

Related

Swift: Struct Array vs Class Array

I have a swift array of struct and I am unable edit the first property, whereas I am able edit the first property with an array of class.
In order to edit the first object of the struct array, I have to do [0] then .first
I know structs are valued by type, class are value by reference. But I don't understand the different behavior. Can someone explain?
class PersonObj {
var name = "Dheearj"
}
struct Person {
var name = "Dheearj"
mutating func update(name: String){
self.name = name
}
}
var array = [Person(),Person()]
array[0].update(name:"dheeraj")
array[0].name = "yuuu"
array.first?.name = "dddddd" <--- "Error Here"
var array1 = [PersonObj(),PersonObj()]
array1.first!.name = "ttt"
print(array1.first?.name ?? "")
print(array.first?.name ?? "")
print(array.count)
Screenshot of the error message:
Mutating a struct stored within some other property behaves as though you've copied out the value, modified it, and overwrote it back into place.
Take this line for example: (I replaced the optional chaining with force unwrapping, for simplicity)
array.first!.name = "dddddd"
It behaves as though you did:
var tmp = array.first!
tmp.name = "dddddd"
array.first = tmp
It's easy to see what that doesn't work. Array.first, is a get-only property (it doesn't have a setter).
The case for classses works because the value stored in the array is a reference to the object, and the reference isn't changing (only the values within the object it refers to, which the array doesn't know or care about).

let array behavior in Swift 2.1

I am befuddled by the current array behavior in Swift 2.1. I have read the docs and many posts (which could be out-of-date) and am no closer to understanding.
Here's my code for separating a deck of cards into wildCards and non-wildcards:
let wildCards : [Card] = []
let nonWildCards : [NonJoker] = []
Meld.sortAndSeparateWildCards(wildCardRank, cards: self.cards, nonWildCards: nonWildCards, wildCards: wildCards)
...
static func sortAndSeparateWildCards(wildCardRank : Rank, var cards : [Card], var nonWildCards : [NonJoker], var wildCards : [Card]) {
//cards contains the list to be sorted
if cards.isEmpty {return}
nonWildCards.removeAll()
wildCards.removeAll()
for card in cards {
if (card.isWildCard(wildCardRank)!) {wildCards.append(card)}
else {nonWildCards.append(card as! NonJoker)}
}
cards = nonWildCards.sort(NonJoker.cardComparatorRankFirstDesc)
cards += wildCards
}
What I don't understand:
Xcode insists that I should change wildCards and nonWildCards to let constants, even though I am mutating the arrays by adding values to them (many of the posts I have read say that let behavior prevents adding to or removing elements from an Array)
I originally had these arrays passed as inout variables, because I thought that Arrays were passed by value and not by reference (the docs suggest that they are treated this way; see the bottom of this page https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html#//apple_ref/doc/uid/TP40014097-CH13-ID82)
EDIT: I thought the code was working, but I was confused.
Corrected code, per the answer below, with the bad behavior that isEmpty returns nil.
var wildCards : [Card] = []
var nonWildCards : [NonJoker] = []
Meld.sortAndSeparateWildCards(wildCardRank, cards: &self.cards, nonWildCards: &nonWildCards, wildCards: &wildCards)
...
static func sortAndSeparateWildCards(wildCardRank : Rank, inout cards : [Card], inout nonWildCards : [NonJoker], inout wildCards : [Card]) {
//cards contains the list to be sorted
if cards.isEmpty {return}
nonWildCards.removeAll()
wildCards.removeAll()
for card in cards {
if (card.isWildCard(wildCardRank)!) {wildCards.append(card)}
else {nonWildCards.append(card as! NonJoker)}
}
cards = nonWildCards.sort(NonJoker.cardComparatorRankFirstDesc)
cards += wildCards
}
Arrays are structs in Swift. In the way you've done it, the changes are not persisted out of the method. If you want to alter them in a function, you need to mark them as var in the declaration, and pass their pointer when calling the function. So something like this:
var wildCards = [Card]()
var nonWildCards = [NonJoker]()
static func sortAndSeparateWildCards(wildCardRank : Rank, inout cards : [Card], inout nonWildCards : [NonJoker], inout wildCards : [Card]) {}
Meld.sortAndSeparateWildCards(wildCardRank, cards: &self.cards, nonWildCards: &nonWildCards, wildCards: &wildCards)
You were right when assuming that arrays were being passed by value and not by reference. In Swift, arrays are value types - they are structs - which means that if you want to modify their contents, you need to declare them as var.
Now var has a broader meaning, and if it's applied to function arguments, it allows you to mutate those arguments within the function. However, keep in mind that you are mutating a copy of the array that was passed as argument.
So, in order for the changes that you apply to an array within a function be visible to the caller of that function, you need to declare the parameters as inout, which passes them by reference instead of value.

Swift how to "pass by value" of a object

I am quite new in Swift. And I create a class(for example):
class Fraction{
var a: Int
init(a:Int){
self.a = a
}
func toString() -> String{
return "\(self.a)"
}
}
and I also build a in other class function:
class func A_plusplus(f:Fraction){
f.a++
}
Then in the executive class I write:
var object = Fraction(a:10)
print("before run func = " + object.toString())
XXXclass.A_plusplus(object)
print("after ran func =" + object.toString() )
So the console output is
before run func = 10; after ran func =11
The question is how can I just send a copy of the "object" to keep its value which equal to 10
And if functions are always pass-by-reference, why we still need the keyword: "inout"
what does difference between A_plusplus(&object)//[if I make the parameter to be a inout parameter] and A_plusplus(object)
Universally, I don't want to use struct. Although this will solve my
problem exactly, I do pass-by-value rarely.So I don't want program's
copying processes slow my user's phone down :(
And It seems conforming the NSCopying protocol is a good option.But
I don't know how to implement the function:
func copyWithZone(zone:
NSZone)-> AnyObject? correctly
If your class is subclass of NSObject,better to use NSCopying
class Fraction:NSObject,NSCopying{
var a:Int
var b:NSString?
required init(a:Int){
self.a = a
}
func toString() -> String{
return "\(self.a)"
}
func copyWithZone(zone: NSZone) -> AnyObject {
let theCopy=self.dynamicType.init(a: self.a)
theCopy.b = self.b?.copy() as? NSString
return theCopy
}
}
class XXXclass{
class func A_plusplus(f:Fraction){
f.a++
f.b = "after"
}
}
var object = Fraction(a:10)
object.b = "before"
print("before run func = " + object.toString())
print(object.b!) //“Before”
XXXclass.A_plusplus(object.copy() as! Fraction)
print("after ran func =" + object.toString() )
print(object.b!)//“Before”
If it is just a common swift class,You have to create a copy method
class Fraction{
var a: Int
init(a:Int){
self.a = a
}
func toString() -> String{
return "\(self.a)"
}
func copy()->Fraction{
return Fraction(a: self.a)
}
}
class XXXclass{
class func A_plusplus(f:Fraction){
f.a++
}
}
var object = Fraction(a:10)
print("before run func = " + object.toString())
XXXclass.A_plusplus(object.copy())
print("after ran func =" + object.toString() )
To make it clear,you have to know that there are mainly two types in swift
Reference types. Like Class instance,function type
Value types,Like struct and others(Not class instance or function type)
If you pass in a Reference types,you pass in the copy of Reference,it still point to the original object.
If you pass in a Copy type,you pass in the copy of value,so it has nothing to do with the original value
Let us talk about inout,if you use it,it pass in the same object or value.It has effect on Value type
func add(inout input:Int){
input++
}
var a = 10
print(a)//10
add(&a)
print(a)//11
Swift has a new concept so called "struct"
You can define Fraction as struct (Not class)
And
struct Fraction{
...
}
var object = Fraction(a:10)
var object1 = object //then struct in swift is value type, so object1 is copy of object (not reference)
And if you use struct then try to use inout in A_plusplus function
Hope this will help you.
how can I just send a copy of the "object" to keep its value which equal to 10
In Swift classes and functions are always passed by reference. Structs, enums and primitive types are passed by value. See this answer.
You can't pass an object by value. You would have to manually copy it before passing it by reference (if that's what you really want).
Another way is to turn your class into a struct, since it would then be passed by value. However, keep in mind there a few other differences between classes and structs, and it might not necessarily be what you want.
And if functions are always pass-by-reference, why we still need the keyword: "inout"
According to the swift documentation, inout is used when
you want a function to modify a parameter’s value, and you want those changes to persist after the function call has ended, define that parameter as an in-out parameter instead.
So in practice with inout you can pass a value type (such as struct or primitive) by reference. You shouldn't really use this very often. Swift provides tuples, that could be used instead.
what does difference between A_plusplus(&object)//[if I make the parameter to be a inout parameter] and A_plusplus(object)
There is no difference for your A_plusplus function. In that function you don't modify the parameter f itself, you modify the f.a property.
The following example shows the effect of using inout when passing a class object. Both functions are the same, differing only in its parameter definition.
class Person {
var name: String
init(name: String) { self.name = name }
}
var me = Person(name: "Lennon") // Must be var to be passed as inout
// Normal object by reference with a var
func normalCall(var p: Person) {
// We sure are able to update p's properties,
// and they will be reflected back to me
p.name = "McCartney"
// Now p points to a new object different from me,
// changes won't be reflected back to me
p = Person(name: "Ringo")
}
// Inout object reference by value
func inoutCall(inout p: Person) {
// We still can update p's properties,
p.name = "McCartney"
// p is an alias to me, updates made will persist to me
p = Person(name: "Ringo")
}
print("\(me.name)") //--> Lennon
normalCall(me)
print("\(me.name)") //--> McCartney
inoutCall(&me)
print("\(me.name)") //--> Ringo
In normalCall p and me are different variables that happen to point to the same object. When you instantiate and assign a new object to p, they no longer refer to the same object. Hence, further changes to this new object will not be reflected back to me.
Stating that p is a var argument just means that its value can change throughout the function, it does not mean the new value will be assigned to what was passed as argument.
On the other hand, in inoutCall you can think of p and me as aliases. As such, assigning a new object to p is the exact same as assigning a new object to me. Any and every change to p is persisted in me after the function ends.

I am creating objects with var because I mutate them but I get warning: "Variable 'variableName' was never mutated, consider..."

I am creating at launch Dictionaries with var because I will modify them later when user does something. Dictionaries are added inside an Array in a singleton class to be used in multiple places but I get the warning "Variable 'variableName' was never mutated, consider...."
in the place I am creating them
If I make them with let and when I get object form array to modify it if I take it from array with var, no crash, no warning, no nothing...
What is the explanation for this?
UPDATE:
My Singleton Class:
class Config {
static let sharedInstance = Config()
var array_shapes: Array<Dictionary<NSObject,AnyObject>> = Array()
func createInitialShapeArray(){
var avion = createShapeDictionaryFor("Avion", objectName: "Avion", badgeStatus: "0", shapeImageName: "shape_avion");
//.......more objects like avion
array_shapes = [avion,//.....the other objects];
}
func createShapeDictionaryFor(objectID:String, objectName:String, badgeStatus:String, shapeImageName:String) -> Dictionary<NSObject,AnyObject>{
var dict: Dictionary<NSObject,AnyObject> = [:]
dict["objectID"] = objectID
dict["objectName"] = objectName
dict["badgeStatus"] = badgeStatus
dict["shapeImageName"] = shapeImageName
return dict;
}
}
And when I am mutating dictionaries (In main class):
#IBAction func btnPressed_done(sender:UIButton){
pennyPincherGestureRecognizer.recognize();
screenShotMethod()
var dict = Config.sharedInstance.array_shapes[Config.sharedInstance.currentShapeIndex] as Dictionary<NSObject,AnyObject>
dict["badgeStatus"] = "1"
self.initNextShape()
}
var avion has the warning "Variable 'variableName' was never mutated, consider...."
It is not an error trough, it's a warning and I was curious if I could silence them or what can I do to make them dissappear
Facts
You are declaring avion as a local variable of the method createInitialShapeArray
You are not mutating avion in the scope where it is defined
avion is a Dictionary therefore a Struct (value type rules are applied)
Conclusion
There is no need to declare avion as a variable, it should be a constant.
Please note that where you write
array_shapes = [avion, ...]
you are creating a copy of avion (because it's a Dictionary).
So if you change the value inside array_shapes you are changing another value.
Therefore, at the end of the day, you are not mutating avion... and the compiler is right, it should be a constant.
Example
Please consider the following code
func foo() {
var dict = [1: "One"] // <-- Compiler warning
var anotherDict = dict
anotherDict[2] = "Two"
}
Here I am getting the same compiler warning
Variable 'dict' was never mutated; consider changing to 'let' constant
This happens because I am changing anotherDict that is not just another reference to the same value, it is actually a totally different value. This is the rule with Struct(s) and Enum(s) because they are Value Types.
Hope this helps.
In Swift arrays and dictionaries are declared as struct so when you pass them to other function or use them in assignments their value is copied and not passed as reference the same way it's done for classes, this means that when you pass avion to the append() function of your array you pass a copy of the dictionary so the original variable is never mutated.
The same things happens when you try to modify on dictionary in the array thus copying the dictionary of your interest in dict: you aren't modifying the array inside your shared instance but the local variable dict.

Swift: how to make array of mutable dictionaries? [duplicate]

I’m new to Swift and have been having some troubles figuring out some aspects of Arrays and Dictionaries.
I have an array of dictionaries, for which I have used Type Aliases - e.g.
typealias myDicts = Dictionary<String, Double>
var myArray : [myDicts] = [
["id":0,
"lat”:55.555555,
"lng”:-55.555555,
"distance":0],
["id":1,
"lat": 44.444444,
"lng”:-44.444444,
"distance":0]
]
I then want to iterate through the dictionaries in the array and change the “distance” key value. I did it like this:
for dict:myDicts in myArray {
dict["distance"] = 5
}
Or even specifically making sure 5 is a double with many different approaches including e.g.
for dict:myDicts in myArray {
let numberFive : Double = 5
dict["distance"] = numberFive
}
All my attempts cause an error:
#lvalue $T5' is not identical to '(String, Double)
It seems to be acting as if the Dictionaries inside were immutable “let” rather than “var”. So I randomly tried this:
for (var dict:myDicts) in myArray {
dict["distance"] = 5
}
This removes the error and the key is indeed assigned 5 within the for loop, but this doesn't seem to actually modify the array itself in the long run. What am I doing wrong?
The implicitly declared variable in a for-in loop in Swift is constant by default (let), that's why you can't modify it directly in the loop.
The for-in documentation has this:
for index in 1...5 {
println("\(index) times 5 is \(index * 5)")
}
In the example above, index is a constant whose value is automatically
set at the start of each iteration of the loop. As such, it does not
have to be declared before it is used. It is implicitly declared
simply by its inclusion in the loop declaration, without the need for
a let declaration keyword.
As you've discovered, you can make it a variable by explicitly declaring it with var. However, in this case, you're trying to modify a dictionary which is a struct and, therefore, a value type and it is copied on assignment. When you do dict["distance"] = 5 you're actually modifying a copy of the dictionary and not the original stored in the array.
You can still modify the dictionary in the array, you just have to do it directly by looping over the array by index:
for index in 0..<myArray.count {
myArray[index]["distance"] = 5
}
This way, you're sure to by modifying the original dictionary instead of a copy of it.
That being said, #matt's suggestion to use a custom class is usually the best route to take.
You're not doing anything wrong. That's how Swift works. You have two options:
Use NSMutableDictionary rather than a Swift dictionary.
Use a custom class instead of a dictionary. In a way this is a better solution anyway because it's what you should have been doing all along in a situation where all the dictionaries have the same structure.
The "custom class" I'm talking about would be a mere "value class", a bundle of properties. This was kind of a pain to make in Objective-C, but in Swift it's trivial, so I now do this a lot. The thing is that you can stick the class definition for your custom class anywhere; it doesn't need a file of its own, and of course in Swift you don't have the interface/implementation foo to grapple with, let alone memory management and other stuff. So this is just a few lines of code that you can stick right in with the code you've already got.
Here's an example from my own code:
class Model {
var task : NSURLSessionTask!
var im : UIImage!
var text : String!
var picurl : String!
}
We then have an array of Model and away we go.
So, in your example:
class MyDict : NSObject {
var id = 0.0
var lat = 0.0
var lng = 0.0
var distance = 0.0
}
var myArray = [MyDict]()
let d1 = MyDict()
d1.id = 0
d1.lat = 55.55
d1.lng = -55.55
d1.distance = 0
let d2 = MyDict()
d2.id = 0
d2.lat = 44.44
d2.lng = -44.44
d2.distance = 0
myArray = [d1,d2]
// now we come to the actual heart of the matter
for d in myArray {
d.distance = 5
}
println(myArray[0].distance) // it worked
println(myArray[1].distance) // it worked
Yes, the dictionary retrieved in the loop is immutable, hence you cannot change.
I'm afraid your last attempt just creates a mutable copy of it.
One possible workaround is to use NSMutableDictionary:
typealias myDicts = NSMutableDictionary
Have a class wrapper for the Swift dictionary or array.
class MyDictionary: NSObject {
var data : Dictionary<String,Any>!
init(_ data: Dictionary<String,Any>) {
self.data = data
}}
MyDictionary.data

Resources