iOS - Views (ViewController), having persistent data - ios

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!

Related

Best way to switch between multiple view controllers while preserving any downloaded data?

What is the best way to have multiple UIViewControllers that can be accessed from a side menu and without having to re-download any data each time the view controller is shown?
Say for example I have three view controllers, one is a homepage that shows recent notifications, one is a news page that shows recent articles and one is a page that has a collection of images. When each view controller is loaded, data is downloaded from a remote server and displayed - if the user switches to another view controller and back again I want the previously downloaded data to be shown immediately without having to be re-downloaded (until a UIRefreshControl is used or similar).
Off the top of my head I can see this being achieved in 4 ways:
Store the data in a singleton and load the data from this if it has been previously downloaded, however, this seems to go against general practices that I have seen and also doesn't seem efficient - especially if there is a lot of data and/or multiple view controller.
Use a UITabBarController that selects the index based on the side menu instead of the traditional bottom bar, would this cause issues if there were 10-12 items on the side menu?
Cache the data to disk (using NSCache etc) and instantiate/dismiss each view controller as needed, the view controller can use the cached data - only one view controller will exist in memory at a time.
Use some kind of customised container view that caches the view controllers and loads them as they are needed, feels a bit hacky and not very efficient?
What are the best ways to achieve this? Most tutorials I see just show how to segue/present view controllers but I have struggled to find anything that explains how to preserve the data that was downloaded on them.
Just as an FYI. If one of your ViewControllers is supposed to "show recent notifications/articles" then caching data won't work because it might not be the most recent unless you pull from the server every time. (Unless the recents are getting pushed and even then you might have huge sync issues if you don't pull every time.)
That said, you options 1 and 3 are effectively the same, the only difference is where you are keeping the information.
You should not be using a tab bar controller. If you can't use UISplitViewController then you should make your own custom container view controller.
As for the caching issue. I would implement some sort of caching mechanism for each endpoint. It could store in ram or on disk but I think in ram is better because the data sounds pretty volatile.
Set it up so that when a view controller makes a "network" call, the function it uses calls the closure with the last data it pulled and then optionally can make the network call again and call the closure a second time with the new data. This way the VCs will be very responsive in getting data up ASAP and still pull in the new data on every view will appear.
I would go with a combination of option 3 and 4.
There are many formats you could use to cache the data to disk. You could use a database like CoreData or Realm, or write directly to files as a Plist, JSON, NSKeyedArchive.
You probably should build a container view controller as well. Thats what a TabBarController is after all. It's pretty easy to do and the docs cover what you need to do. This will make you app more efficient by only having to load data from the database/file the first tome you go to the view controller. Then when switching sections, it can just show the VC thats already been loaded.

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.

Data management across segues

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?

Dismissing view in UINavigationController and at the same time syncing

There's two views on a NavigationController stack:
EDIT ITEM-DETAILS VIEW (= basically a form)
SHOW ALL ITEMS IN A TABLE VIEW
I wonder if there are best practices for the task I have:
When the user taps "BACK" in the UINavigationController-bar (while being in view 1) the app should update the item on the server.
That's not so difficult, but the BACK-action leads to view 2, and 2 is not up-to date, because the update happened in the background and wasn't through before the GET-request for the table view data finished.
So in order to have view 2 always show accurate data, I have several options. All a bit annoying.. (for example having ViewController of view 2 talk to server on 1's behalf and update itself when completed, or having a "update happened" notification that triggers a reload, ...)
But.. what's a good best-practice for this case?
I think I would make a central place for the Items. Lets call it a ItemsStore. ItemsStore is a singleton that has the responsibility to have a set of the latest items and give access to the items. It also fires notifications if new data arrives, or old data is saved.
In this case:
View 2 adds the data to the store. The store notifies that there are
changes.
View 1 updates on the notification.
View 2 also asks the ItemsStore to
save the data to the server.
I would not give the responsibility of loading and saving to the controllers, it will get ugly and complex.

Switching from a map screen to a battle screen without the map progress being reset

I'm trying to make a role-playing game, and I want the game to work so that it transitions to the battle NIB for battles, then returns back to the map NIB afterward, yet still retain all the progress the player has made exploring the dungeon.
I've tried proto-typing this with just a view-switcher, where one view creates content, and then switches to the other view, and then coming back from the other view. However, once the view goes back to the original, the original view is reset.
How do I make the data persistent so that it doesn't reset after every "battle"?
In the model view controller paradigm, you would have a model object that manages the data and a view object that displays the data. When switching from one view to another, you can discard the old view and just store the model objects. You might even write your model object to store data on disk, so you can restore state across launches.
A controller in this scenario would create and destroy the view and pass it the appropriate model object.

Resources