AppExtension:扩展的学习笔记
一、概述
“扩展”的原理网上有很多,官网也清晰讲明,具体参见以下官方链接。
本文旨在整理笔记,简述相关原理,以备后续查看。
二、AppExtension基本原理
1、搞清楚几个
概念
-
App Extension,Apple定义为”扩展“,也可以理解为”插件“。
-
Host App,能够调起extension的app被称为host app。
An app that a user employs to choose an app extension is called a host app.
例如,widget的host app就是Today。 -
Containing App,包含一个或者多个的Extension的App叫做ContainingApp,也叫做宿主App。
an app that contains one or more extensions is called a containing app
2、iOS是如何处理扩展?
-
扩展不是独立App,系统将其初始化为单独的进程。
基于安全和性能的考虑,每一个扩展运行在一个单独的进程中,它拥有自己的bundle
,bundle
后缀名是.appex
。iOS系统把扩展定义为 额外功能的触发入口点,它不是一个独立的App。因此,它必须依赖于宿主App,不能单独存在,也就没办法单独提审AppStore。
An app extension is not an app. It implements a specific, well scoped task that adheres to the policies defined by a particular extension point.
An app extension is different from an app. Although you must use an app to contain and deliver your extensions, each extension is a separate binary that runs independent of the app used to deliver it.
3、扩展的生命周期问题(App Extension’s Life Cycle)。
![](https://img.haomeiwen.com/i740862/0c160b1c74b7cc41.png)
主要有以下四个步骤:
1、用户触发“扩展”,如UI触发或者代码触发。
2、iOS系统自动唤起“扩展”。
3、执行“扩展”代码。
4、执行完成后,系统杀死“扩展”,回收资源。
三、扩展相关的通信
1、通信归类
-
Host App 与 ContainingApp 无法直接通信!
An app extension’s containing app and the host app don’t communicate at all.
-
扩展与Host App的通信是基于
request/response
模型。
simple_communication_2x.png
-
一般,扩展与ContainingApp是无法直接通信的,例如扩展允许的时候,宿主App可能还未运行。
There is no direct communication between an app extension and its containing app; typically, the containing app isn’t even running while a contained extension is running.
2、扩展与ContainingApp的有限通信
the limited interaction available between an app extension and its containing app.
-
A Today widget
可以通过UrlSchemes的方式唤起ContainingApp。
通过NSExtensionContext
的方法openURL:completionHandler:
。 -
扩展和ContainingApp可以通过
shared container
实现间接通信。
数据共享shared container
与 扩展AppExtension 、ContainingApp的实现彼此双向数据通信。detailed_communication_2x.png
3、request/response
模型的理解
request/response
是通过 ”上下文“ 机制实现。HostApp为”扩展“提供运行的上下文(an extension context)
。大致流程是,HostApp将 RequestData
通过上下文(an extension context)
输送给 扩展AppExtension,扩展进行处理(UI显示,用户交互,代码处理等),处理完成后将 ResponseData
返回给HostApp。
由于”扩展“是一个独立的进程,一般一个App也是一个独立进程,因此它们间通信应该是基于进程间的通信方式。例如Socket、管道等,至于哪一种官方好像未说明(待研究)。
4、扩展(App Extension)与宿主App(Containing App)的数据共享shared container
虽然AppExtension的Bundle被导入如ContainingApp的包内,但是彼此的沙盒是没办法访问的。Apple发明了share container
的中间层,来实现彼此的数据共享问题。也被称之为AppGroup
的概念。
这也映射软件工程的一句经典话语:多了个中间层,一切都显得那么美好。
基本原理如下:Apple允许 App进程
和 扩展进程
都可以对SharedContainer
的共享数据区进行操作。
![](https://img.haomeiwen.com/i740862/0c276178dbd3b3d2.png)
如何集成AppGroup能力?
-
ContainingApp和Extension的Target中,分别都打开
AppGroup
的能力。
To enable data sharing, use Xcode or the Developer portal to enable app groups for the containing app and its contained app extensions.
-
对应的开发和发布证书都必须勾选开启AppGroup的能力。
-
同一个ContainingApp或者扩展下面,可以有多个AppGroup,根据名字区分,名字必须以
group.
开头。 -
开启AppGroup后,App和扩展工程目录下的环境文件
.entitlements
里面多了一个App Groups
。
FE61DC22-9CCC-416D-925A-55AEA2E9083D.png
支持的系统框架
-
NSUserDefaults
必须使用initWithSuiteName:
方法来初始化一个NSUserDefaults
对象,其中的SuiteName就是创建的Group的名字,然后利用这个对象来实现,跨应用的数据读写 -
NSFileManager
通过调用containerURLForSecurityApplicationGroupIdentifier:
方法可以获得AppGroup的共享目录,然后在此目录的基础上实现任意的文件操作。 -
CoreData
在初始化CoreData时,先使用NSFileManager取得共享目录,然后再指定共享目录为存储数据文件的目录。
共享数据的数据同步问题!
可以使用CoreData、SQList或者Posix locks锁技术,来实现数据同步问题。
Use Core Data, SQLite, or Posix locks to help coordinate data access in a shared container.
四、代码能力相关
1、能力限制
- 包含
NS_EXTENSION_UNAVAILABLE
宏声明的API,则此API在”扩展“中将不可用。
例如,扩展中无法使用+ (UIApplication *)sharedApplication
。 - 不能使用 sharedApplication 对象及其其中的方法。
- ”扩展“无法使用摄像头和麦克风。
- ”扩展“无法通过AirDrop接收数据。
- ”扩展“无法运行长时间的后台任务
long-running background tasks
。 - 系统严格限制 "扩展" 的内存
这是因为在同一时间可能会有多个扩展同时运行,如Widget扩展。
2、代码复用
- 内嵌一个framework文件在扩展和containing app之间共享代码。
假设你希望在你的containing app与扩展之间共享图片处理的代码,此时你可以将代码打包成framework文件,内嵌到两个目标中。对于内嵌框架中的代码,确保不包含扩展不允许使用的API。
官方自定义方案-Handling Common Scenarios
扩展Framework的要求
-
设置扩展Target的
Require Only App-Extension-Safe API
为YES,否则Xcode会提醒linking against dylib not safe for use in application extensions
。
To configure an app extension target to use an embedded framework, set the target’s “Require Only App-Extension-Safe API” build setting to Yes.If you don’t, Xcode reminds you to do so by displaying the warning “linking against dylib not safe for use in application extensions”.
-
提审必须是包含
arm64 (iOS) or x86_64 (OS X)
A containing app that links to an embedded framework must include the arm64 (iOS) or x86_64 (OS X) architecture build setting or it will be rejected by the App Store.
-
勾选
Copy Files build phase.
When configuring your Xcode project, you must choose “Frameworks” as the destination for your embedded framework in the Copy Files build phase.
五、实例
见另外一篇推送支持图片,详见:
AppExtension:NotificationServiceExtension集成、调试、打包的实践
参考
官网-Adding an App to an App Group.
官网-App Extensions Increase Your Impact
iOS 8新特性之扩展
App Extension编程指南(iOS8/OS X v10.10)中文版
iOS扩展开发攻略(一) - Share Extension
活久见的重构 - iOS 10 UserNotifications 框架解析