3D Touch Home Shortcuts In Obj-C - ios

All of my apps are currently written in Obj-C. The link https://developer.apple.com/library/content/samplecode/ApplicationShortcuts/Introduction/Intro.html#//apple_ref/doc/uid/TP40016545 for the sample code of implementing Home Screen Shortcuts with 3D Touch is completely compiled in Swift. Anyone come across documentation for Obj-C, so I don't have to go through my AppDelegate and translate it all?
UPDATE:
After adding in all the shortcuts in Info.plist, I added in the AppDelegate.m:
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler {
UINavigationController *nav = (UINavigationController *) self.tabBarController.selectedViewController;
NSLog(#"%#", shortcutItem.type);
if ([shortcutItem.type isEqualToString:#"com.316apps.iPrayed.addPrayerRequest"]) {
Requests *gonow = [[Requests alloc] init];
[nav pushViewController:gonow animated:YES];
}
if ([shortcutItem.type isEqualToString:#"com.316apps.iPrayed.addPrayer"]) {
PrayerStats *controller = [[PrayerStats alloc] init];
[nav pushViewController:controller animated:YES];
}
if ([shortcutItem.type isEqualToString:#"com.316apps.iPrayed.addFast"]) {
FastStats *controller1 = [[FastStats alloc] init];
[nav pushViewController:controller1 animated:YES];
}
if ([shortcutItem.type isEqualToString:#"com.316apps.iPrayed.addStudy"]) {
StudyStats *controller2 = [[StudyStats alloc] init];
[nav pushViewController:controller2 animated:YES];
}
}
This allows it to work, without putting any other methods in, or adding anything to didFinishLaunchingWithOptions.

There are two states from where the user can open the app through Quick Actions.
TL;DR
You are always doing the same thing regardless of the state in which the app is when the quick action is done, that's why you only need to override application:performActionForShortcutItem:completionHandler: So if you wanted to do different things, then you would want to handle them in the two places, if not then just the overridden is enough.
One is if the app is killed or not running in background where we get the shortcut info on launch.
The other is if the app is running in background where we get the shortcut info on the new app delegate method.
To handle these Quick Action shortcuts when in background you need to override this method on App Delegate:
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler
And for not running in background (killed) on your
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
or
-(BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
you should check if the app was launched by a Quick Action:
UIApplicationShortcutItem *shortcutItem = [launchOptions objectForKey:UIApplicationLaunchOptionsShortcutItemKey];
(Link to related Apple Documentation)
Quote from the Official Apple Docs
It’s your responsibility to ensure the system calls this method
conditionally, depending on whether or not one of your app launch
methods (application:willFinishLaunchingWithOptions: or
application:didFinishLaunchingWithOptions:) has already handled a
quick action invocation. The system calls a launch method (before
calling this method) when a user selects a quick action for your app
and your app launches instead of activating.
The requested quick action might employ code paths different than
those used otherwise when your app launches. For example, say your app
normally launches to display view A, but your app was launched in
response to a quick action that needs view B. To handle such cases,
check, on launch, whether your app is being launched via a quick
action. Perform this check in your
application:willFinishLaunchingWithOptions: or
application:didFinishLaunchingWithOptions: method by checking for the
UIApplicationLaunchOptionsShortcutItemKey launch option key. The
UIApplicationShortcutItem object is available as the value of the
launch option key.
If you find that your app was indeed launched using a quick action,
perform the requested quick action within the launch method and return
a value of NO from that method. When you return a value of NO, the
system does not call the
application:performActionForShortcutItem:completionHandler: method.

Implement below 3 simple steps:
Step 1 : Write below method in AppDelegate class to Configure dynamic shortcut items.
NOTE : You can configure shortcut items in info.plist if you want it static.
(Refer Apple documentation.)
/**
* #brief config dynamic shortcutItems
* #discussion after first launch, users can see dynamic shortcutItems
*/
- (void)configDynamicShortcutItems {
// config image shortcut items
// if you want to use custom image in app bundles, use iconWithTemplateImageName method
UIApplicationShortcutIcon *shortcutSearchIcon = [UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeSearch];
UIApplicationShortcutIcon *shortcutFavoriteIcon = [UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeFavorite];
UIApplicationShortcutItem *shortcutSearch = [[UIApplicationShortcutItem alloc]
initWithType:#"com.sarangbang.QuickAction.Search"
localizedTitle:#"Search"
localizedSubtitle:nil
icon:shortcutSearchIcon
userInfo:nil];
UIApplicationShortcutItem *shortcutFavorite = [[UIApplicationShortcutItem alloc]
initWithType:#"com.sarangbang.QuickAction.Favorite"
localizedTitle:#"Favorite"
localizedSubtitle:nil
icon:shortcutFavoriteIcon
userInfo:nil];
// add all items to an array
NSArray *items = #[shortcutSearch, shortcutFavorite];
// add the array to our app
[UIApplication sharedApplication].shortcutItems = items;
}
Step 2 : In AppDelegate class application didFinishLaunchingWithOptions method write below code.
// UIApplicationShortcutItem is available in iOS 9 or later.
if([[UIApplicationShortcutItem class] respondsToSelector:#selector(new)]){
[self configDynamicShortcutItems];
// If a shortcut was launched, display its information and take the appropriate action
UIApplicationShortcutItem *shortcutItem = [launchOptions objectForKeyedSubscript:UIApplicationLaunchOptionsShortcutItemKey];
if(shortcutItem)
{
// When the app launch at first time, this block can not called.
//App launch process with quick actions
[self handleShortCutItem:shortcutItem];
}else{
// normal app launch process without quick action
}
}
Step 3 : Write below delegate method and completion handler in AppDelegate class.
/*
Called when the user activates your application by selecting a shortcut on the home screen, except when
application(_:,willFinishLaunchingWithOptions:) or application(_:didFinishLaunchingWithOptions) returns `false`.
You should handle the shortcut in those callbacks and return `false` if possible. In that case, this
callback is used if your application is already launched in the background.
*/
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler{
BOOL handledShortCutItem = [self handleShortCutItem:shortcutItem];
completionHandler(handledShortCutItem);
}
/**
* #brief handle shortcut item depend on its type
*
* #param shortcutItem shortcutItem selected shortcut item with quick action.
*
* #return return BOOL description
*/
- (BOOL)handleShortCutItem : (UIApplicationShortcutItem *)shortcutItem{
BOOL handled = NO;
NSString *bundleId = [NSBundle mainBundle].bundleIdentifier;
NSString *shortcutSearch = [NSString stringWithFormat:#"%#.Search", bundleId];
NSString *shortcutFavorite = [NSString stringWithFormat:#"%#.Favorite", bundleId];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
if ([shortcutItem.type isEqualToString:shortcutSearch]) {
handled = YES;
SecondViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"secondVC"];
self.window.rootViewController = vc;
[self.window makeKeyAndVisible];
}
else if ([shortcutItem.type isEqualToString:shortcutFavorite]) {
handled = YES;
ThirdViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"thirdVC"];
self.window.rootViewController = vc;
[self.window makeKeyAndVisible];
}
return handled;
}

If you look at the sample code provided for apple, you'll see that they suggest that you write a method that handles your shortcut item so that you can handle it in all three places:
application: performActionForShortcutItem,
application: didFinishLaunchingWithOptions and
willFinishLaunchingWithOptions
An example of what I did was:
- (BOOL)handleShortCutItem:(UIApplicationShortcutItem *)shortcutItem {
BOOL handled = NO;
if (shortcutItem == nil) {
return handled;
}
if ([shortcutItem.type isEqualToString:kFavoritesQuickAction]) {
handled = YES;
}
if (handled) {
// do action here
}
return handled;
}
Then you would just call this method in any place where you are getting a shortcut item. This should help you along your way!

I make an objective-c demo project for home screen quick action.
3D touch home quick action demo : https://github.com/dakeshi/3D_Touch_HomeQuickAction
The Demo project implements the static quick action without Info.plist file to avoid unwanted situations before launching the app at first.
You can easily change it to the dynamic quick action.
As mentioned in apple documentation, you can handle quick action in application:didFinishLaunchingWithOptions: method. In that case, you should return NO to block to call application:performActionForShortcutItem:completionHandler: method.

It works on both swift 3 and 4 (only on home screen shortcuts)
//Add plist items as show in image and write following method in Appdelegate
//3D Touch Method shortcuts from home screen
func application(_ application: UIApplication, performActionFor shortcutItem:UIApplicationShortcutItem, completionHandler: #escaping (Bool) -> Void) {
if shortcutItem.type == "Share" {
//handle action Share
let alert = UIAlertController(title: "3D touch Share", message: "Yahoo!!! 3D touch is working👌", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.window?.rootViewController?.present(alert,animated: true,completion: nil)
completionHandler(true)
} else if shortcutItem.type == "Logout" {
//handle action Type02
let alert = UIAlertController(title: "3D touch Logout", message: "Yahoo!!! 3D touch is working👌", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.window?.rootViewController?.present(alert,animated: true,completion: nil)
completionHandler(true)
} else {
completionHandler(false)
}
}

Related

"Unable to monitor event loop" crash

I add breakpoint and find that it block in the method that
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
// init local data
[[PDKeychainBindings sharedKeychainBindings] setObject:#"0" forKey:kNEED_GESTURE_LOGIN];
// register notification to notice switching RootVC
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(switchRootControllerWithType:)
name:kNoti_SwitchRootView
object:nil];
// init the third part SDK
[self setupShareSDK];
[self setupBugly];
[self setupUMeng];
[self setupIQKeyBoard];
[self setupZhugeIOWithOptions:launchOptions];
[self setupTrusfort];
// register push service
[self setupJPushWithOptions:launchOptions];
[self dealWithRemoteNotification:launchOptions];
// set local flag
[KXUserIntroManager setupFlag];
if (self.remoteNotification) {
if ([AccountStateManager isLogin])
[self.loginNavigationController showGesturePDViewController];
self.window.rootViewController = self.loginNavigationController;
} else {
self.window.rootViewController = self.launchController;
}
return YES;
}
i also try the way in stackOverFlow ,Add the following code to the above method
NSArray *args = [NSProcessInfo processInfo].arguments;
for(NSString *arg in args){
if ([arg isEqualToString:#"NoAnimations"]) {
[UIView setAnimationsEnabled:false];
}
}
Image link
it is the detail of didFinishLaunching method. it just init some data and create a notification
While this is not a direct answer to your question, it may well be that your issue is symptomatic of the very evident fact that you are doing much too much work, on the main thread, in didFinishLaunchingWithOptions. Your main job in this method is to get out of the way and let the runtime launch the app. You are doing the exact opposite of that.

Creating shortcut for the app crashes on previous version

I've created a shortcutItem in an App and when i run in ios 9.2 it works fine. But when I open the app which has ios 8.1 it crashes.
Thread 1:EXC_BAD_ACCESS (code=1, address=0x0)
How is the shortcutItem are created say if i create shortcutItem icon and title dynamically after (launchOption == nil) return YES Does the phone shows shortcutItems then?(because createShortcutItem is not called it should not show is what i think.) Will i be able to open shortcutItem once I opened the app is opened and minimized even though the shortcutItems and icons are not called in didFinishLaunchingwithoptions.
Am getting crashes in ios 8.1 at this line
shortcutItem = [launchOptions objectForKeyedSubscript:UIApplicationLaunchOptionsShortcutItemKey];
So i am returning if launchOptions == nil to fix the crash.
Below is the code am using in my app for using shortcut. Its created in my main app so I've modified names a little bit as an example.
How do i handle the shortcut which are not supported on early iOS ( from 8.0) or devices. I want my app to support both ios8 and greater versions.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
BOOL shouldPerformAdditionalDelegateHandling = YES;
// Shortcut creation and methods
[[MyAppShortcuts instance] createShortcutItem];
if (launchOptions == nil) {
return shouldPerformAdditionalDelegateHandling;
}
UIApplicationShortcutItem *shortcutItem;
shortcutItem = [launchOptions objectForKeyedSubscript:UIApplicationLaunchOptionsShortcutItemKey];
if ( shortcutItem != nil ){
// Open app from shortcut
self.rootViewCtrl_.shortcutItem_ = shortcutItem;
[[MyAppShortcuts instance] setMyAppShortcutIs:shortcutItem.type];
[[MyAppShortcuts instance] setAppLaunchedWithShortcut:YES];
// This will block "performActionForShortcutItem:completionHandler" from being called.
shouldPerformAdditionalDelegateHandling = NO;
}
return shouldPerformAdditionalDelegateHandling;
}
- (void) createShortcutItem {
UIApplicationShortcutIcon* firstIcon = [UIApplicationShortcutIcon iconWithTemplateImageName:#"shortcutFirstItem"];
UIApplicationShortcutItem* firstItem;
firstItem = [[UIApplicationShortcutItem alloc]initWithType: firstItemType
localizedTitle: NSLocalizedString(#"First Item", nil)
localizedSubtitle: nil
icon: firstIcon
userInfo: nil];
//..... creating 2nd 3rd 4th item
[UIApplication sharedApplication].shortcutItems = #[firstItem, secondItem, thirdItem, fourthItem];
}
application:performActionForShortcutItem: method is called everytime when the app is opened using shortcut. If i create shortcutItems(icon, title and type) inside the method it calls everytime, does that affect opening shortcut in any way because creating items again and again?
- (void) application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler
{
// Shortcut creation and methods
[ [ MyAppShortcuts instance ] createShortcutItem ];
[[FinAppShortcuts instance] handleShortCut:shortcutItem ];
}
You should place all logic regarding shortcut items after checking if it's available (iOS 9.1 onwards). I don't think launchOptions is nil for instances where it's not invoked by tapping the shortcut.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
BOOL shouldPerformAdditionalDelegateHandling = YES;
//checks if you could user shortcut items. only available in iOS 9.1 onwards
if ([UIApplicationShortcutItem class]){
// Shortcut creation and methods
[[MyAppShortcuts instance] createShortcutItem];
if (launchOptions[UIApplicationLaunchOptionsShortcutItemKey]){
// Open app from shortcut
self.rootViewCtrl_.shortcutItem_ = shortcutItem;
[[MyAppShortcuts instance] setMyAppShortcutIs:shortcutItem.type];
[[MyAppShortcuts instance] setAppLaunchedWithShortcut:YES];
// This will block "performActionForShortcutItem:completionHandler" from being called.
shouldPerformAdditionalDelegateHandling = NO;
}
}
return shouldPerformAdditionalDelegateHandling;
}

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];
}

Unable to Handle Local Notification when app has been terminated

The app that I have made keeps track of Goals and Milestones(which belong to a goal) and reminds the user when they are about to reach the due date of a goal or a milestone through a UILocalNotification
my app has been successful in handling local notifications when the app is in a background state and when the app is in the foreground.
I am aware that if you want to handle local notifications when they are received when the app is terminated you are required to access the local notification through the launch options of the application:didFinishLaunchingWithOptions: method in your appDelegate.
However whenever I test the app and activate the local notification after the app has terminated, I am unable to handle the local notification in application:didFinishLaunchingWithOptions:
I suspect it has something to do with either launch options being nil or launch options not having the local notification payload.
The way I test this scenario is as such:
I build and run the app (command + R)
I schedule the notification
I stop running the simulator and then change my mac's time to just
before the fire date
I build and run the app again and then proceed to terminate the app
from the simulator (by shift + command + h twice and then
swiping up)
I lock the screen and wait for the notification
After it fires I swipe the notification on the lock screen
After the notification fires and i swipe the notification, the app is launched but the method that i used in handling the local notification payload in application:didFinishLaunchingWithOptions: is not called whatsoever.
My Code in my application:didFinishLaunchingWithOptions: to handle the local notification payload is as follows:
if let options = launchOptions {
let value = options[UIApplicationLaunchOptionsLocalNotificationKey] as? UILocalNotification
if let notification = value {
self.application(application, didReceiveLocalNotification: notification)
}
}
My Code in my application:didReceiveLocalNotification: is as follows:
func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {
//If the user receives a notification while app is in the foreground
println("")
if application.applicationState == UIApplicationState.Active {
println("did receive notification")
UIApplication.sharedApplication().applicationIconBadgeNumber = 0
let alertController = UIAlertController(title: nil, message: notification.alertBody!, preferredStyle: UIAlertControllerStyle.Alert)
let alertAction = UIAlertAction(title: "View", style: UIAlertActionStyle.Default) { (alertAction) -> Void in
self.performFollowUpActionForNotification(notification)
}
let cancelAction = UIAlertAction(title: "Close", style: UIAlertActionStyle.Cancel, handler: nil)
alertController.addAction(alertAction)
alertController.addAction(cancelAction)
self.window!.rootViewController!.presentViewController(alertController, animated: true, completion: nil)
}
else {
performFollowUpActionForNotification(notification)
application.applicationIconBadgeNumber = 0
}
}
as well as the helper method performFollowUpActionForNotification:
func performFollowUpActionForNotification(notification:UILocalNotification) {
if notification.alertAction! == "View Goal" {
if let tabVC = self.window?.rootViewController? as? UITabBarController {
let navCon = tabVC.viewControllers![0] as UINavigationController
if navCon.topViewController is GoalPageViewController {
}
else {
navCon.pushViewController(navCon.viewControllers![0] as UIViewController, animated: true )
}
}
}
if notification.alertAction! == "View Milestone" {
if let tabVC = self.window?.rootViewController? as? UITabBarController {
let navCon = tabVC.viewControllers![0] as UINavigationController
if let goalPageVC = navCon.viewControllers![0] as? GoalPageViewController {
goalPageVC.findGoalThatContainsMilestoneAndGoToDetailForNotification(notification)
}
else {println("cant down cast view controller to goal page view controller")}
}
}
}
Is the problem with my code or how i test my app for this scenario? I am desperately in need of an answer.
Hope this will be helpful for you please have look below.
In iOS 7.1 Apple has introduced a very important update to how the system handles notifications triggered by beacons. Now an app can take action when enter/exit monitoring events occur, even if it has been terminated. It’s an amazing improvement in comparison to iOS 7, but there’s still a lot of confusion regarding how it really works, so we’ve prepared a short tutorial.
Location events (related to the beacons in this case) are handled the same way as any other app launching events. Every single time phone enters or exits beacon’s region while the app is terminated, it will be automatically launched and application:didFinishLaunchingWithOptions: method (of AppDelegate class) is called with UIApplicationLaunchOptionsLocationKey key existing in launchOptions parameter.
When you verify this key exists (so location was the reason that your app was launched) you should create new instance of ESTBeaconManager class, set delegate to AppDelegate object (or any other object that is working as ESTBeaconManagerDelegate and was created before this event occurred) and start monitoring. Region you are passing to the startMonitoringForRegion: method is not important, as ESTBeaconManager delegate will receive most recent region information. You can just pick any of the ones your app registered in iOS. When the monitoring was revoked, app will automatically receive most recent entered/exited region event in beaconManager:didEnterRegion: or beaconManager:didExitRegion: method.
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if([launchOptions objectForKey:#"UIApplicationLaunchOptionsLocationKey"])
{
self.beaconManager = [ESTBeaconManager new];
self.beaconManager.delegate = self;
// don't forget the NSLocationAlwaysUsageDescription in your Info.plist
[self.beaconManager requestAlwaysAuthorization];
[self.beaconManager startMonitoringForRegion:[[ESTBeaconRegion alloc]
initWithProximityUUID:ESTIMOTE_PROXIMITY_UUID
identifier:#"AppRegion"]];
}
return YES;
}
-(void)beaconManager:(ESTBeaconManager *)manager didEnterRegion:(ESTBeaconRegion *)region
{
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = #"Enter region";
notification.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
-(void)beaconManager:(ESTBeaconManager *)manager didExitRegion:(ESTBeaconRegion *)region
{
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = #"Exit region";
notification.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}

NSUSer defaults and swiping the application off ios

All,
I just cannot find an answer to this question. The settings View Controller needs to shown once on startup ONLY. So when you download the app from the App Store / test flight.
I have it correct, so it runs it first, thats fine.
when you have finished with the settings page it goes to the main page and when you move the app to the background it carries on from where it left off. thats fine.. But... When you swipe the app away by double pressing the home button and pushing the app up to remove (ios7) it goes back to the settings screen again but it should carry on from where it left off.
So in my App Delegate, I have :
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSString *savedValue = [[NSUserDefaults standardUserDefaults] stringForKey:#"SettingsShown"];
NSLog(#"%#", savedValue);
Reachability *reachability = [Reachability reachabilityWithHostname:#"www.outtonightapp.com"];
[reachability startNotifier];
NSUserDefaults *settingsscreen = [NSUserDefaults standardUserDefaults];
[settingsscreen registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],#"firstTime", nil]];
//BOOL firstTime = [settingsscreen boolForKey:#"firstTime"];
BOOL firstTime = [settingsscreen boolForKey:#"SettingsShown"];
if (!firstTime) {
//if ( firstTime==YES) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"SettingsShown"];
[[NSUserDefaults standardUserDefaults] synchronize];
self.window.rootViewController = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"SetUpNav"];
}
else
{
return YES;
}
This did work until I had to recreate my settings VC.. any advice would be great.
In similar situation I used delegation pattern.
Assume you have your initial view controller initialVC. And special settings view controller (SetUpNav) which is meant to be run only first time when defaults is not set. Then you could do the following:
You define SetUpNavdelegate protocol in SetUpNavViewController.h and property "initiator" conforming to that protocol
#protocol SetUpNavDeleagte;
#interface SetUpNavViewController : UIViewController
#property (strong,nonatomic) id <SetUpNavdelegate> initiator;
// the rest
#end
#protocol SetUpNavdelegate <NSObject>
-(void)setupFinished;
#end
In your InitialVC' viewDidLoad you do:
Check Your defaults are set properly or not by determine "firstTime" BOOL value
and fire setUpNav controller in code
-(void)viewDidLoad
{
// Check your defaults for consistency
if (firstTime){
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Main OR you SB name" bundle:nil];
UIViewController *setUpNavVC = [loginStoryboard instantiateViewControllerWithIdentifier:#"SetUpNav"];
setUpNavVC.initiator = self;
YouAppDelegateClass *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.window.rootViewController = setUpVC;
// Here you don't need animation as I assume it is the very first screen
}
// ... the rest
}
-(void)setupFinished
{
// Here You animately restoring your initial vc.
YouAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.window.rootViewController = self; // This line is for device orientation sync
[UIView transitionWithView:appDelegate.window
duration:FINISH_DURATION
options:UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionCurveEaseInOut
animations:^{ appDelegate.window.rootViewController = self; }
completion:nil];
// setUpNav controller will be dealloced by ARC
}
In your setUpNavViewController after you finished all defaults job you set notification for your delegation:
[self.initator setupFinished];
In may app this setting work is actually a separate storyboard with it's own workflow in it. By the way using this approach you are able not only show it first time but whenever your app's user defaults is not set properly (like if you using settings bundle). You can show it modally, in navigation stack or in pop over (iPad case). It is a more generic approach.
Summary: The one who starts, he finishes.

Resources