一、概述
“扩展”的原理网上有很多,官网也清晰讲明,具体参见以下官方链接。
本文旨在整理笔记,简述相关原理,以备后续查看。
二、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)。
主要有以下四个步骤:
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
的共享数据区进行操作。
如何集成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 框架解析