Swift: Pass array by reference?

ArraysSwiftPass by-Reference

Arrays Problem Overview


I want to pass my Swift Array account.chats to chatsViewController.chats by reference (so that when I add a chat to account.chats, chatsViewController.chats still points to account.chats). I.e., I don't want Swift to separate the two arrays when the length of account.chats changes.

Arrays Solutions


Solution 1 - Arrays

For function parameter operator we use:

let (it's default operator, so we can omit let) to make a parameter constant (it means we cannot modify even local copy);

var to make it variable (we can modify it locally, but it wont affect the external variable that has been passed to the function); and

inout to make it an in-out parameter. In-out means in fact passing variable by reference, not by value. And it requires not only to accept value by reference, by also to pass it by reference, so pass it with & - foo(&myVar) instead of just foo(myVar)

So do it like this:

var arr = [1, 2, 3]

func addItem(_ localArr: inout [Int]) {
	localArr.append(4)
}

addItem(&arr)    
print(arr) // it will print [1, 2, 3, 4]

To be exact it's not just a reference, but a real alias for the external variable, so you can do such a trick with any variable type, for example with integer (you can assign new value to it), though it may not be a good practice and it may be confusing to modify the primitive data types like this.

Solution 2 - Arrays

Structs in Swift are passed by value, but you can use the inout modifier to modify your array (see answers below). Classes are passed by reference. Array and Dictionary in Swift are implemented as structs.

Solution 3 - Arrays

Define yourself a BoxedArray<T> that implements the Array interface but delegates all functions to a stored property. As such

class BoxedArray<T> : MutableCollection, Reflectable, ... {
  var array : Array<T>

  // ...

  subscript (index: Int) -> T { 
    get { return array[index] }
    set(newValue) { array[index] = newValue }
  }
}

Use the BoxedArray anywhere you'd use an Array. Assigning of a BoxedArray will be by reference, it is a class, and thus changes to the stored property, through the Array interface, will be visible to all references.

Solution 4 - Arrays

For Swift versions 3-4 (XCode 8-9), use

var arr = [1, 2, 3]

func addItem(_ localArr: inout [Int]) {
    localArr.append(4)
}

addItem(&arr)
print(arr)

Solution 5 - Arrays

Something like

var a : Int[] = []
func test(inout b : Int[]) {
    b += [1,2,3,4,5]
}
test(&a)
println(a)

???

Solution 6 - Arrays

One other option is to have the consumer of the array ask the owner for it as needed. For example, something along the lines of:

class Account {
    var chats : [String]!
    var chatsViewController : ChatsViewController!

    func InitViewController() {
        chatsViewController.getChats = { return self.chats }
    }

}

class ChatsViewController {
    var getChats: (() -> ([String]))!

    func doSomethingWithChats() {
        let chats = getChats()
        // use it as needed
    }
}

You can then modify the array as much as you like inside the Account class. Note that this doesn't help you if you want to also modify the array from the view controller class.

Solution 7 - Arrays

Using inout is one solution but it doesn't feel very swifty to me since arrays are value types. Stylistically I personally prefer to return a mutated copy:

func doSomething(to arr: [Int]) -> [Int] {
    var arr = arr
    arr.append(3) // or likely some more complex operation
    return arr
}

var ids = [1, 2]
ids = doSomething(to: ids)
print(ids) // [1,2,3]

Solution 8 - Arrays

use a NSMutableArray or a NSArray, which are classes

this way you don't need to implment any wraper and can use the build in bridging

open class NSArray : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration

Solution 9 - Arrays

Based off of GoZoner's BoxedArray answer, I created the class below that worked for me. I like the freedom of passing around arrays as references (as the other languages I work with do).

class MArray<T> : MutableCollection {
    
    var array : Array<T> = Array()
    var count: Int { return array.count }   // getter (without, index func below will be called repeatedly to determine)
    
    func add(_ value: T) {
        array.append(value)
    }

    // MutableCollection requires:
    subscript (index: Int) -> T {
      get { return array[index] }
      set(value) { array[index] = value }
    }
    var startIndex: Int {
        return 0
    }
    var endIndex: Int {
        return array.count
    }
    func index(after i: Int) -> Int {
        return i+1
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionma11hew28View Question on Stackoverflow
Solution 1 - ArraysGene KarasevView Answer on Stackoverflow
Solution 2 - ArraysKaan DedeogluView Answer on Stackoverflow
Solution 3 - ArraysGoZonerView Answer on Stackoverflow
Solution 4 - Arraysuser6798251View Answer on Stackoverflow
Solution 5 - ArraysChristian DietrichView Answer on Stackoverflow
Solution 6 - ArrayselRobboView Answer on Stackoverflow
Solution 7 - ArraysToddHView Answer on Stackoverflow
Solution 8 - ArraysPeter LapisuView Answer on Stackoverflow
Solution 9 - ArraysErnie ThomasonView Answer on Stackoverflow