Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing Arrays/Objects between ViewControllers in Swift

Tags:

ios

swift

ios8

Following on from this question: Is there a reason that Swift array assignment is inconsistent (neither a reference nor a deep copy)? -

I have been playing with passing objects in Swift and noticed some strange results.

To clarify the kind of behaviour i'm used to (prior to Swift) would be that of Objective C.

To give an example in one of my Applications (written in Obj C) I have the concept of a 'notification list'. - really just an array of custom objects.

In that App I often pass my global array of 'notifications' to various viewControllers which provide a UI to update the list.

When I pass the global array to a child viewController I assign it to a local array variable in the recipient object. Then, simply by updating/changing the local array these changes are reflected in the global array on the rootViewController. I understand this behaviour is implicit in Objective C as objects as passed by reference, but this is really handy and I have been trying to replicate this behaviour in Swift.

However whilst I have been rewriting my App in Swift I've hit a wall.

I first tried to pass a Swift array of strings (not NSMutableArray) from the rootViewController to a child viewController (as described above).

Here is the behaviour when passing in the array of Strings the child viewController:

I Pass in:

[Bill, Bob, Jack] and then assign this passed array to a local array for local modification,

Then I append the String “Frank” to the local array

The results are:

Local array = [Bill, Bob, Jack, Frank]

Global array = [Bill, Bob, Jack]

No changes to the local array are reflected back to the global array. - The SAME result occurs for a change of element (without changing the length of the array.)

I have also tried the above experiment with a more real world example - passing in an array of my custom 'notification' objects to a child viewController. The SAME result occurs with none of the changes to the locally assigned array of custom objects being reflected to the original global array that was passed in.

This behaviour is not desirable to me, I assume the best practice here is to use delegate protocols to pass the modified array (or whatever object) back to the parent object and then to manually update the global array?? - if so this creates quite an extra workload over the Objective C style behaviour.

Finally I did try the inout keyword, which effectively lets you directly modify the function parameter var thats passed to the destination object.

Changes are reflected back to the global array (or object) However the problem is, if the input parameter is assigned to a local variable (to edit outside of scope of the init function) changes to the local variable are still not reflected in global scope.

I hope the above makes sense - It's really stifling my productivity with Swift.

Am I missing something or is this schizophrenic behaviour expected?

If so what is best practice on passing modified data back, delegates?

like image 649
Woodstock Avatar asked Jun 24 '14 20:06

Woodstock


2 Answers

The linked question provides the answer - it is for performance.

The behaviour may not be desirable for you, but I would say that relying on side-effects from calling methods to modify parameters is the behaviour that is not considered desirable - particularly in a multi-threaded, multi-core environment where data structures can be corrupted.

A design that relies on side-effects is flawed, in my opinion.

If functions need to modify the "global" then they should either return the new value, or if that isn't possible then you should wrap your array inside an object and provide appropriate functions to manipulate the data values.

Swift blurs the lines between intrinsic and object somewhat with arrays, which makes it a little confusing - in Objective-C an NSMutableArray is an object so it always passed by reference.

For notifying other objects that the data has changed you can use an observer pattern. The typical delegate pattern only has a single registered delegate - With an observer pattern you can have multiple registered observers.

You can do this through NSNotificationCenter or an array of "delegates". The former has the advantage of decoupling the code more than delegation

like image 126
Paulw11 Avatar answered Nov 07 '22 11:11

Paulw11


Why don't you create a Model class that contains the array as a var. Add methods to the Model class to manipulate the array and store the new instance in the property. Create a single instance of the Model class at startup and pass it to the view controllers. They all access the array through the Model or through methods in the Model class. The behavior of Swift (where it copies the array on change of size) will be hidden from all of the view controllers.

like image 23
Brian Walker Avatar answered Nov 07 '22 11:11

Brian Walker