Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift swap array objects

I'm unable to swap an array of strings on cell reorder:

var scatola : [String] = []

override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
    swap(&scatola[fromIndexPath.row], &scatola[toIndexPath.row])
}

this code throws:

inout writeback to computed property 'scatola' occurs in multiple arguments to call, introducing invalid aliasing

What is the right way to do it?

like image 643
Enlil Avatar asked Oct 10 '14 20:10

Enlil


People also ask

How to swap array elements in Swift?

An array can be mutable and immutable. In the Swift array, we can swap the position of the elements. To do so we use the swapAt() function.


2 Answers

Alternatively,

let f = fromIndexPath.row, t = toIndexPath.row
(scatola[f], scatola[t]) = (scatola[t], scatola[f])
like image 168
Can Avatar answered Oct 03 '22 23:10

Can


Update: As of Swift 3.2/4 (Xcode 9) you have to use the swapAt() method on the collection

 scatola.swapAt(fromIndexPath.row, toIndexPath.row)

because passing the array as two different inout arguments to the same function is no longer legal, compare SE-0173 Add MutableCollection.swapAt(_:_:)).


Update: I tested the code again with Xcode 6.4, and the problem does not occur anymore. It compiles and runs as expected.


(Old answer:) I assume that scatola is a stored property in the view controller:

var scatola : [Int] = []

Your problem seems be related to the problem discussed in https://devforums.apple.com/thread/240425. It can already be reproduced with:

class MyClass {
    var array = [1, 2, 3]

    func foo() {
        swap(&array[0], &array[1])
    }
}

Compiler output:

error: inout writeback to computed property 'array' occurs in multiple arguments to call, introducing invalid aliasing
        swap(&array[0], &array[1])
                         ^~~~~~~~
note: concurrent writeback occurred here
        swap(&array[0], &array[1])
              ^~~~~~~~

I haven't yet grasped the contents of the discussion completely (too late here :), but there is one proposed "workaround", namely to mark the property as final (so that you cannot override it in a subclass):

final var scatola : [Int] = []

Another workaround that I found is to get a pointer on the underlying array storage:

scatola.withUnsafeMutableBufferPointer { (inout ptr:UnsafeMutableBufferPointer<Int>) -> Void in
    swap(&ptr[fromIndexPath.row], &ptr[toIndexPath.row])
}

Of course, the fool-proof solution would simply be

let tmp = scatola[fromIndexPath.row]
scatola[fromIndexPath.row] = scatola[toIndexPath.row]
scatola[toIndexPath.row] = tmp
like image 43
Martin R Avatar answered Oct 04 '22 00:10

Martin R