I try to explain my problem. In appdelegate I have to select a rootViewController depending on the result of an asynchronous request (I'm using AFNetworking framework). In fact, I need to know if my user is profiled or not: if he is profiled I can show him app's Home, if he is not I have to show him a profilation view.
In storyboard I set the Home view as the designated entry point, but in this way this view is always shown until the asynchronous request is completed. Is there a way to make appdelegate wait for the response?
I think there is't good solution to let app delegate wait for the response because if the network connection will be poor the app loading time will be very long and OS could kill your app or user can turn it off.
You can add some loading view controller (with animation so user will know that the app is doing something) instead of home one and when you receive the response present appropriate view to the user (modal segue could do the job).
Hope this help
A better solution is to use splash screens. That is when your app gets loaded in AppDelegate, create and push a splash view controller. Which would just contain a single UIImageView covering whole screen showing your application splash image. Upon asynchronous call completion, pop that splash view controller and push your required view Controller.
Many apps use this way to download necessary asynchronous data before showing the app. So that user don't see empty screens or garbage data.
If something gets failed like internet connectivity failure or server response error, etc., Show error to user and perform error handling according to your app logic.
You can programatically navigate to the root view controller as
[self.navigationController popToRootViewControllerAnimated:YES];
This code can be put in the condition of result.
Or in your way, I think you are created a segue for navigation to the rootViewController. You can programatically perform a segue using
- (void)performSegueWithIdentifier:(NSString *)identifier sender:(id)sender
If you are using the AFNetworking, just add a method in the success block and pass the response to that method in a parameter of dictionary. Check your response in the method and choose the controller which you want to make make the root view controller from that method.
Related
One of the tabs in my app presents blog posts. I notice that when I move to another tab or leave the app that when I return, new blog posts are not downloading. The download is kicked off by viewDidLoad() in the ViewController. It's not firing when I return to this view.
Why isn't viewDidLoad() firing when I leave the app? How long does the app remain view loaded in memory?
How should I check for new posts when the user comes back to the app or from another tab?
Thanks!
viewDidLoad may not be the best place to download the updates. If for example you push from ViewController A -> ViewController B, the first view controller (A) isn't unloaded.
You may want to put the code in viewDidAppear or viewWillAppear.
Look at ViewController LifeCycle for some reference.
You can use the applicationDidBecomeActive notification to trigger updates or whatever else you want your app to do when it is brought back from the background.
There is a good answer on this here: How can I use applicationDidBecomeActive in UIViewController?
There is also a good article on Apple's website about handling transitions from various app states here: https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/StrategiesforHandlingAppStateTransitions/StrategiesforHandlingAppStateTransitions.html
ViewDidLoad is only called once when the ViewController is instantiated. In a UITavBarController, the child view controllers are only instantiated once. As you move from tab to tab, the ViewControllers are kept in memory.
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITabBarController_Class/
If you background the app, then iOS will keep it in memory until it starts to get low, then starts to terminate apps.
Take a look at the delegate for the TabBar
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITabBarControllerDelegate_Protocol/index.html
This can tell you specifically when the user switches tabs. But if they flip back and forth, it could needlessly create several API requests.
I wonder if dealloc is always called when you close a ViewController in Objective-C for iOS. I've done some tests and it seems like it. Except I don't get any logs when I swipe close the application. I figured that maybe XCode doesn't log things if you fully close the app like that.
The reason I wonder this is because I'm sending analytic data when one of the ViewControllers closes and I kinda need to know if dealloc is always called or if there's any better way doing this.
If you want to get notified when app is killed via Swiping , you can use applicationWillTerminate method and in this method , you can check for current view controller in navigation stack and send analytical data for that View controller .
It's not defined when dealloc will be called for a view controller (or pretty much any class), therefore don't use it for sending analytics.
Use viewDidDisappear instead, which you can rely on to be called when the view controller has been removed.
The Background
I have an iOS application I am building with StoryBoard for the UI and AFNetworking for the back end. On my UIViewController I have some text boxes. When the user hits a submit UIButton I am making the AFNetworking call to a web service which returns an XML file.
The issue
I used the StoryBoard interface to hook up the button to a UITableViewController. Initially this worked fine because I had the code for the web service call inside the UITableViewController. The problem is that if the web service returns nothing I get a blank UITableView. So I moved the web service call into the UIViewController to be called when the button is pushed. OOPS! I make the web service call and the prepareForSegue gets called before the web service returns the data since its Asynchronous.
My Question
What is the best approach when getting data from a web service when you are using storyboards. Should I make the call inside the UITableViewController and then go back to the UIViewController if there is no data or an error returned from the service. If so how do I get back. I have tried self.navigationController popViewController but it comes up with a warning in the console that removing a view controller can mess up the stack.
Is there a way that I can control when the segue is called and only do it once I have data back from the web service?
Posting this step by step answer to help anyone else out that may need help with this:
Connect the two ViewControllers in Storyboard which will create a segue.
You simply select the first view controller (make sure the viewController is highlighted with a blue line and you didn't select like an image view or navigationItem. Then CTRL and click on the destination view controller (where you want to transition to).
The round circle in the middle is called the segue. If you click on that and go to your attributes panel on the right, you will see this:
In the identifier enter a name you want to call this transition: for example myNewPage.
Whenever you are ready to make the call to the next scene. Use this delegate method to do pass data from one controller to another or do anything special:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NameOfDestinationController *dataViewController;
dataViewController = segue.destinationViewController;
if ([segue.identifier isEqualToString:#"nameOfYourSegueIdentifier"]) {
dataViewController.variable1 = self.variable1;
...
}
}
Then in my case I needed to trigger the segue after I got a success from the AFNetworking web service call. So in the Success block I added this line:
[self performSegueWithIdentifier:#"nameOfYourSegueIdentifier" sender:self];
It works perfectly now!!!
Let's say I have a relatively complex storyboard with a login screen guarding access to a UITabBarController containing a couple of embedded UINavigationControllers and their children, like so:
The LoginViewController is the root VC, and decides if the user is logged in, either by checking stored credentials or asking for fresh ones. It then presents the UITabBarController as a modal.
There are four scenarios:
1) If the user starts the app from cold, they should see the ListViewController.
2) If the app is started from cold via a push notification regarding a "Foo", they should go directly to the FooDetailViewController.
3) If the app is warm-started via a push notification regarding a "Foo", they should go directly to the FooDetailViewController, no matter what VC they were looking at when the app was backgrounded.
4) If the user warm-starts the app, they should go back to whatever VC they were looking at when the app was backgrounded.
Where does the logic for all of this decision-making go? If I put it in the AppDelegate, I end up trying to push view controllers onto my hierarchy before they exist (Warning: Attempt to present ViewControllerX on ViewControllerY whose view is not in the window hierarchy!). The UITabBarController's viewWillAppear: lifecycle method does not seem to be being called on a warm start, and putting logic in every child view controller to check if it's been started from a push seems like coupling things that shouldn't be coupled (and in practice seems to lead to corrupted navigation stacks.)
How does everyone else structure this?
You must create that logic inside UIApplicationDelegate by creating properties needed to decide what is the view controller that has to be opened first.
The first place you have that guarantees the user interface (the window) is ready, is on the application delegate's -applicationDidBecomeActive:, but from here there are two courses of action:
For ListViewController and OtherListViewController is easy, you can just call -setSelectedViewController on UITabBarController;
For FooDetail and BarDetail, I think the best method is putting some logic on ListViewController, sending it a reference to the object you want to show the detail and by performing the relevant segue on ListViewController's -viewDidAppear:.
I'm using storyboard to develop my app, and the initial view controller is a Tab Bar Controller. At the AppDelegate I did some thing work to get the jsonData from my website. It will take 10-20 seconds . You know, I want to do something to notice user is loading, not just showing a default.png picture. How can I do it?
Well, you can have your rootViewController have a loading state (maybe an UIActivityIndicatorView on top of the other views and some public methods like showLoading and hideLoading.
Steps :
In the AppDelegate application:didFinishLaunchingWithOptions: method, build your rootView controller as usual and call your method that loads the JSON files.
Call the showLoading method when you're starting the request
Call the hideLoading method when you receive the response.
Also, remember to use asynchronous request or make it in a different thread to prevent blocking the main thread.