I've recently come to learn that NSFetchedResultsController is an extremely buggy class and its been causing me headaches for a while now with my rather large Core Data app.
Would it be appropriate to use an NSMutableArray to feed the table view instead of an NSFetchedResultsController? What I'm talking about is, temporarily creating a fetched results controller to grab the objects from my managed object context, creating a new NSMutableArray with the fetchedObjects from the fetched results controller, then using that to feed my table view.
Are there any benefits to using NSFetchedResultsController directly over an NSMutableArray to supply data to my table view?
You don't have to use a fetched results controller. It is a new convenience class and you can always do things old school.
I presume that because you want to use mutable array that you intend to use add and remove objects. This is not a problem except you have to everything manually. If something else modifies the data, you have to register notifications to monitor the changes and refetch as needed.
Related
I'm new to iOS & Cocoa. My question isn't about how to make something work, but more about design to improve UX & performance.
I'm adding features to an existing app. Currently, the app has one class "RootViewController" (RVC) that is responsible for making server requests. RVC calls a server for a json response. This json response is parsed and the parsed response is referenced by an NSArray object called "array". The data that the server gives to "array" must be updated regularly because it represents live inventory that other customers could buy.
I need to use the reference to "array" in other classes at different times during the lifetime of the app. I don't want to call the server every time I want to use or update "array". When testing this app on my own device, it seems like calling the server can be slow -> hurts performance of the app.
I've considered creating a class that could act as a delegate to keep a reference to an NSArray - sort of acting like a global variable. I'd make an asynchronous request to the server and keep up with the response in this delegate class. I'm not sure how to determine whether this approach is efficient or considers best practices (with MVC in mind).
I'm looking to find out the best place to store "array" so that other classes may use it quickly without depending too much on the network or memory usage. "array" must be able to be updated occasionally from the server (as the "model" may change from inventory changes) . Based on my research, iOS's CoreData seems like the best place to start, but I'm not sure how to update CoreData regularly if the app is not active. In other words, I don't want to present the user with stale data if the data in CoreData hasn't been updated recently.
The json responses are about 20KB - 45KB.
Where is the best/alternate place to store light weight objects so that they can be updated regularly? I'm leaning toward the session style variable, but I don't know if there is a better way to do this.
To look at this according to MVC, you have two parts:
An array – this is the model
The code which fetches the inventory from the server – this is the controller
This controller code is really the model-controller, not the view-controller. I wouldn't put it in a view controller class. You could put it in your app delegate if the code is very simple, but I'd recommend putting it entirely in its own class.
In -applicationDidFinishLaunching:
[[InventoryController sharedInstance] reloadContent];
[[InventoryController sharedInstance] scheduleUpdates];
InventoryController.h
#interface InventoryController
#property (retain) NSArray *inventory;
#property (retain) NSTimer *reloadTimer;
+ (InventoryController *) sharedInstance;
- (void) reloadContent;
- (void) scheduleUpdates;
#end
InventoryController.m
#implmentation InventoryController
- (void) reloadContent {
...
}
+ (InventoryController *) sharedInstance {
static InventoryController * singleton;
if (!singleton)
singleton = [[self.class alloc] init];
return singleton;
}
- (void) scheduleUpdates {
self.reloadTimer = ...;
}
#end
Everywhere else:
NSArray *inventory = [[InventoryController sharedInstance] inventory];
In -reloadContent, you should pull the content from the server. In -scheduleUpdates, you should set up a timer which acts on the controller, causing it to periodically reload data. If your view controller needs to adjust its behavior when the data is stale, store an NSDate alongside the array, add a method like -isStale which checks the dates, and invoke that first.
Remember to load URLs in the background. You shouldn't stop and reload data while you're handling an event or action, so in essence, your view controller needs to return from the action method while it's waiting for data, and adjust its display when you get the data back.
If a view controller needs to respond once the data is refreshed, have the view controllers register for a notification which you can post when your inventory controller finishes updating its content:
[[NSNotificationCenter defaultCenter]
postNotificationName:InventoryControllerDidReloadContent
object:self];
If you want to cache inventory data on the device so you can remove it from memory when the app goes into the background, you might do that by writing the array to a property list, but if stale data isn't useful, you may not want to bother.
You could use Core Data in place of the array and the property list, but it doesn't remove the need for a controller which loads the data from the server and loads it into the context. Instead of having an array, you'll probably have have a managed object context and a fetched results controller. If you're not editing that content in your app I doubt Core Data will provide any benefits over an array and property list.
I'd recommend storing this in your application delegate. It works well to store an array and can be accessed from any class in your app.
If this "array" has interesting structure CoreData is a nice choice. In this case I'm guessing CoreData and a plist are going to be about as fast as each other. Use whichever seems simplest and if it is too slow try the other.
Try not to need to update the data while the app isn't running. Store the last update time with your cached data. If it is "too old" display a "please wait" placeholder and update it. If it is "old but not too old" display what you have and update it in a non-main thread. Or try other variants of that UI ("too old" == notice + read-only mode).
If you do find a trick to updating in the background that gets past app review, think long and hard about how much of your user's battery this will eat. Add a way to disable it. Or have it disabled by default and add a way to enable it. Or just don't do it. :-)
I'm new to iOS. After going through a lot of and documents i'm confused .This is what I have to do.
I have several view controllers each has NSString values which I'll enter in a textfield and save it in a common place and when I need to view the data, it would be displayed in aUITableview. I know how to create a UITableview and load the data in it. But I have to know how to save and load that.
So far I have tried somethings . If I enter a new field, the old contents are overwritten.I don't know where to start..Can anyone give me step by step logic(not code). So that I can follow that.
I tried using NSdefaults but its not efficient as i expected
You can save the data in multiple ways
Use CoreData to save the data. You will find some good tuts on how to use CoreData
Use an SQLite database without CoreData
Save data into your app delegate or a view controller accessible from the final view controller
Pass the data from all viewcontrollers to the final viewcontroller
Save data in a plist
The possibilities are endless. What works best for your project is what you should use.
1-you can save common data with your app delegate interface . then you can access it from others interfaces
2-you can use NSUserDefault to store data with keys
3-create NSString object and with passing from view to another one pass data to new NSString object
There are two options available storing and retrieving data in different view controllers.
1)NSUserDefaults is best option for storing data and accessing in any other view controllers.
The NSUserDefaults class provides convenience methods for accessing common types such as float, double, integer, Boolean. A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary.
This is very easy and best method for storing and retrieving data.
if you want to read about NSUserDefaults, here i am sharing document.
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/Reference/Reference.html
2) You would create properties when you want them to be accessible outside the class or other view controllers.
Create property in this way. #property (nonatomic, retain) NSArray *arrayData; and then you can use this array value in other view controllers also.
Properties replace the accessor methods for objects.
using a db is better choice here. But if you want those data only dynamically then u can save it in a array and get those common data by delegates.
I'm building an app with two tabs. The first tab has a main tableview that connects to a detail view of the row. The second tab will display a tableView based on the user adding content to it by tapping a button on the detail view.
My question is this. What is the correct design pattern to do this? Do I create a second ManagedObjectContext/ManagedObjectContextID then save that context to a new persistent store or can the MOC be saved to the existing store without affecting the original tableview?
I've looked at CoreData Recipes and CoreData Books and neither deal with multiple stores although books does deal with multiple MOC's. Any reference would be great.
A single NSManagedObjectContext is more than sufficient for this design. What you want to do is implement the dependency injection design pattern. What this translates to is that when you create each of your tabs, you pass the singular NSManagedObjectContext instance into each of them. Each NSViewController is then responsible for accessing the NSManagedObjectContext as needed.
update
If you are seeing the same data then that is an issue with your 'NSFetchedResultsController' and not the 'NSManagedObjectContext'. The 'NSManagedObjectContext' has access to all of the data, the 'NSFetchedResultsController' is what does all of the filtering based on it's 'NSFetchRequest'.
Perhaps you should post the 'NSFetchedResultsController' for each of your controllers (by editing your question here) so that we can see what is going on.
update
The MOC does not get destroyed, at all, ever. You simply have more than one reference/pointer to the same MOC. The MOC is the scratchpad for the NSManagedObject instances that you are accessing. When you call -save: on that MOC, it takes the changes in that scratchpad and persists them out to disk.
Except in some very, highly unusual, situations, you only ever need one MOC per thread. In your design that you have described so far, one MOC is more than sufficient.
I'm new to this Core Data business.
I got a UITableViewController hooked up with a NSFetchedResultsController. In viewDidLoad, I fire a request to get necessary data from the server, then use [self.fetchedResultsController performFetch:&error] to update the table view. All works fine until that point.
Now I want to move the data fetching stuff to another thread, so after the app received a NSArray object from the server, it performs the didFinishFetchingItems selector on the main thread. In that selector, I save the NSArray to the Core Data store and have the fetchedResultsController perform a fetch. No data show up, although a NSLog reveals that the data is still there (e.g. [[fetchedResultsController fetchedObjects] count] returns 100). I have to put a [self.tableView reloadData] at the end of the method to refresh the table view manually.
My question is: What have I done wrong? Why did I need to do the table view refreshing manually?
You should not touch your NSFetchedResultsController in a non-main thread, that is not a thread-safe operation.
If you are having a long delay on fetching then you need to do a background fetch using a separate NSManagedObjectContext. If you perform a separate fetch in the background it will load the data into cache and then the NSFetchedResultsController will hit the cache instead of disk, speeding up retrieval on the main thread.
You do not need to refresh anything manually; fetchedResultsController does that for you.
What you need to do is to implement NSFetchedResultsControllerDelegate for some object, and set that object as the delegate for your fetchedresultscontroller.
See this about what you need to implement. If your model is simple, you can pretty much copy-paste that code into your delegate and everything works.
The important thing is to keep both the resultscontroller and other pieces of code working against the same managed object context. This is how the resultscontroller can pick up the changes. But, in Core Data guide, there are some caveats about multithreading, so make sure you have your threading bases covered and then all works.
My application has a simple data model with 70 read-only records grouped into six categories. Currently the persistent data is stored in an XML file which I parse on application launch, and create objects for each record which are stored in an NSMutableArray.
I then populate a UIPickerView from the objects stored in the array, and when the user selects a row in the picker, display content pulled from the same objects.
Using CoreData and SQLite would use much less code, but it seems to be designed to work with UITableViews exclusively.
Has anyone used CoreData to fetch records outside of the UITableView interface?
Thanks
jk
Core Data is not designed to work with UITableViews only. Although it's really easy to Core Data with the NSFetchedResultsController to populate a table view you can still do all the fetches you want on your an.
Just make your own NSFetchRequests to get the data you want. From the list of objects, particular objects or just single values you can use this date for whatever purpose intended.
I put my custom Core Data methods into the application delegate, where they can be called from any class. To do this, I add a #define as follows:
#define UIAppDelegate ((MyAppDelegate *) [[UIApplication sharedApplication] delegate])
I can then call my methods like so:
[UIAppDelegate fetchBooks:managedObjectContext];
A UITableView is not required for any of this.