Let's look at a typical RESTful iOS app, let's say a contact app, main screen is the list of contacts, when tapping on a contact you get to the contact detail screen.
The contact list is obtained through a REST API, and the contact details through another API.
Which event would you use to trigger the call to those APIs:
viewDidAppear on both view controllers
viewWillAppear on both view controllers
From the main view controller, call the contact detail API before calling the pushViewController:detailViewController
Any other events?
Currently I am using viewWillAppear mostly for this kind of scenario, or viewDidAppear in some specific cases, but in an effort to standardize my coding practices, I would like to definitely settle on the pros/cons of those various approaches.
It's partly a matter of preference. Since the API call will generate an unknown delay, the app should present UI that indicates it's busy. My preference is to have the UI do as much as possible before the request. (My naive model of cognition is that looking at the new VC's UI while it's fetching data will occupy user's mind for an instant, making the lag seem that much shorter).
So I favor paramaters to the VCs that describe the request - like the id of the contact to be fetched on the detail VC, and do the request on viewDidAppear (if the data isn't already cached or needs a refresh). In that method, put up some UI to indicate the fetch is happening, so it has the form:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (/* i don't have my model or it's out of date */) {
// put up 'i am busy' UI
MyRequestClass *request = // form a request that fetches my model
[request runWithBlock:^(id result, NSError *error) {
// i build my request classes to run with blocks simplifying the caller side
// if it's a json request, then pass a parsed result back to this block
// remove 'i am busy' UI
if (!error) {
// init my model from result
// other parts of this class observe that the model changes and updates the UI
} else {
// present error UI
}
}];
}
}
Firstly, it's good practice to ensure your API interface and data access is happening outside of your view controllers in a separate data access class (or data controller - if you're already doing this then apologies, and ignore this paragraph). You want to avoid putting networking code directly into your view controller, because it's going to make your life very difficult if you either want to create an iPad specific view or need to revamp your UI in some way later on down the line).
With that out of the way, you have several options. In terms of performance from the user's perspective, it's best to pre-fetch as much from your RESTful API as you can. This is what libraries such as AFIncrementalStore that map your API to Core Data try to do. But if you have many thousands of contacts, are heavily rate limited, or bandwidth constrained this is going to be problematic.
What's absolutely certain is that you want to make the call to your networking API as soon as possible so that the user experiences minimal delay. You may find using viewDidLoad rather than viewWillAppear or viewDidAppear may work better in this case: you can set your view up with a loading/holding graphic or animation, trigger your asynchronous networking call, and then once complete display the required information.
I usually do this:
Load the contacts on viewWillAppear, and if you have pull-to-refresh, when that happens.
When user taps on a cell, in the method that handles that event, load event details, and pass that object to the contact details controller's constructor, and push it.
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. :-)
What is the need of delegation in iphone/ipad development or objective C?
I read so many articles on it. All were telling how to implement the concept, but no one was telling why we need to implement that, in which case we should implement it.
Suppose you want to implement Login functionality in your app ... now you won't show Login screen every time you run your app.. only when it is first started and you don't have a login and password...
So in this case..
Your app starts :
View 1 loads (default view )
You check no Login name is there..
You load a new view..(Login View ) .
User enter his details..you get your login and password...
now you want to go back to default view and load the main app with
the names the user entered in Login View....
Now you will use delegate to pass these information(login details) back to default View..so that it knows..its details. now there are many different ways to do these things...like notification and singleton classes.. but when you want to sent more than 3-4 sets of data.. it is best to use delegates
Think of all the components that iOS and Cocoa provide you with. TableViews, TextFields, PopOvers...etc.
When the developers wrote these components, they couldn't possibly know all the various implementations that us developers were going to create using these components. But we need somehow to communicate with them in a generic way.
These components use delegates. The delegate is an implementation independent way of describing some behaviour that your component can conform to.
When UITableView need to find out what is the height of the rows, the UITableView only needs to know about UITableViewDelegate. It doesn't need to know about MyTableViewController, JohnsTableViewController, BobsTableViewController... etc.
So the delegate is decoupling the component from the implementation and the type.
Decoupling is a good thing. It makes maintaing and changing code a lot easier, and makes code reusable.
Delegation is a simple and powerful pattern in which one object in a
program acts on behalf of, or in coordination with, another object.
The delegating object keeps a reference to the other object—the
delegate—and at the appropriate time sends a message to it. The
message informs the delegate of an event that the delegating object is
about to handle or has just handled. The delegate may respond to the
message by updating the appearance or state of itself or other objects
in the application, and in some cases it can return a value that
affects how an impending event is handled. The main value of
delegation is that it allows you to easily customize the behavior of
several objects in one central object.
SOURCE
Use a delegate if you want to talk to only one object. For example, a
tableView has a delegate - only one object should be responsible for
dealing with it.
Use notifications if you want to tell everyone that something has
happened. For example in low memory situations a notification is sent
telling your app that there has been a memory warning. Because lots of
objects in your app might want to lower their memory usage it's a
notification.
this was an answer posted to my question here
There are two key benefits of delegation: customizing objects without subclassing, and improving encapsulation.
Customization without subclassing is a benefit you get from many of the Cocoa and Cocoa-Touch APIs using the delegate pattern. If they didn't do so, you might have to subclass a UITableView every time you wanted to change its behavior by using different types of cells or a different data source. Instead, you just set the table view's delegate and data source to customize its behavior.
As for encapsulation, the delegate pattern helps you keep the different components of your code separate. For example, if your custom View needs to have some data, it would be bad practice to simply give it access to your Model, or even full access to your Controller. Instead, you'd probably set up some kind of delegate protocol for your View that your Controller would implement. That way your classes know no more about each other than they need to, so that changes in one part would be less likely to break others.
I have an app that has many web services and notifications going on. I need a logout feature. I wish there was a way simply to kill the app and restart it but there is not. Does anyone have some recommended guidelines on creating a logout function (it will take the user back to the login screen). The problem is there are notifs that should be unsubscribed from, views that should be removed, view controllers that I want to be released, then everything to reinitialize. That seems like a lot of work for a simple task. Any thoughts?
The first thing to make sure when terminating all requests is to change all delegates that are supposed to receive responses to nil. After you've taken care of this, you should indeed remove all irrelevant view controller's views from your root view (and release them if they are retained anywhere), and of course flush any existing data you don't need. If you design your MVC elegantly, you can achieve these actions without a lot of fuss, for example a ScreenManager Singleton class that manages all your view controllers should have no problem taking you back to the login screen while releasing any other view. A DataManager Singleton class that holds various data collections should have no problem removing any unneeded data entities and so on...
I've read in a number of places that using the AppDelegate as the heart of the application is bad (apart from the actual UIApplication delegates themselves).
Would it be wise to create custom delegate classes to keep code modularized.
For example if I need to format various text inputs, create a Formatter delegate class and set the delegate on any text input object that needs to be formatted?
Does that make sense?
The point of the articles you've read is 'Do not keep all application logic in app delegate'. Responsibility of app delegate is to handle important events in app lifecycle in lightweight way by notifying appropriate parts of application, not to manage views or data flows or network calls. For example in application:didFinishLaunchingWithOptions: delegate usually adding root controller's view into window, makes this window active and leaves everything else to controller.
I'm new to Objective-C and not a full time programmer. I'm beginning to understand the Model-View-Controller design pattern for differentiating the UI from the model. So the user takes an action and the view controller sends a message to the delegate (model). But I'm not sure what the best way to send actions from the delegate back to the view controller.
For example, the user pushes a button, the VC messages the Delegate. That part I understand. Then the delegate takes action, and following that the delegate wants to update the VC (e.g., update a label).
So what I missed (or have forgotten) is how this gets done, while maintaining separation between the UI and the model. I suppose I can use the notification center. Or I think I can just have the view controller pass a callback to the delegate. Or maybe there's another choice I don't know of. Can someone give me a recommendation, please?
I think you're slightly misunderstanding the MVC paradigm. Models should never be delegates of views, since models should have no dependencies or knowledge of any view classes. Typically, a view sends a message to its delegate or target (if you're using target/action), which is usually a controller (often a subclass of UIViewController on iOS). The controller then accesses data from the model and can update any views that need updating. I'd recommend reading the MVC fundamentals guide for a more complete explanation.
Basically you're right, you could do all the notification-related things yourself (i.e. with NotificationCenter) but since we're talking about UI-Stuff here I would greatly recommend you to use IBAction-Methods and IBOutlet-Properties in your code which you can easily connect to UI-Elements respectively their Callbacks in Interface Builder.
A very basic introduction to this topic can be found here:
iPhone SDK Interface Builder basic training
i hope that it is not too basic tough, and that I could lead you on the right track.
First of all delegate is NOT a Model.
Model is something passive that only holds the data (DB, plist, array, dictionary etc.).
While delegate is some set of functions that exist in order to react to some events.
Delegate is more likely to be a view controller in your case.
The view controller should react to user's action.
If the button tap should display some data from your model in some label then view controller should do all the work (receive user's action, take the necessary data from the model and display it on the view...).