Statik Statik - 3 months ago 8
iOS Question

Pass Data Between Two Unrelated ViewControllers

I was given a challenge to pass data back and forth between two (or many) unrelated (by segue) view controllers.

There are three view controllers,

ViewController A
-
ViewController B
and
ViewController C
.

ViewController A
segues to
ViewController B
and
ViewController B
segues to
ViewController C
.
I want to share data (such as text on a textfield) between
ViewController A
and
ViewController C
back and forth

Upon investigation I found four possible ways to achieve it :-


  1. Delegation.

  2. Creating a class with
    static
    member, to store the data contained in textfield.

  3. Using
    NSUserDefaults

  4. Using global variables



What would be the most logical way of achieving this? I would like to use one with least overhead.
Thanks!

Answer

Here are two solutions to the problem, both better (IMO) than delegation or the defaults system, and hugely better than singletons, static variables, global variables.

1. Pass the data through B.

You've got a structure that's like this: A->B->C, and C needs some data that A has. So the view controllers are not really unrelated, they're just not directly connected. A good strategy for handling this is to pass the necessary information through B. That is, A doesn't necessarily know about C, probably shouldn't need to know. And C doesn't need to know about A either. From the perspective of A, there's a job to do and a certain amount of information required to do it. A should therefore provide all the necessary information to B during the A->B segue. If B uses C to get part of its job done, that's fine, but A doesn't care about that. Likewise, C doesn't know about A or even B -- all C knows is that it's given some data to do a job. Since it's B that's instantiating (by means of a segue) C, it's B's responsibility to provide the necessary information.

The same is true going in the other direction. If C generates some data that A will eventually need, B should retrieve it from C before C goes away, and A should retrieve it from B.

The only reason you'd need to make A a delegate of C is if A needs to find out about updates as soon as they occur. If that happens, though, it's probably a good indication that you should move on to the next option...

2. Use your data model.

It's easy to forget that the M in MVC is supposed to be an equal partner in applications. We often just let our view controllers manage all the data, and then try to figure out how to get the right data to flow through the view controller graph to the right places. That can make your application a lot less flexible than it could be: in a A->B->C situation like the one you've got, you're suddenly prevented from inserting another view controller between B and C, for example, because the new guy doesn't know that it needs to pass certain data through from B to C.

The answer is to use a data model -- an object or set of objects that manage the application's data. If you've got data generated in one controller (C in your case) that's needed by some other controller (like A), that data must be important to the overall application and not just to a single view controller. That's exactly the kind of data that should be managed as part of your data model. If you do that, then you don't have to worry about passing a ton of different data to every view controller that might need it, or that (like B) might need to pass it on to someone else. You only need to pass one thing -- a reference to the data model, or the relevant part of the data model.

Let's make this concrete and say that C is actually a "settings" view controller, and that the piece of data in question is the user's name. A needs to know about updates to the name so that it can display it properly. B doesn't care about the name at all. So, you set up some kind of Model class that includes a username property. The Model might be instantiated by the app delegate at startup, or by the root view controller A, and a reference to the model is passed on to each view controller in the view controller graph. Now C can set the username property in the model when the user changes it. A can read the same property to retrieve the data -- in fact, it can use KVO to find out about all the changes to the model that it cares about. A doesn't know about C or vice versa, and you don't have to bother with creating yet another delegate protocol just to pass some data around.