AppDelegate 是 iOS 各种功能的集散中心。

AppDelegate.m

在 AppDelegate 所有这些拥挤的、超出负载的方法中,-application:didFinishLaunchingWithOptions: 是最臃肿的一个了。

launchOptions 参数就是类似于 Java 的 main 函数中 String[] args 的作用 —— 在构建应用时候一般是被忽略的。在其平淡的外表下,launchOptionslaunchOptions


UIApplicationDelegate -application:didFinishLaunchingWithOptions:(更精确地说以后或许也包含 -application:willFinishLaunchingWithOptions:) 启动。应用调用这个方法来告诉 delegate 进程已经启动完毕,已经准备好运行了。 在 Springboard 中点击图标应用就开始启动了,但也有其他一些启动的方法。比如说注册了自定义 URL scheme 的应用可以以类似于 twitter://launchOptions 参数的职责所在。就像 userInfo 字典一样,在 -application:didFinishLaunchingWithOptions: 的 launchOptions

这些键中的许多在应用启动时发出的UIApplicationDidFinishLaunchingNotification

launchOptions

从 URL 打开

其他应用通过传递 URL 可以打开一个应用:


Objective-C

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"app://..."]];


http:// 开头的 URL 会在 Safari 中打开,mailto:// 开头的 URL 会在邮件中打开,tel://UIApplicationLaunchOptionsURLKey

UIApplicationLaunchOptionsURLKey

  • : 标示应用是通过 URL 打开的。其对应的值代表应用被打开时使用的 

NSURL

UIDocumentInteractionController中打开时,launchOptions

UIApplicationLaunchOptionsSourceApplicationKey

  • :请求打开应用的应用 id。对应的值是请求打开应用的 bundle ID 的 

NSStringUIApplicationLaunchOptionsAnnotationKey

  • :标示通过 URL 打开应用时携带了自定义数据。对应的值是包含自定义数据的属性列表对象


Objective-C

NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"Document" withExtension:@"pdf"];
if (fileURL) {
    UIDocumentInteractionController *documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:fileURL];
    documentInteractionController.annotation = @{@"foo": @"bar"};
    [documentInteractionController setDelegate:self];
    [documentInteractionController presentPreviewAnimated:YES];
}


响应通知

不要和 NSNotification 混淆了,应用可以通过本地(local)或远程(remote)通知打开。

推送通知

自 iOS 3 开始引入的 remote(或者叫 push)notification 是在移动平台上的重要特性。

application:didFinishLaunchingWithOptions: 中调用 registerForRemoteNotificationTypes:来注册推送通知。

Objective-C

[application registerForRemoteNotificationTypes:
    UIRemoteNotificationTypeBadge |
    UIRemoteNotificationTypeSound |
    UIRemoteNotificationTypeAlert];


-application:didRegisterForRemoteNotificationsWithDeviceToken:,之后该设备就能随时收到推送通知了。application:didReceiveRemoteNotification:。但是如果是通过在通知中心中滑动通知打开的应用,则会调用 application:didFinishLaunchingWithOptions: 并携带UIApplicationLaunchOptionsRemoteNotificationKey

UIApplicationLaunchOptionsRemoteNotificationKey

  • :标示推送通知目前处于可用状态。对应的值是包含通知内容的 

NSDictionary

  • 。 > -

alert

  • :一个字符串或包含两个键 

body

  •  和 

show-view

  •  的字典。 > -

badge

  • :标示从通知发出者那应该获取数据的数量。这个数字会显示在应用图标上。没有 badge 信息则表示应该从图片上移除数字显示。 > -

sound

  • :通知接收时播放音频的文件名。如果值为 "default" 那么则播放默认音频。

application:didFinishLaunchingWithOptions:中手动调用 application:didReceiveRemoteNotification:

Objective-C

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // ...

    if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
        [self application:application didReceiveRemoteNotification:launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]];
    }
}


本地通知

本地通知 是在 iOS 4 中加入的功能,这个功能至今都被误解了。

UILocalNotification。如果应用处于打开状态,那么将回调 -application:didReceiveLocalNotification:UIApplication 的 delegate 提供了统一控制本地通知的方法。如果应用是通过本地通知启动的,-application:didReceiveLocalNotification: 将会在 -application:didFinishLaunchingWithOptions: 之后被自动调用(意思就是不需要像推送通知一样在-application:didFinishLaunchingWithOptions:UIApplicationLaunchOptionsLocalNotificationKeyUIApplicationLaunchOptionsLocalNotificationKey

  • : 标示本地通知目前处于可用状态。对应的值是包含通知内容的 

NSDictionary

UILocalNotification

Objective-C

// .h
@import AVFoundation;

@interface AppDelegate ()
@property (readwrite, nonatomic, assign) SystemSoundID localNotificationSound;
@end

// .m
- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification
{
    if (application.applicationState == UIApplicationStateActive) {
        UIAlertView *alertView =
            [[UIAlertView alloc] initWithTitle:notification.alertAction
                                       message:notification.alertBody
                                      delegate:nil
                             cancelButtonTitle:NSLocalizedString(@"OK", nil)
                             otherButtonTitles:nil];

        if (!self.localNotificationSound) {
            NSURL *soundURL = [[NSBundle mainBundle] URLForResource:@"Sosumi"
                                                      withExtension:@"wav"];
            AudioServicesCreateSystemSoundID((__bridge CFURLRef)soundURL, &_localNotificationSound);
        }
        AudioServicesPlaySystemSound(self.localNotificationSound);

        [alertView show];
    }
}

- (void)applicationWillTerminate:(UIApplication *)application {
    if (self.localNotificationSound) {
        AudioServicesDisposeSystemSoundID(self.localNotificationSound);
    }
}


地理位置事件

听说你在开发一个 LBS 签到照片的应用?哈哈,看起来你的动作好像落后时代四年多了。

但不要害怕!有了 iOS 的位置监控,你的应用可以通过地理位置触发的事件启动了:

UIApplicationLaunchOptionsLocationKey

  • :标示应用是响应地理位置事件启动的。对应的值是包含 Boolean 值的 

NSNumber

  •  对象。可以把这个键作为信号来创建 

CLLocationManager

以下是检测位置变化来判断启动行为的例子:


Objective-C

// .h
@import CoreLocation;

@interface AppDelegate () <CLLocationManagerDelegate>
@property (readwrite, nonatomic, strong) CLLocationManager *locationManager;
@end

// .m
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // ...

    if (![CLLocationManager locationServicesEnabled]) {
        [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Location Services Disabled", nil)
                                    message:NSLocalizedString(@"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
                          cancelButtonTitle:NSLocalizedString(@"OK", nil)
                          otherButtonTitles:nil] show];
    } else {
        self.locationManager = [[CLLocationManager alloc] init];
        self.locationManager.delegate = self;
        [self.locationManager startMonitoringSignificantLocationChanges];
    }

    if (launchOptions[UIApplicationLaunchOptionsLocationKey]) {
        [self.locationManager startUpdatingLocation];
    }
}


报刊杂志(Newsstand)

所有的报刊杂志开发者都会为此欢呼的!

欢呼声.aiff

够了...

有新的可用下载时,报刊杂志应用可以启动。

这样注册即可:


Objective-C

[application registerForRemoteNotificationTypes:
    UIRemoteNotificationTypeNewsstandContentAvailability];


然后在启动参数中找到这个键:

UIApplicationLaunchOptionsNewsstandDownloadsKey

  • :标示应用有新的可用杂志资源下载。对应的值是包含 

NKAssetDownload

  •  id 的字符串数组。虽然你可以通过这些 id 进行检查,但还是应该通过 

NKLibrary

  •  对象的 downloadingAssets 属性来持有这些 

NKAssetDownload

详细情况不再赘述。

蓝牙

iOS 7 开始支持外围蓝牙设备重新唤醒应用。

CBCentralManager 或 CBPeripheralManager 用于连接蓝牙设备,之后应用就可以通过蓝牙系统的相关动作来被重新唤醒了。取决于发出通知的是一个中心设备还是外围设备,launchOptions

UIApplicationLaunchOptionsBluetoothCentralsKey

  • :标示应用之前曾有过一个或多个 

CBCentralManager

  •  对象并被蓝牙系统的相关动作唤醒过。对应的值是包含 

NSStringUIApplicationLaunchOptionsBluetoothPeripheralsKey

  • :标示应用之前曾有过一个或多个 

CBPeripheralManager

  •  对象并被蓝牙系统的相关动作唤醒过。对应的值是包含 

NSString


Objective-C

// .h
@import CoreBluetooth;

@interface AppDelegate () <CBCentralManagerDelegate>
@property (readwrite, nonatomic, strong) CBCentralManager *centralManager;
@end

// .m
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:@{CBCentralManagerOptionRestoreIdentifierKey:(launchOptions[UIApplicationLaunchOptionsBluetoothCentralsKey] ?: [[NSUUID UUID] UUIDString])}];

if (self.centralManager.state == CBCentralManagerStatePoweredOn) {
    static NSString * const UID = @"7C13BAA0-A5D4-4624-9397-15BF67161B1C"; // generated with `$ uuidgen`
    NSArray *services = @[[CBUUID UUIDWithString:UID]];
    NSDictionary *scanOptions = @{CBCentralManagerScanOptionAllowDuplicatesKey:@YES};
    [self.centralManager scanForPeripheralsWithServices:services options:scanOptions];
}



要搞清楚所有的启动参数确实要费点力气。幸运的是一般的应用只需要处理其中的一两种就够了。

了解所有的可能性需要从理解概念走到亲自实现的步骤上。当你了解到事情有这么多可能性的时候,下一个改变世界的想法可能就已经在脑中开始酝酿了。