Make UITableViewController cells not disappear when navigating away - objective-c

I'm making an app that uses a UITableViewController, and fills that table view with data from a webserver.
Inside my viewDidLoad I have a method that loads data from said webserver, stores it in an array as custom objects, and then loads that into cells. This is working fine.
Problem:
However, every time I navigate away from that UITableViewController, and then back, it loads everything again. This is very unnecessary, so what I did was store a boolean in the NSUserDefaults, which keeps track of whether or not this is the first time starting the app. If it is (you just logged in), load the data from the server. If not, don't.
However, as I noticed, the tableView resets every time I navigate away from (or back to) the Controller. Also, all the arrays I stored the custom objects in are now empty, so I can't load it back from the arrays either.
(Every time I navigate back to the TableViewController, it's empty)
I tried storing the arrays in the NSUserDefaults, and then just populate the tableView with that data every time, but it turns out I can't store custom objects in the NSUserDefaults.
What I want to achieve is this:
Whenever I navigate away from and back to said TableViewController (I use the SWRevealViewController), I don't want the tableView to empty out. I want all the cells to stay, that way there is no wait time between when the view is loaded and the tableview is filled.
If this is impossible, I want the second best. To store the array somewhere in the app, and then reload that data into the tableview as soon as the user navigates back to the TableViewController. This is slower than my preferred solution, but still quicker and less data-consuming than loading everything from my server.
Any help is appreciated!
Thanks.

You should create a separate object that manages fetching the data from the web service and then stores it locally. This object can be created in the app delegate or wherever appropriate and passed to the table view controller. The data object should provide an array that the view controller can then use to populate the table. You can even have that data object start pulling from the web service as soon as the app opens instead of waiting for the table view controller to be displayed.
I do not recommend keeping the view in memory just to save the very minimal amount of time it takes to load up a table view (using locally stored data). Unless you are talking about thousands and thousands of entries, you will not notice a lag time in the loading of the view. If you are talking about thousands and thousands of entries, I recommend you load a few hundred at a time into the table.
As far as storing the data, the simplest might be just writing the raw response of the web request to a file. A more elegant solution would probably include creating some objects to represent the data and using NSKeyedArchiver. Keeping data stored locally is a huge topic with countless options so I recommend doing some googling on your own to find the best solution for you. You might start at these places:
NSKeyedArchiver: http://www.raywenderlich.com/1914/nscoding-tutorial-for-ios-how-to-save-your-app-data
Other Options: https://developer.apple.com/technologies/ios/data-management.html
If you go with the NSKeyedArchiver option, you can generate a file path by doing the following:
+ (NSString *)dataFilePath
{
NSURL *directory = [[NSFileManager defaultManager]
URLForDirectory:NSLibraryDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:YES
error:nil
];
NSURL *fullURL = [cachesDirectory URLByAppendingPathComponent:#"a_file_name"];
return [fullURL relativePath];
}

You need to store all the data in cache at first time when user is calling data from server. And after that whenever user navigate and comeback to the page load data from cache.

Related

Add items to .plist from and display in custom UITableViewCell

I want to write data to multiple NSMUtableArray's from the user pressing IBAction's that then saves the time they pressed it, Location and os on, to a .plist. I then want to retrieve the data that was written in the plist and display it in a custom UITableViewCell that I have created. There are around 8 - 10 different labels in the cell. I have already made the plist file history.plist and there are a few things in there. I need the data to go in there. And to keep writing data there, such that after the data is gathered, it will then be displayed in the custom cell within the UITableView.
Should Item 0 be a dictionary or array?
So far I have the data being saved as NSUserDefaults
well if you want to use plists like that then keep item 0 as a dictionary... However, I strongly suggest that you use Core Data instead of plists to store your data.

How do I prevent NSMutableArray from losing data?

The first view of my app is a UITableView.
The user will choose an option and the next view will be another UITableView. Now the user can tap on an "add" button to be taken to another UIViewController to enter text in a UITextField. That text will then appear in the previous UITableViewCell when saved.
The issue I am having: if I back out to the main view and then go back to where I previously was, that inputed text is now gone.
How can I make sure that text is not being released or disappears like this?
You might want to store this array somewhere else in your project, like in an MVC (data model). You could create a new class for it that passes the information through the classes and stores the array in one place. Then once you add to the array, you could reference that class and call a method in that class to store the text in the array and whenever you load the table view it loads with that array in the class.
In my case, I would do this, but I would make everything class methods (where you cannot access properties or ivars) and just store the array in the user defaults / web service or wherever you need and retrieve and add/return it like this:
+ (NSMutableArray *)arrayOfSavedData {
return [[NSUserDefaults standardUserDefaults] objectForKey: #"savedData"];
}
+ (void)addStringToArray: (NSString *)stringFromTextField {
[[[[NSUserDefaults standardUserDefaults] objectForKey: #"savedData"] mutableCopy] addObject: stringFromTextField];
}
The mutableCopy part is important because arrays don't stay mutable after you store them into the user defaults
The reason the text is gone, is probably because you're instantiating new controllers when you go back to where you were. You can keep a strong reference to your controllers, and only instantiate one if it doesn't exist yet. Exactly how to do this depends on how you're moving between controllers -- whether you're doing it in code, or using a storyboard for instance.
This kind of issue is very frequent. When you move around multiple controllers and views.
Each time you load a new view and controllers are alloc+init, new set of values are assigned and previous values are not used!!!.
You can use SharedInstance/SingletonClass so that it is allocated & assigned once and does not get re created with new set of values.

Correct time to delete persistent store to delete all entries of an entity

I am using an UITableViewController containing a UITableView and an UISearchTableView.
The table lists e.g. 1000 entries. I want to provide the user a button to delete all
entries of a specific entity. Because looping over all managed objects and saving the context takes a very long time, I thought of deleting by removing the persistent store and rebuilding the stack.
Now to my questions: How can I do this in the UITableViewController? I don't want to restart the app for deletion so I want to move the code from the app delegate.
To delete the persistent store, you need to close down the Core Data stack, delete the persistent store and then rebuild it the stack.
I would just use the tableview's beginUpdate method to freeze the table, then nil out the fetchedResults controller, then the Core Data stack. Delete the persistent store then rebuild the stack and create a new fetched results controller. Then tell the `endUpdate' and then tell the table to reload data. That should all take brief time unnoticeable by the user.

Core Data: When and where are Entities loaded in the first Place?

I have a question about Core Data. When starting my appliction, when is my data (which is stored automatically by Core Data) loaded into the NSArrayControllers? I want to modify it in the first place before the user can interact with it.
To be more specific: I have an NSArrayController for the entitity Playlist. Before the user can add new playlists or interact with the app at all, I want to modify the playlists programmatically. I tried windowControllerDidLoadNib: in my NSPersistentDocument (MyDocument.m) and awakeFromNib both in my NSPersistendDocument and the NSArrayController, but when I check in these methods with [[myArrayController arrangedObjects] count] I get 0 as result (the array controller's content is empty).
However, I actually have data stored and it is displayed to the user. I just do not know when and where I can modify it in the first place.
Thank your for any help.
Data is never "loaded" into the NSArrayController. The array controller is not an array itself. It does not contain or otherwise store data.
Instead, the array controller queries the object it is bound to for specific pieces of data only when that specific data is needed. This is especially true of Core Data in which managed objects are only fully instantiated when their attributes are accessed. The array controller moves data from an array type data structure to another object (usually an UI element.)
If you want to modify an existing store before it displays in the UI, you need to process the data before the array controller used by the UI is even initialized. If you're using NSPersistentDocument, then you can override readFromURL:ofType:error: to fetch and modify all your objects when the document is first opened. Alternatively, you can override the window controller's windowWillLoad or showWindow methods.
Regardless of where you do it, you must fetch all the managed objects you want to modify. You could programmatically create an array controller to do this but a fetch request is easier to micro manage if you have a large number of objects to modify.
You could try observing the "arrangedObjects" keypath of the controller and adding some logic to work that your array controller has been populated for the first time.
Another possible hook is implementing the awakeFromInsert/awakeFromFetch methods of your managed objects.

NSMutableArray removeAllObjects issue

Here is my scenario:
I am building a location finder using the iPhone mapkit. I have an array stored in the application delegate to hold the information about the location of a store (name, address, etc.). When a certain button is pressed, a view slides in with a textfield and a button which performs a lookup of the users input, and returns all of the necessary information.
All of this works fine and the points get plotted onto the map. However, if I go and try to do a search a second time, the application crashes. I am trying to remove all of the objects from the array when the xml parser begins:
- (void)parserDidStartDocument:(NSXMLParser *)parser {
[dataTempForSearch removeAllObjects];
}
and the debugger simply puts an arrow on the method call with no real explanation as to why...
Has anyone run into a scenario like this before? any thoughts as to why this might be happening only on the second time the action is performed?
It's hard to tell based on just that one line of code.
It's probably a memory management issue, but what specifically I can't say.
Shots in the dark:
I would destroy and re-create my parser each time I did a search.
I would also clear the dataTempForSearch immediately after passing the data to the app delegate, not right when you go to do another search.
MapKit has some very nasty problems. When you get that odd behaviour of the debugger putting an arrow at that line, take a look at the call stack provided to you (this can be seen in debug mode on the left side usually). I'll bet you it's to do with MapKit.