Data management across segues - ios

This is a concept question.
Like many others, I have a parent scene and a child or detail scene. When I click on a specific button in my parent scene, prepare to segue gets called and I pass over a couple of properties. In my detail scene, I gather more information and need to save it for use by the parent. I have seen various methods involving delegates, using singletons, and passing directly back to the parent properties. Here is my question, would it be more correct to store the data in a database in the detail controller, or pass it back to the parent controller to store it? It seems to me that since it was collected in the child, it should be stored there.
Would that be the more correct way of handling it?

For simplicity, say the larger model is an array of custom objects and the detail view presents and edits one of those objects. If you pass a custom object to the detail vc on before the segue, there's no need to "pass it back" later. The parent vc passed that object in the first place, so we know it already has a pointer.
Instead, the parent vc should notice that the work has been done on the object it passed and react accordingly (usually update it's view) This can be done by one of a handful techniques:
just assume something changed and update the view on viewWillAppear
notice the custom object changed via KVO (observing one of its properties)
be notified that the object changed because the detail vc posts an NSNotification
learn that the object changed by arranging to be the delegate of the detail vc
pass the detail vc a block to execute when it updates the custom object
It's hard to say what's better without knowing more about your situation. I can say that simple is usually better, and that favors (1). I can also say that delegates are fine, but often overused.

If you're dealing with more than a few pieces of information, it would likely be much easier in the long run to use Core Data. If, down the road, you decided to add more functionality to the app, or wanted to store different kinds of information, it would take no more than a few extra lines of code to do this.
But from what it sounds like, you have a really simple app, so you should probably be fine just making an instance of your "parent scene" in your "detail scene" and storing whatever values you may have in the "detail scene" into properties that already exist in the "parent scene".
Does that answer your question well enough?

Related

iOS - Views (ViewController), having persistent data

I am currently working on an app using Xcode/swift 4 where I need a view (lets call it loggerView), which is not the main view, to be able to take data from the main view (or other views) in a manner that allows said data to be shown whenever loggerView is displayed (using a text view in this case). I understand that I can use a segue to present/push this data (text log msg) to the loggerView upon transition to the loggerView, but there are a couple of probs with that:
When using segues the data can only be pushed all at once through segue prepare when transitioning and not piecemeal, which I need.
When the user exits loggerView back to the primary (or other) view all the data in the loggerView is lost.
Because of (2) I would need the primary view to store all logging data related to the information I want displayed in the loggerView such that upon segueing to loggerView all the data can be provided each time. This is not ideal because I don't want the primary view to store this, I'm looking for a way to fire this data to loggerView in the same way a delegate might work back from loggerView->primaryView where it can be called at any time whilst loggerView is being shown.
I think the main prob here is that I am working with a parent->child view situ (using navigation controller) where every time we transition back from child to parent the child view (loggerView in this case) is removed from memory and so all data pertaining to loggerView is deleted, thus the need to segue everything each time loggerView is opened.
Is there a way to make more than one view permanent (memory wise) and then send data between primaryView and this other view on an ad-hoc basis rather like the way delegates might work in reverse (loggerView->primaryView)?
Summarizing our discussion in the comments:
Your objection to storing the child's data back in the parent is probably misplaced. Such behavior is normal and standard. If you insist on being different, you could keep the data in UserDefaults, as being both global and persistent, but that is usually regarded as a crutch.
In effect, your desire to persist the view controller merely in order to persist some data that it has fetched from the user is just wrong. Suppose a dialog pops up asking you to enter a name. You enter the name and tap OK, and the dialog vanishes. You do not believe that the dialog should persist behind the scenes just to hold the name, do you? No — what persists is the name. The dialog, and all the mechanism that enabled it to appear, was merely temporary in order to manage the user interface.
It might help to repeat "model view controller" (MVC) to yourself over and over as you work. The data collected from the user is model. If you want it to persist, persist it. But do not persist a view controller merely in order to do that. Think of view controllers as nimble; their purpose is to come and go, as the views that they control come and go. If you have data that needs to persist, it needs to be passed around, or put in a more persistent place. Model, view, controller. Three different things. Do not try to turn the controller into the model!

Bi directional communications between two view controllers in Objective-C

I have a main Container View controller which has a table view controller and another view controller. In table view I am displaying certain items which can be selected and grouped. This group details are shown on the view controller(like a summary). once grouped these items will no longer be in table view. If needed I can even ungroup them and add it back to table view.
So these two view controllers need to have a communication channel between them. What would be the best approach here ? Protocols or blocks ? Notifications are a strict no.
The business logic of your application should be handled separately from the code that glues things into views — you're conflating model and controller.
So the items and the groupings would be maintained by a third class. Both view controllers would talk to it. They would nit talk to one another beyond transient UI information like, say, one saying "this was the specific item selected".
A good way to think about it is to start with the goal of no direct communication. Instead, both VCs have access to the application's model (usually a form of a singleton), and configure their state based on the state of the model. In other words, the view controllers are stateful with respect to the app's model, stateless with respect to each other.
But sometimes vcs in the same container must communicate, like in a navigation controller when a selection is made. That's typically done in prepare for segue. The selection is conveyed to the vc being pushed, and then it carries on. To the extent that vc needs to communicate back down the stack, I try to go by the first principal: can it just change the model state and get popped, counting on its parent to notice the changes in viewDidAppear?
If I need further communication still, I begin to worry about my design, but the next place I go is KVO. There again, view controllers are passive with respect to each other, watching the app's model for changes they care about. I classify NSNotification in this category. Even though it sounds like you want to rule it out, it's my third favorite idea after nothing.
While I'm editing here, I notice #Tommy has provided concise advice that I agree with (and consistent with my opinion here). I'll leave the answer since it adds a little nuance.

Custom ViewController objects don't persist through View changes

I have no idea if I'm using the right terminology here, but I'm hoping someone can help - I'm pretty new to iOS Dev and have run into a problem:
I have created a custom class that is used to store an object. It has a bunch of properties and functions, just as an object should. This object is being declared in my ViewController's .h file, and initialised and used throughout the ViewController. The object holds a bunch of information about a test, and takes some measurements in various threads.
The problem I'm facing is that when I load another view (using ECSlidingViewController for the menu) and then return to the view which had the object...it seems to have forgotten the object. The tasks running in the threads are all still running, but that instance of the object seems to be gone.
Is there a way to preserve an instance of the object when changing views so that when I return to the appropriate view, the object is still there and I can still use it?
Thanks!
I'd recommend you to put the objects reference in a transversal class, something like a manager or any other class which you consider appropriate according to your design and most important it needs to be a class that you're completely sure won't be release nor re-created as view controllers are usually when you change from one view controller to the other.
A singleton instance that manages your main logic could be a good option.
Turns out that my menuViewController that was implementing the ECSlidingViewController was recreating the view that contained the object each time I selected the item from the menu. Thanks #rdelmar for pointing this out!
The object was persisting, but in a viewController that was being replaced each time it was selected from the menu and hence the object was unreachable.
I simply implemented this and references to the viewControllers are now being stored in a mutable dictionary and simply recalled when the view is needed, rather than recreating the view. This means that the viewController is being reused and the object is persisting with it.

The correct way to communicate between several different view controllers in Objective C

I have an app that was extremely simple until today. It had a tab bar view controller with 3 tabs. The middle tab was a camera, and the other 2 were table views. The tab bar view controller was the central hub for all the data in the app. So from there, I would set a table's data array as:
(PLEListViewController*)[self.viewControllers objectAtIndex:0] setList:newList];
Obviously, PLEListViewController is my UITableView subclass.
So now, I want to wrap the table views in a UINavigationController, which is fairly simple. But now, that line of code turns into:
[(PLEListViewController*)((UINavigationController*)[self.viewControllers objectAtIndex:0]).topViewController setList:newList];
There are 15 lines in the code that do this, which is not pleasant.
So my question: what is a more elegant way of doing this that I'm missing?
It's good that you're asking this and seeing the issue now. Your problem is can be found in your question. The answer to "the correct way to communicate between several different view controllers in Objective C" is "don't." Specifically, your mistake is here:
The tab bar view controller was the central hub for all the data in the app.
A view controller should never hold any of the data in the app. Your data should live in your model classes. All the view controllers should talk to the model classes. They should very seldom talk to each other. That's the heart of MVC.
So, you move your "list" (whatever that is, doesn't matter) into some model object that all the view controllers know about. That model object can be a singleton, or often better, it can be passed to the view controllers when they are created. When things change, you change the model. And in viewWillAppear: you update your view controller to match the current state of the model.
Never assume that a view controller exists when it is not currently on screen. If your design requires that a non-active view controller exist, then your design needs fixing.
You need to work with your architecture. Make the appropriate datasource and delegation protocols to ensure your classes can communicate anonymously. What you currently have is very inflexible and it will get worse as your app grows/changes.
You want to make things more loosely-coupled, instead of coding explicit traversal of links between your objects in your code.
Assuming you have one data model that is displayed in various places in your application, I think there are 2 approaches that could help...
One is to use your view controller hierarchy.. For example, use [ self enclosingTabBarController ] to find your closest parent tab bar controller and get it's data model property. Substitute -enclosingTabBarController with what works better for your application.
The other approach would be a "data model as a singleton" approach. For this you can either
move the data to your application delegate and access it via ((MyApplicationDelegateClass*)[ UIApplication sharedApplication ].delegate).dataModel
or
have a singleton data model object for your app, and access it via [ MyDataModelClass sharedModel ]
In any case you are moving to a looser coupling, which requires less explicit traversal of links between objects in your app. Less is more!

Pattern for class to manage view controller flow

At the moment I'm using a Singleton class to do some work but I'm wondering if there is something better.
I have an app that has a completely dynamic work flow. It uses a navigation controller but the order of the view controllers depends entirely on some data that is downloaded from our server.
The entire workflow is downloaded and saved in an array.
The "main menu" screen of the app has several choices (settings, recent, etc...) these are fixed but one of them is the dynamic one. It always starts with the same type of view controller but from then on it depends on what you choose.
Description
There are 4 different types of these dynamic controllers.
Table View Controller with single selection and detail indicators.
Table View Controller with multiple selection and checkmarks.
View Controller with a text field and keyboard.
View Controller (with other related VCs) used for searching for accounts on the server.
When you press the option "New Event" on the main menu the menu then goes off to the singleton (EventManager) and tells it to start a new event.
The singleton then pushes a single selection dynamic view on the nav controller and gives it the initial options.
From here on the singleton picks up all the selections and works out what type of view is required next.
I hope this is making sense
Anyway, I don't like the singleton pattern here as I don't think it should be a singleton.
What I would like is a class that I can create from a ViewController and this class will then control the pushing and popping and flow of data between a load of different view controllers. Then when you go back to the main menu this class can go away so I create a new class each time.
Is there a pattern that I can look at that will do this? Or should I stick with a singleton like I am now?
I hope this makes sense.
EDIT
Could I use a UIPageViewController for this? Then the datasource/delegate object of the UIPageViewController will take the place of the Singleton I am currently using... or something?
ADDING PHOTO FROM TWITTER REQUEST
Each VC along the flow has no idea what cam before it or what comes next. All they do is call back to the singleton to say "This value was selected" or "This text was entered" etc...
The singleton then stores that info and works out what comes next and pushes the next VC onto the stack.
It needs to be able to move back along the stack so the user can go back to change something etc...
It's all working as it is I just don't like the use of the singleton.
Lots of comments here in order of importance.
Everything you've described here sounds really good, even down to the naming. "EventManager" sort of sounds like it manages all "events" in the system (so I'd expect there to be a class called Event, but that's a tiny quibble and the name is likely still very good). There are other good designs, but I wouldn't have any problem with yours.
I agree that this does look like a good fit for UIPageViewController. You should certainly investigate that to see if it's the right fit. It's always nice to use a built-in controller if you can.
There's no reason to strongly avoid singletons. They are a natural part of iOS development and fairly common in good Cocoa design. They should be "shared" singletons generally (never create "strict" singletons that override +allocWithZone:). This just creates an easy-to-access instance rather than a true "singleton." This is the way things like NSNotificationCenter work and is often a very good pattern.
Singletons are best when many random pieces of the system need to access them directly and passing them around to everyone would be a lot of overhead (especially if many of the pieces you'd have to pass the object to don't need it themselves). Again, think NSNotificationCenter. If the users of it are mostly contiguous (i.e. most objects you would pass it to actually need it themselves), then just create one at the start of the program and pass it around. That sounds like your situation, so your intuition about it seems good. Just de-singleton it and pass it. Easy change.
But I'd definitely dig into UIPageViewController. It could match your problem very well.

Resources