Objective-C中的App Extensions扩展应用
2024-01-196.9k 阅读
一、App Extensions 概述
在 iOS 和 macOS 开发中,App Extensions 为应用程序提供了一种扩展功能和与其他应用或系统集成的方式。通过 App Extensions,一个应用可以在其他应用的上下文中执行特定的任务,从而增强用户体验并实现更丰富的功能。例如,一个图像编辑应用可以提供一个 Share Extension,让用户在其他应用(如照片应用)中分享编辑后的图片;或者一个笔记应用可以提供一个 Action Extension,让用户在 Safari 浏览器中直接保存网页内容到笔记应用。
(一)App Extensions 的类型
- Share Extensions:用于在不同应用之间共享内容。比如,用户可以从照片应用中选择一张图片,通过 Share Extension 将其分享到社交应用(如微信、微博等)。
- Action Extensions:允许用户在其他应用中执行特定的操作。例如,在 Safari 中,用户可以使用一个翻译应用的 Action Extension 来翻译网页上选中的文本。
- Today Extensions:显示在通知中心的 Today 视图中,提供快速查看和交互的功能。比如,天气应用的 Today Extension 可以让用户在通知中心直接查看当前天气信息。
- Photo Editing Extensions:用于在照片应用中对图片进行编辑。开发者可以创建自定义的图片编辑工具,让用户在照片应用内使用这些工具处理图片。
- Document Provider Extensions:使应用能够在文件管理系统(如 iOS 上的 Files 应用)中提供文档。这样,用户可以在不同应用之间共享和访问文档。
(二)App Extensions 的优势
- 增强用户体验:用户无需在多个应用之间频繁切换,就能完成复杂的任务。例如,使用 Share Extension 分享内容时,操作更加便捷。
- 提高应用的曝光度:通过与其他应用集成,扩展应用的功能范围,吸引更多用户使用。比如,一个提供实用 Action Extension 的应用,可能会吸引更多用户下载主应用。
- 促进应用间的协作:不同应用可以通过 App Extensions 实现功能互补,共同为用户提供更好的服务。例如,文档编辑应用和云存储应用通过 Document Provider Extension 协作,方便用户管理和共享文档。
二、创建 Objective-C 的 App Extensions
(一)创建 Share Extension
- 新建项目:在 Xcode 中,选择“File” -> “New” -> “Target”。在弹出的对话框中,选择“Share Extension”,然后点击“Next”。
- 配置项目:输入扩展的名称、组织标识符等信息,然后点击“Finish”。Xcode 会自动生成 Share Extension 的基本结构。
- 实现分享逻辑:打开生成的
ShareViewController.m
文件,在viewDidLoad
方法中可以获取到需要分享的内容。例如:
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *contentItems = self.extensionContext.inputItems;
for (NSExtensionItem *item in contentItems) {
for (NSItemProvider *itemProvider in item.attachments) {
if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeText]) {
[itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeText options:nil completionHandler:^(NSString *text, NSError *error) {
if (text) {
NSLog(@"分享的文本内容: %@", text);
// 在此处实现分享逻辑,比如将文本发送到某个服务器
}
}];
}
}
}
}
- 完成分享:分享完成后,需要通知系统。在
ShareViewController.m
中添加如下代码:
- (IBAction)done:(id)sender {
[self.extensionContext completeRequestReturningItems:nil completionHandler:nil];
}
- 配置 Share Extension:在
Info.plist
文件中,可以配置 Share Extension 支持的内容类型等信息。例如,添加对文本和图片的支持:
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsText</key>
<true/>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>1</integer>
</dict>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).ShareViewController</string>
</dict>
(二)创建 Action Extension
- 新建项目:同样在 Xcode 中,选择“File” -> “New” -> “Target”,然后选择“Action Extension”,点击“Next”进行项目配置并创建。
- 实现操作逻辑:打开
ActionViewController.m
文件,在viewDidLoad
方法中获取操作的输入内容。例如:
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *inputItems = self.extensionContext.inputItems;
for (NSExtensionItem *item in inputItems) {
for (NSItemProvider *itemProvider in item.attachments) {
if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeText]) {
[itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeText options:nil completionHandler:^(NSString *text, NSError *error) {
if (text) {
NSLog(@"操作的文本内容: %@", text);
// 在此处实现具体的操作逻辑,比如对文本进行加密
}
}];
}
}
}
}
- 返回操作结果:操作完成后,需要返回结果给调用的应用。在
ActionViewController.m
中添加如下代码:
- (IBAction)done:(id)sender {
NSExtensionItem *outputItem = [[NSExtensionItem alloc] init];
NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithItem:@"操作后的结果" typeIdentifier:(NSString *)kUTTypeText];
outputItem.attachments = @[itemProvider];
[self.extensionContext completeRequestReturningItems:@[outputItem] completionHandler:nil];
}
- 配置 Action Extension:在
Info.plist
文件中配置 Action Extension 的相关信息,比如支持的内容类型和操作类型:
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsText</key>
<true/>
</dict>
<key>NSExtensionCategory</key>
<string>com.apple.category.editor</string>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.ui-services</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).ActionViewController</string>
</dict>
(三)创建 Today Extension
- 新建项目:在 Xcode 中,选择“File” -> “New” -> “Target”,然后选择“Today Extension”,点击“Next”完成项目创建。
- 布局界面:打开
TodayViewController.m
文件,在viewDidLoad
方法中可以设置界面布局。例如:
- (void)viewDidLoad {
[super viewDidLoad];
self.preferredContentSize = CGSizeMake(0.0, 100.0);
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 200, 30)];
label.text = @"今日信息";
[self.view addSubview:label];
}
- 更新数据:可以在
viewWillAppear:
方法中更新 Today Extension 显示的数据。例如:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// 从服务器获取最新数据并更新界面
UILabel *label = (UILabel *)[self.view viewWithTag:100];
label.text = @"新的今日信息";
}
- 配置 Today Extension:在
Info.plist
文件中可以配置 Today Extension 的相关属性,如背景颜色、是否支持展开等:
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>WidgetBackgroundPreference</key>
<string>dark</string>
<key>WidgetAllowsEditing</key>
<true/>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widget-extension</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).TodayViewController</string>
</dict>
三、App Extensions 与主应用的交互
(一)共享数据
- 使用 App Groups:
- 创建 App Group:在 Xcode 中,选择主应用的项目,在“Capabilities”选项卡中,开启“App Groups”,并创建一个新的 App Group,格式为
group.com.example.appgroup
。然后为 Extension 也开启“App Groups”,并选择相同的 App Group。 - 共享数据示例:在主应用中写入数据:
- 创建 App Group:在 Xcode 中,选择主应用的项目,在“Capabilities”选项卡中,开启“App Groups”,并创建一个新的 App Group,格式为
NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.example.appgroup"];
[sharedDefaults setObject:@"一些共享数据" forKey:@"sharedKey"];
[sharedDefaults synchronize];
在 Extension 中读取数据:
NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.example.appgroup"];
NSString *sharedData = [sharedDefaults objectForKey:@"sharedKey"];
NSLog(@"从主应用获取的共享数据: %@", sharedData);
- 使用文件共享:
- 配置文件共享:在主应用和 Extension 的
Info.plist
文件中,添加NSSupportsDocumentBrowser
键,并设置为YES
。 - 共享文件示例:在主应用中保存文件到共享目录:
- 配置文件共享:在主应用和 Extension 的
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.com.example.appgroup"];
NSURL *fileURL = [containerURL URLByAppendingPathComponent:@"sharedFile.txt"];
NSString *content = @"这是要共享的文件内容";
NSError *error;
[content writeToURL:fileURL atomically:YES encoding:NSUTF8StringEncoding error:&error];
if (error) {
NSLog(@"保存文件错误: %@", error);
}
在 Extension 中读取共享文件:
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.com.example.appgroup"];
NSURL *fileURL = [containerURL URLByAppendingPathComponent:@"sharedFile.txt"];
NSString *fileContent = [NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:nil];
NSLog(@"从共享文件读取的内容: %@", fileContent);
(二)通信机制
- 使用 Notification Center:
- 在主应用中发送通知:
[[NSNotificationCenter defaultCenter] postNotificationName:@"MyExtensionNotification" object:nil userInfo:@{@"message": @"通知内容"}];
- 在 Extension 中接收通知:在
viewDidLoad
方法中添加如下代码:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveNotification:) name:@"MyExtensionNotification" object:nil];
然后实现 receiveNotification:
方法:
- (void)receiveNotification:(NSNotification *)notification {
NSDictionary *userInfo = notification.userInfo;
NSString *message = userInfo[@"message"];
NSLog(@"接收到主应用的通知: %@", message);
}
- 使用 URL Scheme:
- 配置 URL Scheme:在主应用的
Info.plist
文件中,添加CFBundleURLTypes
数组,设置CFBundleURLName
和CFBundleURLSchemes
。例如:
- 配置 URL Scheme:在主应用的
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>com.example.app</string>
<key>CFBundleURLSchemes</key>
<array>
<string>myappscheme</string>
</array>
</dict>
</array>
- 在 Extension 中打开主应用并传递参数:
NSURL *url = [NSURL URLWithString:@"myappscheme://param1=value1¶m2=value2"];
[self.extensionContext openURL:url completionHandler:^(BOOL success) {
if (success) {
NSLog(@"成功打开主应用");
} else {
NSLog(@"打开主应用失败");
}
}];
- 在主应用中接收参数:在
AppDelegate.m
中实现application:openURL:options:
方法:
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
NSString *query = url.query;
NSLog(@"从 Extension 接收到的参数: %@", query);
return YES;
}
四、App Extensions 的性能优化
(一)资源管理
- 内存管理:在 App Extensions 中,由于其运行在其他应用的上下文环境中,内存使用需要特别注意。避免创建大量不必要的对象,及时释放不再使用的对象。例如,在处理图片的 Photo Editing Extension 中,加载图片后及时释放图片数据,防止内存泄漏:
UIImage *image = [UIImage imageWithData:imageData];
// 对图片进行处理
image = nil; // 处理完成后释放图片对象
- CPU 资源:尽量优化算法,减少复杂计算。如果需要进行大量计算,可以考虑使用异步任务或者分块处理。例如,在 Action Extension 中对文本进行加密操作,如果加密算法复杂,可以使用
dispatch_async
将加密任务放到后台线程执行:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *encryptedText = [self encryptText:inputText];
dispatch_async(dispatch_get_main_queue(), ^{
// 返回加密后的文本给调用应用
});
});
(二)加载速度优化
- 延迟加载:对于一些不急需的资源,采用延迟加载策略。例如,在 Today Extension 中,如果某些数据不是每次都需要立即显示,可以在用户展开扩展或者特定操作时再加载。在
viewDidLoad
方法中可以先设置一个占位视图,然后在需要时加载实际数据并更新视图:
- (void)viewDidLoad {
[super viewDidLoad];
UIView *placeholderView = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 200, 30)];
placeholderView.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:placeholderView];
// 延迟加载实际数据
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[placeholderView removeFromSuperview];
// 加载实际数据并显示
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 200, 30)];
label.text = @"实际数据";
[self.view addSubview:label];
});
}
- 缓存机制:对于经常使用的数据或者资源,建立缓存机制。比如,在 Share Extension 中,如果经常分享相同类型的内容(如文本),可以缓存上次分享的处理结果,下次分享相同内容时直接使用缓存数据,提高分享速度。在
ShareViewController.m
中可以添加一个缓存字典:
@property (nonatomic, strong) NSMutableDictionary *shareCache;
- (void)viewDidLoad {
[super viewDidLoad];
self.shareCache = [NSMutableDictionary dictionary];
}
// 分享处理方法
- (void)handleShareContent:(NSString *)content {
if ([self.shareCache objectForKey:content]) {
// 使用缓存结果
NSLog(@"使用缓存的分享结果: %@", [self.shareCache objectForKey:content]);
} else {
// 处理分享内容
NSString *result = [self processShareContent:content];
[self.shareCache setObject:result forKey:content];
NSLog(@"新的分享结果: %@", result);
}
}
五、App Extensions 的注意事项
(一)权限问题
- 数据访问权限:不同类型的 App Extensions 对数据的访问权限不同。例如,Share Extension 只能访问用户明确选择要分享的内容,不能随意获取其他应用的所有数据。在开发时,要确保遵循系统的权限规定,避免非法获取数据。如果需要访问特定类型的数据(如联系人信息),需要在
Info.plist
文件中添加相应的权限描述,并在使用时请求用户授权:
<key>NSContactsUsageDescription</key>
<string>你的应用需要访问你的联系人数据,以便分享联系人信息。</string>
然后在代码中请求授权:
CNContactStore *store = [[CNContactStore alloc] init];
[store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted) {
// 可以访问联系人数据
} else {
// 未获得授权
}
}];
- 系统功能权限:某些系统功能(如相机、麦克风)在 App Extensions 中使用也需要特定权限。比如,在创建一个带有拍照功能的 Action Extension 时,需要在
Info.plist
文件中添加相机权限描述:
<key>NSCameraUsageDescription</key>
<string>你的应用需要访问相机,以便拍照。</string>
然后在使用相机时进行权限检查和请求:
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (authStatus == AVAuthorizationStatusDenied || authStatus == AVAuthorizationStatusRestricted) {
// 权限被拒绝或受限
} else if (authStatus == AVAuthorizationStatusNotDetermined) {
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if (granted) {
// 获得权限
} else {
// 未获得权限
}
}];
} else {
// 已有权限
}
(二)兼容性问题
- 系统版本兼容性:不同系统版本对 App Extensions 的支持可能存在差异。在开发时,要测试不同系统版本下扩展的功能是否正常。例如,某些新的系统特性可能只在特定版本以上支持,如果在低版本系统中使用这些特性,可能导致扩展崩溃。可以在代码中使用系统版本判断来提供兼容的实现:
if (@available(iOS 13.0, *)) {
// 使用 iOS 13 及以上的新特性
} else {
// 提供低版本系统的兼容实现
}
- 设备兼容性:不同设备(如 iPhone、iPad)的屏幕尺寸、性能等方面存在差异。在设计 App Extensions 的界面和功能时,要考虑到这些差异。比如,在 Today Extension 中,要确保在不同设备的通知中心中都能正常显示和交互。可以使用 Auto Layout 或者 Size Classes 来适配不同设备的屏幕尺寸:
// 使用 Auto Layout 约束
UILabel *label = [[UILabel alloc] init];
label.text = @"示例文本";
[self.view addSubview:label];
[label.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:10].active = YES;
[label.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:10].active = YES;
(三)审核问题
- 功能完整性:App Extensions 的功能必须完整且符合预期。例如,Share Extension 必须能够正确分享内容,Action Extension 必须能够正确执行操作。在提交审核前,要进行充分的测试,确保扩展在各种情况下都能正常工作。
- 隐私政策:如果 App Extensions 涉及收集用户数据,必须有明确的隐私政策,并在应用内或相关文档中说明数据的收集、使用和共享方式。隐私政策需要符合苹果的审核要求,否则可能导致审核不通过。
- 用户体验:扩展的用户体验要良好,界面简洁易用。避免出现卡顿、崩溃等问题,确保扩展的加载速度和响应速度符合用户期望。在审核过程中,苹果会重点关注用户体验方面的问题。
通过以上对 Objective - C 中 App Extensions 扩展应用的详细介绍,开发者可以更好地利用这一强大功能,为用户提供更丰富、便捷的应用体验,同时在开发过程中注意各种细节和问题,确保扩展应用能够顺利通过审核并稳定运行。