iOS8擴展插件開發配置
一.iOS8擴展插件概述
WWDC14除了發布了OS X v10.10和switf外,iOS8.0也開始變得更加開放了。說到開放,當然要數應用擴展(App Extension)了。顧名思義,應用擴展允許開發者擴展應用的自定義功能和內容,能夠讓用戶在使用其他應用程序時使用該項功能,從而實現各個應用程序間的功能和資源共享。可以將擴展理解為一個輕量級(nimble and lightweight)的分身。
以下為常見的三類插件:
Target Type
Extension point identifier
Scenarios
Today Extension
com.apple.widget-extension
系統通知欄下拉顯示
Share Extension
com.apple.share-services
Host App(照片、Safari、郵件、語音等)分享菜單第一行
Action Extension(UI)
com.apple.ui-services
Host App(照片、Safari、郵件、語音等)分享菜單第二行
下圖為iPhone/iOS8中的【照片】分享:
例如【微信】最多支持分享9張(NSExtensionActivationSupportsImageWithMaxCount=9)照片給好友或到朋友圈。
二.插件工作機制
1.插件只能與Host App通過上下文直接通信
2.插件可通過共享資源區與Containing App間接通信
3.Host App-Extension-Containing App工作流程
- Host App通過點擊系統分享菜單中的插件圖標調起擴展程序——Share/ActionExtension (*.appex)。
- iOS系統(Host App)通過擴展上下文(NSExtensionContext)向Share/ActionExtension傳遞欲分享的數據。
- Share/Action Extension提取數據並序列化到以AppGroup ID標識的共享資源區NSUserDefaults/AppGroup Container(containerURLForSecurityApplicationGroupIdentifier)中。
- Share/Action Extension通過URL Scheme呼起ContainingApp,同時插件通過上下文向iOS系統(HostApp)發出request completion通知,以便返回到Host App(iOS系統會dismiss插件UIViewController)。
- Containing App通過App Group ID從NSUserDefaults/containerURL中讀取分享過來的數據,並對分享數據進行後續處理。
由此可見,擴展插件將Host App與Containing App勾搭起來,而App Group Container則架起了數據交互的鵲橋。
這裏需要註意的是,在iOS 8.0中,只有Today Extension才支持直接調用NSExtensionContext的openURL:completionHandler:打開URL鏈接;Share/Action Extension要想實現URL Scheme,只能創建一個Sink UIWebVew對URL進行loadRequest實現曲線救國(所謂“Sink”是指隱而不顯,例如frame=CGRectZero)。
4.插件的UI形態
插件在UI上以UIViewController模式存在,被parentViewController(Host App)以模態窗口形式彈出(present as modal viewController)。
插件工程在Info.plist的NSExtension中通過NSExtensionMainStoryboard指定UI視圖入口。當然,如果不想使用storyboard,也可以使用NSExtensionPrincipalClass指定自定義UIViewController子類名(也可以封裝到UINavigationController)。
註意:
- 新建Extension Target後(Deployment Target≥8.0),需在Build Settings|Architectures|Valid Architectures中增加arm64!
- 初始安裝Containing App時,擴展插件並未使能,需要到【更多】中打開開關。
三.插件的局限性
以下文字節選自《App Extension Programming Guide》,主要列舉了插件的局限性,以知其可為不可為。
--------------------------------------------------------------------------------
1.Design a Streamlined UI
- An extension`s UI should be simple, restrained, and focused on facilitating a single task.
- To improve performance and the user`s experience, avoid including extraneous UI that doesn`t support your extension`s main task.
2.Optimize Efficiency and Performance
(1)App extensions should feel nimble and lightweight to users.
- Design your app extension to launch quickly, aiming for well under one second.
- An extension that launches too slowly is terminated by the system.
(2)Memory limits for running app extensions are significantly lower than the memory limits imposed on a foreground app.
- On both platforms, the system may aggressively terminate extensions because users want to return to their main goal in the host app.
- Some extensions may have lower memory limits than others.
(3)Your app extension doesn`t own the main run loop, so it`s crucial that you follow the established rules for good behavior in main runloops.
- For example, if your extension blocks the main runloop, it can create a bad user experience in another extension or app.
(4)Keep in mind that theGPU is a shared resource in the system.
- App extensions do not get top priority for shared resources; for example, a Today widget that runs a graphics-intensive game might give users a bad experience. The system is likely to terminate such an extension because of memory pressure.
- Functionality that makes heavy use of system resources is appropriate for an app, not an app extension.
--------------------------------------------------------------------------------
由此可見,iOS系統對插件要求簡潔至上:UI啟動要快、內存消耗要少、runloop執行耗時要短。
iOS系統對插件的限制決定了開發的插件必須輕量,發點Twitter/微博分享、小圖片文件分享、URL跳轉還是可以的;奢望豐富絢麗的UI或者用來傳大文件等大動作是不合適的。
當然,如果希望擴展(即使退出)執行長時間任務(比如上傳/下載),可以使用NSURLSession來創建一個上傳/下載session,並初始化一個後臺上傳/下載任務。
註意:
Apple也限制了擴展在API使用方面的權限,在擴展中禁用的API原型聲明被標上了NS_EXTENSION_UNAVAILABLE宏。例如:
+ (UIApplication*)sharedApplication NS_EXTENSION_UNAVAILABLE_IOS;
對sharedApplication的限制實際上就是不讓插件直接獲取訪問宿主應用(Host App的UIApplication)對象。
四.Share/Action擴展插件支持的媒體類型配置
Info.plist中的NSExtension|NSExtensionAttributes|NSExtensionActivationRule Dictionary可以配置插件支持的媒體類型及數量:
iOS擴展插件支持媒體類型配置鍵
描述
配置
說明
NSExtensionActivationSupportsAttachmentsWithMaxCount
附件最多限制
20
附件包括下面的File、Image和Movie三大類,單一、混選總量不超過20
NSExtensionActivationSupportsAttachmentsWithMinCount
附件最少限制
上面非零時,default=1
默認至少選擇1個附件,【分享】中才顯示擴展插件圖標
NSExtensionActivationSupportsFileWithMaxCount
文件最多限制
20
文件泛指除Image/Movie之外的附件,例如【郵件】附件、【語音備忘錄】等。
單一、混選均不超過20。
NSExtensionActivationSupportsImageWithMaxCount
圖片最多限制
20
單一、混選均不超過20
NSExtensionActivationSupportsMovieWithMaxCount
視頻最多限制
20
單一、混選均不超過20
NSExtensionActivationSupportsText
文本類型
default=0
默認不支持文本分享,例如【備忘錄】
NSExtensionActivationSupportsWebURLWithMaxCount
Web鏈接最多限制
default=0
默認不支持分享超鏈接,例如【Safari】
NSExtensionActivationSupportsWebPageWithMaxCount
Web頁面最多限制
default=0
默認不支持Web頁面分享,例如【Safari】
宿主應用(Host App)提供一個上下文(NSExtensionContext)向擴展(appex)傳遞數據,包含了待處理的數據(inputItems)。其傳遞的數據是一組NSExtensionItem對象,其中要分享的圖片、視頻、URL等附件就保存在NSExtensionItem的attachments數組中。
關於UTIs,參考UniformType Identifiers Reference | System-Declared Uniform Type Identifiers。
媒體類型
文件UTI
圖片(public.image)
kUTTypeImage
kUTTypeJPEG
kUTTypePNG
kUTTypeGIF
kUTTypeTIFF
kUTTypeBMP
kUTTypeICO
視頻(public.movie)
kUTTypeMovie
kUTTypeQuickTimeMovie
kUTTypeMPEG
kUTTypeMPEG4
kUTTypeAVIMovie
@"public.3gpp"
@"com.real.realmedia"
@"com.microsoft.windows-media-wmv"
@"com.microsoft.advanced-systems-format"
音頻(public.audio)
kUTTypeAudio
kUTTypeMP3
kUTTypeMPEG4Audio
kUTTypeWaveformAudio
@"com.microsoft.windows-media-wma"
文檔
kUTTypePDF
@"com.microsoft.word.doc"
@"com.microsoft.excel.xls"
kUTTypePresentation
@"com.microsoft.powerpoint.ppt"
@"com.apple.keynote.key"
壓縮包
kUTTypeZipArchive
kUTTypeGNUZipArchive
kUTTypeBzip2Archive
@"public.tar-archive"
@"org.gnu.gnu-zip-tar-archive"
五.插件與Containing App的App Group證書配置
Containing App及其Extension是通過以App Group ID標識的共享資源區—App Group Container來實現數據共享的。
Containing App及其Extension的App ID必須是Explicit,且Extension App ID必須以Containing App ID為Prefix/Seed,並且配置到同一App Group下。
App ID配置到Info.plist的BundleIdentifier中;App Group在target的【Xcode Target|Capabilities】中啟用,配置到【Xcode Target|Build Settings|Code Signing|Code Signing Entitlements】中的*.entitlements文件的com.apple.security.application-groups
鍵中。
證書和Provisioning Profile的申請以及Code Signing配置流程同以前普通的App,詳情請參考《iOS開發證書要點詳解》。
六.Containing App的Build Phases配置(embed app extensions)
Containing App 的【Xcode target|BuildPhases|Target Dependencies】中可以添加或移除插件target。
Containing App 的【Xcode target|BuildPhases|Embed App Extensions】下Destination為Plugins中可以添加或移除插件product(*.appex)。
註意:默認了勾選“Copy only when installing”,表示從AppStore安裝(包括升級覆蓋)時才拷貝插件。Xcode連接真機調試時,需取消該改項,否則系統【分享】菜單中不會出現插件
iOS8擴展插件開發配置