UIApplicationDelegate launchOptions
AppDelegate is the dumping ground for functionality in iOS.
Application lifecycle management? URL routing? Notifications? Core Data incantations? Invasive and monolithic 3rd-party SDK initialization? Random functionality that doesn’t seem to fit anywhere else? Just stash it in App
!
And of all of the crowded, over-loaded parts in AppDelegate, -application:did
is the most congested of all.
For many developers, the launch
parameter is akin to the String[] args
argument of a Java main
method—something ignored in the rush to building an application. Hiding in plain sight, launch
contains a wealth of keyed knowledge that speaks to the multitude of ways an app can be launched on iOS.
This week, all will be revealed in this NSHipster tell-all about the least understood parameter of the most important method in UIKit: launch
.
Every app begins with UIApplication
(or more accurately, -application:will
, when implemented). It is called by the application to notify its delegate that the launch process is finishing, and nearly ready to run.
An app launches when its icon is tapped on Springboard, but there are several other occasions in which an app can be launched. For example, an app registered for a custom URL scheme, such as twitter://
, could be launched as a result of opening a URL. An app could also be launched in response to a push notification, or a significant change in device location.
Determining why and how an app launched is the responsibility of the launch
parameter. Like a user
dictionary, -application:did
can get information for particular named keys in launch
.
Many of these keys are also available in the
UIApplication
notification posted on application launch. Check the docs for additional details.Did Finish Launching Notification
Numerous as they are, launch
keys can be more easily understood when organized into groups, corresponding to why the app was launched:
Opening from URL
Apps can launch other apps by passing URLs:
[[UIApplication shared Application] open URL:[NSURL URLWith String:@"app:…"]];
For example, an http://
URL would open in Safari, a mailto://
URL would open in Mail, and a tel://
URL would open in Phone.
In these circumstances, the UIApplication
key would be populated in launch
.
UIApplication
: Indicates that the app was launched in order to open a URL. The value of this key is anLaunch Options URLKey NSURL
object containing the URL to open.
An app can also be launched through URLs with additional system information. When an app is launched from an UIDocument
or via AirDrop, the following keys are set in launch
:
UIApplication
: Identifies the app that requested the launch of your app. The value of this key is anLaunch Options Source Application Key NSString
object that represents the bundle ID of the app that made the requestUIApplication
: Indicates that custom data was provided by the app that requested the opening of the URL. The value of this key is a property-list object containing the custom data.Launch Options Annotation Key
NSURL *file URL = [[NSBundle main Bundle] URLFor Resource:@"Document" with Extension:@"pdf"];
if (file URL) {
UIDocument Interaction Controller *document Interaction Controller = [UIDocument Interaction Controller interaction Controller With URL:file URL];
document Interaction Controller.annotation = @{@"foo": @"bar"};
[document Interaction Controller set Delegate:self];
[document Interaction Controller present Preview Animated:YES];
}
Responding to Notification
Not to be confused with NSNotification
, apps can be sent remote or local notifications.
Remote Notification
Introduced in iOS 3, remote, or “push” notifications are one of the defining features of the mobile platform.
To register for remote notifications, register
is called in application:did
.
[application register For Remote Notification Types:
UIRemote Notification Type Badge |
UIRemote Notification Type Sound |
UIRemote Notification Type Alert];
…which, if successful, calls -application:did
. Once the device has been successfully registered, it can receive push notifications at any time.
If an app receives a notification while in the foreground, its delegate will call application:did
. However, if the app is launched, perhaps by swiping the alert in notification center, application:did
is called with the UIApplication
launch option:
UIApplication
: Indicates that a remote notification is available for the app to process. The value of this key is anLaunch Options Remote Notification Key NSDictionary
containing the payload of the remote notification.
alert
: Either a string for the alert message or a dictionary with two keys:body
andshow-view
.badge
: A number indicating the quantity of data items to download from the provider. This number is to be displayed on the app icon. The absence of a badge property indicates that any number currently badging the icon should be removed.sound
: The name of a sound file in the app bundle to play as an alert sound. If “default” is specified, the default sound should be played.
Since this introduces two separate code paths for notification handling, a common approach is to have application:did
manually call application:did
:
- (BOOL)application:(UIApplication *)application
did Finish Launching With Options:(NSDictionary *)launch Options
{
…
if (launch Options[UIApplication Launch Options Remote Notification Key]) {
[self application:application did Receive Remote Notification:launch Options[UIApplication Launch Options Remote Notification Key]];
}
}
Local Notification
Local notifications were added in iOS 4, and to this day, are still surprisingly misunderstood.
Apps can schedule UILocal
s to trigger at some future time or interval. If the app is active in the foreground at that time, the app calls -application:did
on its delegate. However, if the app is not active, the notification will be posted to Notification Center.
Unlike remote notifications, UIApplication
delegate provides a unified code path for handling local notifications. If an app is launched through a local notification, it calls -application:did
followed by -application:did
(that is, there is no need to call it from -application:did
like remote notifications).
A local notification populates the launch options on UIApplication
, which contains a payload with the same structure as a remote notification:
-
UIApplication
: Indicates that a local notification is available for the app to process. The value of this key is anLaunch Options Local Notification Key NSDictionary
containing the payload of the local notification.
In the case where it is desirable to show an alert for a local notification delivered when the app is active in the foreground, and otherwise wouldn’t provide a visual indication, here’s how one might use the information from UILocal
to do it manually:
// App Delegate.h
@import AVFoundation;
@interface App Delegate ()
@property (readwrite, nonatomic, assign) System Sound ID local Notification Sound;
@end
// App Delegate.m
- (void)application:(UIApplication *)application
did Receive Local Notification:(UILocal Notification *)notification
{
if (application.application State == UIApplication State Active) {
UIAlert View *alert View =
[[UIAlert View alloc] init With Title:notification.alert Action
message:notification.alert Body
delegate:nil
cancel Button Title:NSLocalized String(@"OK", nil)
other Button Titles:nil];
if (!self.local Notification Sound) {
NSURL *sound URL = [[NSBundle main Bundle] URLFor Resource:@"Sosumi"
with Extension:@"wav"];
Audio Services Create System Sound ID((__bridge CFURLRef)sound URL, &_local Notification Sound);
}
Audio Services Play System Sound(self.local Notification Sound);
[alert View show];
}
}
- (void)application Will Terminate:(UIApplication *)application {
if (self.local Notification Sound) {
Audio Services Dispose System Sound ID(self.local Notification Sound);
}
}
Location Event
Building the next great geomobilelocalsocial check-in photo app? Well, you’re about 4 years late to the party.
But fear not! With iOS region monitoring, your app can be launched on location events:
UIApplication
: Indicates that the app was launched in response to an incoming location event. The value of this key is anLaunch Options Location Key NSNumber
object containing a Boolean value. You should use the presence of this key as a signal to create aCLLocation
object and start location services again. Location data is delivered only to the location manager delegate and not using this key.Manager
Here’s an example of how an app might go about monitoring for significant location change to determine launch behavior:
// .h
@import Core Location;
@interface App Delegate () <CLLocation Manager Delegate>
@property (readwrite, nonatomic, strong) CLLocation Manager *location Manager;
@end
// .m
- (BOOL)application:(UIApplication *)application
did Finish Launching With Options:(NSDictionary *)launch Options
{
…
if (![CLLocation Manager location Services Enabled]) {
[[[UIAlert View alloc] init With Title:NSLocalized String(@"Location Services Disabled", nil)
message:NSLocalized String(@"You currently have all location services for this device disabled. If you proceed, you will be asked to confirm whether location services should be reenabled.", nil)
delegate:nil
cancel Button Title:NSLocalized String(@"OK", nil)
other Button Titles:nil] show];
} else {
self.location Manager = [[CLLocation Manager alloc] init];
self.location Manager.delegate = self;
[self.location Manager start Monitoring Significant Location Changes];
}
if (launch Options[UIApplication Launch Options Location Key]) {
[self.location Manager start Updating Location];
}
}
Newsstand
All of the Newsstand developers in the house: say “Yeah!”
crickets.aiff
Well alright, then.
Newsstand can launch when newly-downloaded assets are available.
This is how you register:
[application register For Remote Notification Types:
UIRemote Notification Type Newsstand Content Availability];
And this is the key to look out for in launch
:
UIApplication
: Indicates that newly downloaded Newsstand assets are available for your app. The value of this key is an array of string identifiers that identify theLaunch Options Newsstand Downloads Key NKAsset
objects corresponding to the assets. Although you can use the identifiers for cross-checking purposes, you should obtain the definitive array ofDownload NKAsset
objects (representing asset downloads in progress or in error) through the downloadingAssets property of theDownload NKLibrary
object representing the Newsstand app’s library.
Not too much more to say about that.
Bluetooth
iOS 7 introduced functionality that allows apps to be relaunched by Bluetooth peripherals.
If an app launches, instantiates a CBCentral
or CBPeripheral
with a particular identifier, and connects to other Bluetooth peripherals, the app can be re-launched by certain actions from the Bluetooth system. Depending on whether it was a central or peripheral manager that was notified, one of the following keys will be passed into launch
:
UIApplication
: Indicates that the app previously had one or moreLaunch Options Bluetooth Centrals Key CBCentral
objects and was relaunched by the Bluetooth system to continue actions associated with those objects. The value of this key is anManager NSArray
object containing one or moreNSString
objects. Each string in the array represents the restoration identifier for a central manager object.UIApplication
: Indicates that the app previously had one or moreLaunch Options Bluetooth Peripherals Key CBPeripheral
objects and was relaunched by the Bluetooth system to continue actions associated with those objects. The value of this key is anManager NSArray
object containing one or moreNSString
objects. Each string in the array represents the restoration identifier for a peripheral manager object.
// .h
@import Core Bluetooth;
@interface App Delegate () <CBCentral Manager Delegate>
@property (readwrite, nonatomic, strong) CBCentral Manager *central Manager;
@end
// .m
self.central Manager = [[CBCentral Manager alloc] init With Delegate:self queue:nil options:@{CBCentral Manager Option Restore Identifier Key:(launch Options[UIApplication Launch Options Bluetooth Centrals Key] ?: [[NSUUID UUID] UUIDString])}];
if (self.central Manager.state == CBCentral Manager State Powered On) {
static NSString * const UID = @"7C13BAA0-A5D4-4624-9397-15BF67161B1C"; // generated with `$ uuidgen`
NSArray *services = @[[CBUUID UUIDWith String:UID]];
NSDictionary *scan Options = @{CBCentral Manager Scan Option Allow Duplicates Key:@YES};
[self.central Manager scan For Peripherals With Services:services options:scan Options];
}
Keeping track of all of the various ways and means of application launching can be exhausting. So it’s fortunate that any given app will probably only have to handle one or two of these possibilities.
Knowing what’s possible is often what it takes to launch an app from concept to implementation, so bear in mind all of your options when the next great idea springs to mind.