How to manage state in ios objective c - ios

I’m new in iOS development. My question is, I’ve two view controllers.
viewController - A viewController - B
Now, if i killed the app from the viewController - A and than relaunch the app. than app must be open the viewController - A. and if i killed the app from the viewController - B and than relaunch the app. than app must be open the viewController - B.
Can anyone help me, I’ve done the RND but can not find the proper solution.
Thanks

Create a sharedDelegate in AppDelegate.m file
+(AppDelegate *)sharedDelegate {
return (AppDelegate *) [UIApplication sharedApplication].delegate;
}
in AppDelegate.h
+ (AppDelegate *)sharedDelegate;
#property (nonatomic, strong) NSString *currentViewContoller;
when push to any contoller then set AppDelegate's currentViewContoller to new VC
YourViewController *vc=[[YourViewController alloc] init];
[self.navigationController pushViewController:vc animated:YES];
[AppDelegate sharedDelegate].currentViewContoller = NSStringFromClass([YourViewController class]);
now when app is terminated
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
[[NSUserDefaults standardUserDefaults]setObject:[AppDelegate sharedDelegate].currentViewContoller forKey:#"currentVC"];
}
now when app launched first time check previous controller when app terminated
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSString *string=[[NSUserDefaults standardUserDefaults] valueForKey:#"currentVC"];
and push this class
UIViewController *object = [[NSClassFromString(string) alloc] init...];
}

applicationWillTerminate you can use but it will only get called if user quit app from forground If your app is in background and then user quit app then applicationWillTerminate will not get called.
So, you have to take care of applicationDidEnterBackground.
So, when app enter in background (i.e call applicationDidEnterBackground ) or call applicationWillTerminate save state(your current VC) in your user defaults.
Now in your didFinishLaunchingWithOptions set that view controller as rootviewcontroller or whatever way that you want to manage it.
reference : Apple documentation for applicationWillTerminate
PS : You should not manage app like this. It is horrible way!! If possible then run your app as normal flow!

If you're using Storyboards you can use the Restoration Identifier to communicate the App which controller to launch as first
https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/PreservingandRestoringState.html

Related

How to remove application from recent apps in Objective C IOS [duplicate]

This question already has answers here:
Prevent iOS from taking screen capture of app before going into background
(10 answers)
Closed 3 years ago.
I am new to IOS development
I want to remove my application from recent apps which is developed in Objective C.
I tried UIApplicationExitsOnSuspend in
info.plist
, but no luck still application is showing in info.plist.
Can anyone help me on this.
Thanks in Advance !!!
You could use concept of the cover window.
When app will resign active state you show your cover, and system will take snapshot of that cover instead of last visible UIViewController.
When app will become active you hide and deallocate your cover window.
Here is example
#import "AppDelegate.h"
#interface AppDelegate ()
#property (nonatomic) UIWindow *coverWindow;
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
self.coverWindow = UIWindow.new;
self.coverWindow.rootViewController = UIViewController.new;
[self.coverWindow makeKeyAndVisible];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[self.coverWindow removeFromSuperview];
self.coverWindow = nil;
}
#end
UIApplicationExitsOnSuspend is deprecated. You shouldn't use it any more. There has been reports of apple rejecting apps with that key. As per apple:
Deprecated
The system now automatically suspends apps leaving the foreground when
they don’t require background execution. For more information, see
About the Background Execution Sequence.
So for now, you are stuck with letting apple handle the background state of apps. Forcefully trying to exit the app by any manner would lead to a rejection from App Store.
UPDATE
I just noticed your comment saying what you actually want. To prevent the Background Snapshot, you can add a custom view to the window. This is similar to the answer posted by Mark Agranal below, but the thing is you don't need to add a new Window or new ViewController. You can simply add a custom view to the window and remove the view when the app reenters active state. In your AppDelegate:
// The view to use as a mask
#property (nonatomic, weak) UIView* coverView;
// Add the view to window
-(void)applicationWillResignActive:(UIApplication *)application
{
coverView = [[UIView alloc]initWithFrame:[self.window frame]];
[self.window addSubview:coverView];
}
// Remove the view to window
- (void)applicationDidBecomeActive:(UIApplication *)application
{
if(coverView != nil) {
[coverView removeFromSuperview];
coverView = nil;
}
}
Note that you can add any view to the window using the above method. The system will take screenshot of the added view and hence the sensitive user data will be protected.

UILocalNotification doesn't trigger didReceiveLocalNotification when tapping from Notification Center

I'm using Objective-c to create a UILocalNotification in my iPhone app. I'm targeting iOS 8 and using XCode 6.
My problem relates to handling UILocalNotification when app is not running and it's opened by tapping on a notification. When the user taps the notification and opens the app I use didReceiveLocalNotification in AppDelegate.m to show a particular View Controller and send the VC some data (a date).
This works fine when tapping the notification from the lockscreen. When tapping the notification in the Notification Center didReceiveLocalNotification is never called. I used a UIAlertView to test this on my device. didFinishLaunchingWithOptions is called, but when I include the code to show the particular View Controller in that method it never works (though another UIAlertView showed me it was running that part of the code, just never completing the showViewController method).
Here's my didFinishLaunchingWithOptions code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([UIApplication sharedApplication].scheduledLocalNotifications.count >= 1) {
// Handle local notification received if app wasn't running in background
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if ([[notification.userInfo objectForKey:#"notification"] isEqual:#"mood rating"]) {
// Create reportVC
NSLog(#"launched app and about to show reportvc");
ReportViewController *reportVC = (ReportViewController *)[self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"reportVC"];
// Date stuff goes here - code removed
// show the reportVC
[self.window.rootViewController showViewController:reportVC sender:self];
}
} else {
[self createLocalNotification];
}
return YES;
}
And here is my didReceiveLocalNotification code:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
// Create report view
ReportViewController *reportVC = (ReportViewController *)[self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"reportVC"];
// Same date stuff goes here as in didFinishLaunchingWithOptions
// Show report vc
[self.window.rootViewController showViewController:reportVC sender:self];
}
The date stuff that I've take out is just checking if it's after 9pm or not, and either creating today's date or yesterday's, and then setting the result as a property of the reportVC. Let me know if that's relevant and I'll add it back in.
So here's some stuff I've tried to fix this:
I've tried using presentViewController:animated:completion: instead of showViewController:sender: but I want to use showViewController so I can have the navbar show up, and that didn't fix the problem anyway.
I've tried adding this line to my didFinishLaunchingWithOptions method:
[self application:application didReceiveLocalNotification:notification];
which did kind of fix the problem—when tapping from Notification Center it opened the right view, but it ruined lockscreen notifications. With this line added, when tapping a notification from the lockscreen I got the reportVC presented twice: the first one was a black screen apart from the navbar, and the one on top of that was correct.
I tried replacing my current showViewController line of code with this instead:
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
[topController showViewController:reportVC sender:self];
but that didn't fix the problem either.
I tried taking the code I've doubled up on out of my didFinishLaunchingWithOptions method, because I realised didReceiveLocalNotification seems to be run anyway once didFinishLaunchingWithOptions is done. That does seem to be the case, but it doesn't fix my problem with Notification Center notifications.
And in case this is screwing it up, here's how I'm testing this at the moment:
I add these two lines to the didFinishLaunchingWithOptions method:
[[UIApplication sharedApplication] cancelAllLocalNotifications];
[self createLocalNotification];
I change the scheduled time for the notification to be about 3 minutes into the future. It's normally set to go off at 9pm every night using date components like this:
dateComponents.hour = 21;
dateComponents.minute = 0;
And I change the notification's repeatInterval to be NSCalendarUnitMinute instead of NSCalendarUnitDay which is what it's set to for release builds.
Then I run the app on my device using XCode, and stop it once it's run and scheduled the notification. I run it again without these two lines:
[[UIApplication sharedApplication] cancelAllLocalNotifications];
[self createLocalNotification];
And then stop the app from XCode, open multitasking on my device, swipe the app up to close it, and wait for a notification to arrive. After tapping each test notification I multitask and close the app again so I can test from a totally closed app each time.
You may want to try this (present your view controller asynchronously with the dispatch_async()):
if ([[notification.userInfo objectForKey:#"notification"] isEqual:#"mood rating"]) {
dispatch_async(dispatch_get_main_queue(), ^{
// Create reportVC
NSLog(#"launched app and about to show reportvc");
ReportViewController *reportVC = (ReportViewController *)[self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"reportVC"];
// Date stuff goes here - code removed
// show the reportVC
[self.window.rootViewController showViewController:reportVC sender:self];
});
}
or this (call [self.window makeKeyAndVisible] before presenting your view controller):
if ([[notification.userInfo objectForKey:#"notification"] isEqual:#"mood rating"]) {
// Create reportVC
NSLog(#"launched app and about to show reportvc");
ReportViewController *reportVC = (ReportViewController *)[self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"reportVC"];
// Date stuff goes here - code removed
// show the reportVC
[self.window makeKeyAndVisible];
[self.window.rootViewController showViewController:reportVC sender:self];
}

iOS UIBackgroundMode remote-notification doesn't work on 4G

I'm testing push notifications with content-available=1, and they don't seem to be delivered to the app in the background unless on Wi-Fi.
I have a simple log statement at the beginning of the push notification handler:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^) (UIBackgroundFetchResult))completionHandler {
NSLog(#"Notification received: %#", userInfo);
completionHandler(UIBackgroundFetchResultNewData);
}
Here is my test:
Run the app, then press the home button to put the app in the background.
Send a push notification with content-available=1
Watch console logs
On Wi-Fi, the console log shows the notification. If I go to Settings and turn off Wi-Fi, switching to 4G, notifications no longer appear in the log (although they do slide in at the top of the screen, so I know they are being delivered).
There are no crash logs, and the notification is logged if I manually tap on it. Furthermore, this problem does NOT occur if I am debugging the app in Xcode. (i.e., if I am debugging in Xcode, the app will receive the notification in the background on 4G). Has anyone else experienced this behavior? Or am I doing something wrong?
EDIT:
To be specific: according to my tests, if the following conditions are true, then the remote notification delegate method above will not be called:
App is running in the background
Phone is on LTE network, not connected to Wi-Fi
App is NOT running in the Xcode debugger
Notification with content-available=1 is received by the phone
However if condition 2 is removed (i.e., the phone is connected to Wi-Fi), then the handler will be called.
Try the following code:
// AppDelegate.h
#class ViewController;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
{
NSString *DeviceToken;
NSMutableDictionary *App_Messages;
NSString *Longitude,*Latitude;
NSMutableDictionary * badge;
}
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) ViewController *viewcontrollervc;
#property (strong, nonatomic) UINavigationController *navcontroller;
#property (nonatomic,retain)NSMutableDictionary *badge;
#property (nonatomic,retain)NSString *DeviceToken;
 
 
// AppDelegate.m
#import "ViewController.h"
#implementation AppDelegate
#synthesize badge,DeviceToken;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc]initWithFrame:[[UIScreen mainScreen]bounds]];
self.viewcontrollervc = [[ViewController alloc]initWithNibName:#"ViewController" bundle:nil];
self.navcontroller = [[UINavigationController alloc]initWithRootViewController:self.viewcontrollervc];
self.window.rootViewController = self.navcontroller;
self.navcontroller.navigationBarHidden = YES;
//Notification
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
NSDictionary * remoteNotificationObj = [launchOptions objectForKey:#"UIApplicationLaunchOptionsRemoteNotificationKey"];
if (remoteNotificationObj)
{
[self performSelector:#selector(handleRemoteNotificationWithUserInfo:) withObject:remoteNotificationObj afterDelay:3.0];
}
[self.window makeKeyAndVisible];
return YES;
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
[self handleRemoteNotificationWithUserInfo:userInfo];
}
-(void)handleRemoteNotificationWithUserInfo:(NSDictionary *)userInfo
{
NSLog(#"userInfo - %#",userInfo);
NSDictionary *alertData = [userInfo objectForKey:#"aps"];
NSDictionary *returnDatalert=[alertData objectForKey:#"alert"];
NSString *alertmsg=[returnDatalert objectForKey:#"body"];
NSLog(#"alertmsg %#",alertmsg);
self.badge = [NSMutableDictionary dictionaryWithDictionary:[alertData objectForKey:#"badge"]];
NSString *notificationtype=[badge objectForKey:#"fnct"];
NSLog(#"%#",notificationtype);
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSLog(#"didRegisterForRemoteNotificationsWithDeviceToken: %#", deviceToken);
NSString *dt = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"<>"]];
dt = [dt stringByReplacingOccurrencesOfString:#" " withString:#""];
self.DeviceToken=dt;
NSLog(#"~~~~devToken(dv)=%#",deviceToken);
}
- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
{
NSLog(#"Failed to get token, error: %#", error);
}
Based on feedback from a commenter here and repeated testing on multiple devices, this appears to be a bug (or intended behavior) on iOS.
For me, it worked on Wi-Fi and on 4G (LTE forced off in cellular settings), but did not work on LTE.
Update: After extensive debugging, I've found this issue related to two things for me when on LTE. One is power. I discovered if the iPhone was plugged into the wall, the app was woken up as expected. If it wasn't plugged in, the app was not woken up in response to content-available = 1. Second, was device settings. Even though every related setting was set correctly, doing a 'Reset all Settings' fixed the issue for me.
Assuming this is not an Apple bug, my guess is as iOS develops a power profile for a given app identifier, it opts, under certain circumstances (network status, battery status, etc) to not wake up apps that use excessive background cycles. For example, using beginBackgroundTaskWithExpirationHandler incorrectly, causing an app to stay active in the background and forcing iOS to expire it. Even fixing excessive background usage might not correct the issue, as iOS has already determined your app is a background hog. This would explain the 'Rest all Settings' clearing up the issue for me.
Unfortunately, all of this is just a guess based on 2-3 days of debugging this issue and we will probably never know for sure as there are so many variables in play with push notifications, not to mention vague and varying documentation.

ios simulator black screen with status bar

I am new to Objective-C and I'm making my first app, a single-view app. When I run my program ,the fullscreen ad that is supposed to appear (I'm using RevMob) appears. However, when I exit the ad I get a black screen with a blue status bar at the top.
I have tried many things, such as setting my main view controller as initial view controller, restarting my computer, changing/removing debugger, resetting the iOS simulator, etc.
My Xcode version is 4.6 and my OS is mac OSX 10.8.4
I don't want to delete Xcode and I also don't want to remove ads because that is my only source of income.
Here is my code:
Appdelegate.m
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
[RevMobAds startSessionWithAppID:#"myappid"];
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
RevMobFullscreen *ad = [[RevMobAds session] fullscreen];
[ad loadWithSuccessHandler:^(RevMobFullscreen *fs) {
[fs showAd];
NSLog(#"Ad loaded");
} andLoadFailHandler:^(RevMobFullscreen *fs, NSError *error) {
NSLog(#"Ad error: %#",error);
} onClickHandler:^{
NSLog(#"Ad clicked");
} onCloseHandler:^{
NSLog(#"Ad closed");
}];
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
- (void)dealloc
{
[_window release];
[super dealloc];
}
#end
Appdelegate.h:
#import <UIKit/UIKit.h>
#import "ViewController.h"
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic)UIWindow *window;
#end
There is nothing wrong with the ad code. What seems to be (not) happening is that your app has no content. I suspect that you haven't set up the rootViewController (initial view controller) for the app correctly.
This is what you need to do:
Get rid of all of the app ad code and just make an app that works. All it has to do is correctly show an initial view controller. You could just make a new project in XCode using the Single View Application template.
Add your app code as per this example. You will invoke the code from your initial view controller via a button.
Once that is working, you can add the code into - (void)applicationDidBecomeActive:(UIApplication *)application as per your question. When you dismiss the advert, you should see your initial view controller.

Using Array from ViewController in AppDelegate

I have an array stored in the ViewController.m. But when the app enters background, i want to post notifications from the array.
so my NSMutableArray "list" was created in ViewController.m but i need to use in AppDelegate.m
- (void)applicationDidEnterBackground:(UIApplication *)application
for (NSString *thing in list) {
UILocalNotification *notif = [[UILocalNotification alloc] init];
notif.alertBody = thing.text;
[[UIApplication sharedApplication] presentLocalNotificationNow:notif];
If ViewController.m is your main VC:
ViewController *yourVC = (ViewController*)self.window.rootViewController;
yourVC.yourMutableArray = whateverYouWant;
I recommend saving the data inside of NSUserDefaults however, then you can just easily access it and read/write anywhere. BTW the presenting of localNotifications from that method in the appDelegate is a bad idea. The app store will not be a fan of immediate notifications when someone tries to leave an app..if you're even able to.

Resources