Since iOS 8, the operating system is indicating an iBeacon-enabled application, which is inside a defined region, with an icon in the left bottom corner of the lockscreen (see http://appleinsider.com/articles/14/06/03/apples-ios-8-uses-ibeacon-tech-brings-location-aware-app-access-to-lock-screen for reference).
I want to make use of this feature in my app by running custom code if the app is launched from the lockscreen (e.g. switch to specific view).
How to check in the Application Delegate if the app was launched from the lockscreen? I couldn't find any documentation on this.
AFAIK, it's not possible to detect whether your app was brought into the foreground via the "Suggested apps" feature (that's what Apple calls the icon on the lock screen), or by any other means (app switcher, app icon on the home screen).
You could do some heuristics. If the app came into the foreground and there are no beacons in range (you can check that with ranging), then there's no way it was via the lock screen icon.
There is a Excellent article on this topic by Matt Coneybeare
Below I have pointed out some of the important points from that article , for more info check out that article.
From Article of Matt Coneybeare
On iOS 6 and lower it could be detected using the current UIApplicationState in applicationWillEnterForeground
UIApplicationState state = [[UIApplication sharedApplication] ApplicationState];
if (UIApplicationStateInactive == state)
// Coming from locked screen (iOS 6)
else
// Coming from Springboard, another App, etc...
But From iOS 7 onwards, the state value is UIApplicationStateBackground in both scenarios.
It seems that there is no possible way to detect where the App is being launched from on iOS 7, but there is a way to detect if you are going to the Lockscreen vs Homescreen (or something else).
The trick is to read the screen brightness in applicationDidEnterBackground.
When the App hits the background due to the lock button being pressed or an auto-lock timeout, the brightness will be 0.0 on iOS 7.
Otherwise, it will be > 0 when the home button is pressed or another App launched from the multitask selector or notification center.
When the app is launched by beacon detection, your AppDelegate's (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region method will be invoked.
You can put any custom code inside that method, perhaps to set a flag indicating this is how the app was launched.
The following code shows how to detect that tapping a local notification launched the app from the lock screen. For details, see: https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/IPhoneOSClientImp.html
- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UILocalNotification *localNotif =
[launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotif) {
// do something
}
...
return YES;
}
This is not the same as launching the app by tapping the icon on the bottom of the lock screen, but it is similar. If you present a local notification on beacon detection it will work as described above if tapped.
I believe the app icon shows up on the bottom of the lock screen only if your app is launched into the background by beacon detection and does not present a local notification. If this is the case you are looking for, you might examine the launchOptions above and see if a different key is present in this case.
Related
Is there a way i could stop an app from going into background ? Or is there a way in which i could bring my app to foreground if it did go into background ?
I'm making a showcase app for a client and the app must always run on the iPad without interaction from the user.
You can force your app to stay active using following code
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];
But if you press home button of device, the app will go in background. You cannot force to stop this.
In this case you can use UILocalNotification to bring the app to the foreground if the device is locked, a notification appears, and the user unlocks the device or if user tap on the notification.
You can also fire a local notification whenever user going to background in
applicationDidEnterBackground: method
In the documentation (App States and Multitasking) which Apple provides:
If your app is launched into the background instead—usually to handle some type of background event—the launch cycle changes slightly to the one shown in Figure 3-3. The main difference is that instead of your app being made active, it enters the background state to handle the event and then is suspended shortly afterward. When launching into the background, the system still loads your app’s user interface files but it does not display the app’s window.
How to simulate to launch an app into the background in iOS Simulator?
If an app launched into the background, will the UIApplicationDelegate method -applicationDidEnterBackground: be called?
No, applicationDidEnterBackground: won't be called in this case.
You can't simulate real launch into background behaviour if Xcode is attached.
(But you can simulate launch with UIApplicationLaunchOptionsLocationKey key using location simulation)
I tested significant location change API on real device and collected logs after tests. Results:
application:willFinishLaunchingWithOptions: is called with UIApplicationLaunchOptionsLocationKey key.
But applicationDidEnterBackground: is not called.
You just have to launch your app and then go to home screen in simulator - press cmd + shift + H and the app is in background state and - (void)applicationDidEnterBackground:(UIApplication *)application in appDelegate is called .
I'm Developing a App using a iBeacon functionality. I want to fire an event when the App is in background & when it is closed. These have different Alert messages. I was able to determine when the App goes to Background. But was finding it difficult when the App gets closed.
I set a checkpoint for the App when it is in background state. But how should we find if the App is closed completely.
Thanks,
i don't know what exactly you want but you can check you app state by following line of code
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
UIApplicationStateBackground when application is in background
UIApplicationStateInactive when app is not in background
Take a look at UIApplicationDelegate's - (void)applicationWillTerminate:(UIApplication *)application method. Seems like what you need. It will be called before application is terminated (for example as a result of user removing it from the list of running apps).
My app has different behavior when becoming active from the lockscreen (locked while active), or becoming active from anything else.
On iOS 6 and lower I could detect this
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
if (UIApplicationStateInactive == state)
// Coming from locked screen (iOS 6)
else
// Coming from Springboard, another app, etc...
But on iOS 7, the state value is UIApplicationStateBackground in both scenarios. Is this the intended behavior? How can I properly detect whether the app is launching from the lockscreen now?
Registered devs, I already posted this on the devforums before the NDA was lifted, see here
I was able to figure out a hack on this and so far seems to be reliable. It only works on the device, not the simulator, and has been tested on an iPhone 5s, 5 and 4S running iOS 7.
It seems that there is no possible way to detect where the app is being launched from on iOS 7, but there is a way to detect if you are going to the lock-screen vs springboard. The trick is to read the screen brightness in applicationDidEnterBackground. When the app hits the background due to the lock button being pressed or an auto-lock timeout, the brightness will be 0.0 on iOS 7. Otherwise, it will be > 0 when the home button is pressed or another app launched from the multitask selector or notification center.
- (void)applicationDidEnterBackground:(UIApplication *)application {
CGFloat screenBrightness = [[UIScreen mainScreen] brightness];
NSLog(#"Screen brightness: %f", screenBrightness);
self.backgroundedToLockScreen = screenBrightness <= 0.0;
}
Now that I have an ivar holding this info, I can use it in applicationWillEnterForeground to determine my app flow.
- (void)applicationWillEnterForeground:(UIApplication *)application {
if (self.backgroundedToLockScreen) {
... // app was backgrounded to lock screen
} else {
... // app was backgrounded on purpose by tapping the home button or switching apps.
}
self.backgroundedToLockScreen = NO;
}
It is not the exact same as the iOS 6 behavior though. On iOS 6, you could inspect the UIApplicationState to detect where you were coming from, and this solution answers the similar, but not exactly the same, question of where you were going when the app was backgrounded. For example, perhaps the app was backgrounded due to a screen lock timeout, but then a notification for another app woke the device, and the user went there directly from the lockscreen, then back to my app. My app would have determined on backgrounding that the user went to the lockscreen, but when they come back they are actually coming from an active screen. For my app, this difference is negligible, but your milage may vary.
So what about older OS support? My app also supports iOS 6 so I needed to get the old behavior too. Simple. Just the application state monitoring to the foreground method:
- (void)applicationWillEnterForeground:(UIApplication *)application {
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
if (UIApplicationStateInactive == state || // detect if coming from locked screen (iOS 6)
self.backgroundedToLockScreen) // detect if backgrounded to the locked screen (iOS 7)
{
... // app is coming from or was backgrounded to lock screen
} else {
... // app was backgrounded on purpose by tapping the home button or switching apps
}
self.backgroundedToLockScreen = NO;
}
I am not sure how reliable the brightness reading is, or whether or not it will change in future OS builds, but in the meantime, this hack seems to be the best we can get. Hope this helps.
Actually the only proper way to set your app behavior when becoming active is via the app delegate methods.
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the active 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.
}
These two are called when the app is running in the background and becomes active either via multitasking UI or after a call or other interruption.
When the app is opened from the Springboard and is not running in the background this method is called:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
return YES;
}
I have an app that needs to do something when it’s sent to background using the Home button and something else when the device is locked using the top hardware button. The standard way of solving these requirements are the notifications and delegate methods sent out by UIApplication. On iOS 4 they look like this:
// Pressing the home button
Will resign active.
Did enter background.
// Tapping app icon on Springboard
Will enter foreground.
Did become active.
// Pressing the lock button
Will resign active.
// Unlocking the device
Did become active.
In other words, it’s quite easy to tell between locking and backgrounding. On iOS 5 the behaviour changed:
// Pressing the home button
Will resign active.
Did enter background.
// Tapping app icon on Springboard
Will enter foreground.
Did become active.
// Pressing the lock button
Will resign active.
Did enter background.
// Unlocking the device
Will enter foreground.
Did become active.
Notice that the didEnterBackground and willEnterForeground notifications are now sent out even when (un)locking the device, making it impossible to tell between locking and backgrounding. Is this change documented somewhere? Is it a regression? Do you know another way to distinguish the two cases?
iOS 6
In my preliminary testing via the simulator, checking the application state with
[[UIApplication sharedApplication] applicationState]
in either
- (void)applicationWillEnterForeground:(UIApplication *)application
- (void)applicationDidEnterBackground:(UIApplication *)application
allows you to differentiate between a call to lock the device and just switching back to the homescreen. A lock screen will return 1 (UIApplicationStateInactive), whereas a home button press will register as a 2 (UIApplicationStateBackground).
It seems consistent and should work on an iOS device just as reliably as it does in the simulator.
iOS 7
The iOS 6 method no longer works in iOS 7. In order to do this now, you have to utilize CFNotificationCenter and listen for a darwin notification (labeled: com.apple.springboard.lockcomplete). You can find the github repo with the sample project here: https://github.com/binarydev/ios-home-vs-lock-button
Credit for the iOS 7 fix goes out to wqq
I have looked into this quite a bit so I would love to be wrong here if someone knows something I don't, but technically, there is no documented way to tell the difference between locking the device, and sending to background.
One thing you can check however, is the UIApplicationState during the transition from foreground to background. Locking a device will give UIApplicationStateInactive and moving the App to the background will give UIApplicationStateBackground. But, since this behaviour is not officially documented it may change in the future.
A basic example:
- (void)applicationDidEnterBackground:(UIApplication *)application {
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
NSLog(#"Device state: %#", state);
switch (state) {
case UIApplicationStateActive:
/* ... */
break;
case UIApplicationStateInactive:
/* Device was/is locked */
break;
case UIApplicationStateBackground:
/* User pressed home button or opened another App (from an alert/email/etc) */
break;
}
}
UIApplicationState - The running states of an application
typedef enum {
UIApplicationStateActive,
UIApplicationStateInactive,
UIApplicationStateBackground
}
UIApplicationState
Constants
UIApplicationStateActive - The application
is running in the foreground and currently receiving events. Available
in iOS 4.0 and later.
UIApplicationStateInactive - The application is running in the
foreground but is not receiving events. This might happen as a result
of an interruption or because the application is transitioning to or
from the background.
UIApplicationStateBackground - The application is
running in the background.
According to the UIApplicationDelegate Protocol Reference:
applicationWillResignActive:
didEnterBackground:
// ...
willEnterForeground:
applicationDidBecomeActive:
are the only methods that ever get called in both situations.
According to the iOS 4.3 to iOS 5.0 API Diff, these are the ONLY changes regarding UIApplication or UIApplicationDelegate, so I couldn't find where they documented any of these notification changes:
UIApplication.h
Added -[UIApplication setNewsstandIconImage:]
Added UIApplication.userInterfaceLayoutDirection
Added UIApplicationDelegate.window
Added UIApplication(UINewsstand)
Added UIApplicationLaunchOptionsNewsstandDownloadsKey
Added UIRemoteNotificationTypeNewsstandContentAvailability
Added UIUserInterfaceLayoutDirection
Added UIUserInterfaceLayoutDirectionLeftToRight
Added UIUserInterfaceLayoutDirectionRightToLeft
This is more of a workaround/hack, but according to my experience it's very reliable.
When the device is screen-locked (not just home button-ed, if that's a word :)) - bound network (UDP) sockets are broken.
I was using GCDAsyncUDPSocket (also AsyncUDPSocket before) and they both fire a network/broken pipe error reliably when the device is turned off.
In my case I need the UDP socket anyway, for other apps it might be a bit smelly, however, just binding/listening on a UDP socket without any action is not too terrible if you really need to differentiate here.
This note will [self destruct]; is 5 minutes (so Apple won't find out).
There’s a thread about this issue on Apple Developer Forums (registered developers only, sorry). The gist is that the new behaviour is by design. There are requests for a new API feature to distinguish between the two use cases, but nothing working yet.
Here is what Apple's iOS Programming Guide says:
Pressing the Sleep/Wake button is another type of interruption that
causes your app to be deactivated temporarily. When the user presses
this button, the system disables touch events, moves the app to the
background but sets the value of the app’s applicationState property
to UIApplicationStateInactive (as opposed to
UIApplicationStateBackground), and finally locks the screen.
http://developer.apple.com/library/ios/#DOCUMENTATION/iPhone/conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html
So, you should check the UIApplication's applicationState property in applicationDidEnterBackground:. If it is UIApplicationStateBackground the user pressed the home button. But if it is UIApplicationStateInactive the user locked the device.