近日,苹果宣布了App Extensions for iOS 8,它允许开发人员将功能和内容扩展到单个应用程序之外。
其中,App Extensions允许修正的两个主要iOS限制是:由Open In系统创建的应用程序之间不必要的数据复制和无法替代苹果的键盘。这种状况与Android平台允许用户借助Widget和自定义键盘形成鲜明的对比。
然而,其中有一个最可能的误解需要澄清一下,就是iOS 8 App Extensions不同于Android Indents,Ars Technica网站撰稿人Andrew Cunningham这样写道。根据Google的描述:
Intent提供了一种机制,用于不同应用程序代码之间的后期运行时绑定。它主要用来启动Activities,因此可以将它看作是Activities之间的粘合剂。从本质上讲,它是一个无源数据结构,存放要执行动作的抽象描述。
虽然Extension在很多情况下与Intent没有什么不同,但在iOS 8中,App Extension系统的整体设计使得它与Intent有很大的不同。
Extension的生命周期
正如苹果文档中的描述,Extension是通过“包含应用程序(containing app)”提供的专门的二进制文件。包含应用程序只负责提供Extension,后者是独立运行的。尽管如此,一个iOS包含应用程序实际上还需要提供Extension之外的某些功能。OS X没有这样的要求,其上的包含应用程序不需要提供任何额外的功能。
文档提到,Extension的生命周期与它的包含应用程序完全没有关系,它由4个阶段组成:
- 用户选择一个App Extension
- 系统启动它
- App Extension运行
- 系统终止App Extension
如果两个应用程序需要同样的Extension做相同的工作,那么这会发生在两个独立的Extension进程中。
这一方法的主要动机是,通过生命周期短暂的Extension减少内存使用和能量消耗,并防止一个Extension的错误影响到使用了相同Extension的应用程序。
Extension的类型
Extension有多种类型,每一种类型都绑定到一个称为“扩展点(Extension point)”的系统区域:
- “今日(Today,又称为Widget)”:可以快速获取更新或者在通知中心的今日视图中执行一项快速任务。
- 共享:发布到一个共享网站或者与其它应用程序共享内容。
- 动作:在另一个应用程序的上下文中操作或查看内容。
- 照片编辑(仅限于iOS):在照片应用程序中编辑照片或视频。
- 查找器(仅限于iOS):在查找器中直接显示文件同步的状态信息。
- 文档提供程序(仅限于iOS):提供对文件库的访问和管理。
- 自定义键盘(仅限于iOS):用自定义键盘替代iOS系统键盘,并用于所有的应用程序中。
由于每个扩展点都有与之相关的使用策略和专门的API,开发人员必须为他们想要提供的那种功能选择恰当的扩展点。例如,在默认情况下,键盘Extension“不能访问网络,而且不能与其包含应用程序共享同一容器”。通过对Extension进行恰当的配置,这样的限制可以移除,但开发人员仍然需要遵守苹果应用商店审查指南和iOS开发者计划许可协议中的具体的网络键盘指南。
沙箱和安全
众所周知,每个iOS应用程序都有自己的沙箱。通过Mac苹果应用商店分发的OS X应用程序也有类似的要求,不过许多OS X应用程序是在Mac苹果应用商店之外分发的,并不需要遵守这一沙箱要求。
沙箱是苹果iOS安全策略的基石之一。沙箱是为了限制应用程序对文件、首选项、网络资源、硬件等的访问,具体来讲,其目的是为了限制受损的应用程序可能对系统造成的损害。
考虑到并不是所有可以用在应用程序中的API都可以用在Extension中,所以与通常的应用程序相比,App Extension运行在有更多限制的沙箱中。不能在Extension中使用的API标记为不可用宏,如NS_EXTENSIONS_UNAVAILABLE,它会在链接时导致失败。
此外,对于Extension与其它应用程序之间的通信,苹果有几项强制规定:
- 调用Extension的应用程序即主应用程序不能启动Extension;只有系统可以启动Extension。
- 当Extension启动后,主应用程序就和它直接通信。
- 主应用程序永远不和包含应用程序直接通信。
- Extension不是一个应用程序,但它由系统生成,并有它自己单独的进程。
- 为了在包含应用程序和它的Extension之间共享数据,包含应用程序及其Extension都必须是应用程序组的一部分。对于应用程序组的其中两个成员,部分数据可以在两者沙箱之外的第三个容器中共享。
正如Ars Technica的Andrew Cunningham总结的那样,这些规则的最终结果主要是一个应用程序不能进入另一个应用程序的沙箱。这与Android相反,在Android上,内容提供程序和解析程序仍然可以一起工作来为应用程序提供对其它应用程序中数据的访问。
反应
App Extension已经在iOS开发人员中间引发了极大的兴趣。Cunningham说,“Extension将会对新操作系统产生最大最显著的影响”。
MacStories的Federico Viticci收集了若干开发人员对苹果公告的反应,他说“Extension对于iOS应用程序生态系统的影响很难量化,但是……考虑到开发人员对苹果公告的反应,在今年秋天,我们将看到许多又新又酷的东西”。
另一方面,安全专家提出警告,更强大的功能往往带来更大的风险。安全公司Symantec写到:“在iOS 8发布之前,我们无法看到攻击是上升还是下降,因此,我们无法知道这些功能效果如何”,同时他们也承认“根据目前获得的信息,少数安全功能应该会增强iOS设备的防护等级”。
================读数据==================================================
转自王中周的技术博客
- @interface UIViewController(NSExtensionAdditions) <NSExtensionRequestHandling>
- // Returns the extension context. Also acts as a convenience method for a view controller to check if it participating in an extension request.
- @property (nonatomic,readonly,retain) NSExtensionContext *extensionContext NS_AVAILABLE_IOS(8_0);
- @end
- //通过openURL的方式启动Containing APP
- - (void)openURLContainingAPP
- {
- [self.extensionContext openURL:[NSURL URLWithString:@"appextension://123"]
- completionHandler:^(BOOL success) {
- NSLog(@"open url result:%d",success);
- }];
- }
- //让隐藏的插件重新显示
- - (void)showTodayExtension
- {
- [[NCWidgetController widgetController] setHasContent:YES forWidgetWithBundleIdentifier:@"com.wangzz.app.extension"];
- }
- //隐藏插件
- - (void)hiddeTodayExtension
- {
- [[NCWidgetController widgetController] setHasContent:NO forWidgetWithBundleIdentifier:@"com.wangzz.app.extension"];
- }
- TARGETS-->AppExtensionDemo-->Capabilities-->App Groups
- TARGETS-->TodayExtension-->Capabilities-->App Groups
- - (void)saveTextByNSUserDefaults
- {
- NSUserDefaults *shared = [[NSUserDefaults alloc] initWithSuiteName:@"group.wangzz"];
- [shared setObject:_textField.text forKey:@"wangzz"];
- [shared synchronize];
- }
- - (NSString *)readDataFromNSUserDefaults
- {
- NSUserDefaults *shared = [[NSUserDefaults alloc] initWithSuiteName:@"group.wangzz"];
- NSString *value = [shared valueForKey:@"wangzz"];
- return value;
- }
- - (BOOL)saveTextByNSFileManager
- {
- NSError *err = nil;
- NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.wangzz"];
- containerURL = [containerURL URLByAppendingPathComponent:@"Library/Caches/good"];
- NSString *value = _textField.text;
- BOOL result = [value writeToURL:containerURL atomically:YES encoding:NSUTF8StringEncoding error:&err];
- if (!result) {
- NSLog(@"%@",err);
- } else {
- NSLog(@"save value:%@ success.",value);
- }
- return result;
- }
- - (NSString *)readTextByNSFileManager
- {
- NSError *err = nil;
- NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.wangzz"];
- containerURL = [containerURL URLByAppendingPathComponent:@"Library/Caches/good"];
- NSString *value = [NSString stringWithContentsOfURL:containerURL encoding:NSUTF8StringEncoding error:&err];
- return value;
- }
- - (BOOL)copyFrameworkFromMainBundleToAppGroup
- {
- NSFileManager *manager = [NSFileManager defaultManager];
- NSError *err = nil;
- NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.wangzz"];
- NSString *sorPath = [NSString stringWithFormat:@"%@/Dylib.framework",[[NSBundle mainBundle] bundlePath]];
- NSString *desPath = [NSString stringWithFormat:@"%@/Library/Caches/Dylib.framework",containerURL.path];
- BOOL removeResult = [manager removeItemAtPath:desPath error:&err];
- if (!removeResult) {
- NSLog(@"%@",err);
- } else {
- NSLog(@"remove success.");
- }
- BOOL copyResult = [[NSFileManager defaultManager] copyItemAtPath:sorPath toPath:desPath error:&err];
- if (!copyResult) {
- NSLog(@"%@",err);
- } else {
- NSLog(@"copy success.");
- }
- return copyResult;
- }
- - (BOOL)loadFrameworkInAppGroup
- {
- NSError *err = nil;
- NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.wangzz"];
- NSString *desPath = [NSString stringWithFormat:@"%@/Library/Caches/Dylib.framework",containerURL.path];
- NSBundle *bundle = [NSBundle bundleWithPath:desPath];
- BOOL result = [bundle loadAndReturnError:&err];
- if (result) {
- Class root = NSClassFromString(@"Person");
- if (root) {
- Person *person = [[root alloc] init];
- if (person) {
- [person run];
- }
- }
- } else {
- NSLog(@"%@",err);
- }
- return result;
- }
- - (void)logAppPath
- {
- //app group路径
- NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.wangzz"];
- NSLog(@"app group:\n%@",containerURL.path);
- //打印可执行文件路径
- NSLog(@"bundle:\n%@",[[NSBundle mainBundle] bundlePath]);
- //打印documents
- NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
- NSString *path = [paths objectAtIndex:0];
- NSLog(@"documents:\n%@",path);
- }
- 2014-06-23 19:35:03.944 AppExtensionDemo[7471:365131] app group:
- /private/var/mobile/Containers/Shared/AppGroup/89CCBFB1-CA5E-4C7F-80CB-A3EB9E841816
- 2014-06-23 19:35:03.946 AppExtensionDemo[7471:365131] bundle:
- /private/var/mobile/Containers/Bundle/Application/1AC73797-A3BB-4BDE-A647-3D083DA6871A/AppExtensionDemo.app
- 2014-06-23 19:35:03.948 AppExtensionDemo[7471:365131] documents:
- /var/mobile/Containers/Data/Application/E5E6E516-0163-4754-9D10-A5F6C33A6261/Documents
- Jun 23 19:37:49 autonavis-iPad com.foogry.AppExtensionDemo.TodayExtension[7638] <Warning>: app group:
- /private/var/mobile/Containers/Shared/AppGroup/89CCBFB1-CA5E-4C7F-80CB-A3EB9E841816
- Jun 23 19:37:49 autonavis-iPad com.foogry.AppExtensionDemo.TodayExtension[7638] <Warning>: bundle:
- /private/var/mobile/Containers/Bundle/Application/596717B7-7CB8-4F53-BCD4-380F34ABD30F/AppExtensionDemo.app/PlugIns/com.foogry.AppExtensionDemo.TodayExtension.appex
- Jun 23 19:37:49 autonavis-iPad com.foogry.AppExtensionDemo.TodayExtension[7638] <Warning>: documents:
- /var/mobile/Containers/Data/PluginKitPlugin/57581433-3DBD-4930-971F-78D30C150E8A/Documents