在說widget開發前,先來了解下APP Extensions和App Groups:
一、關于App Extensions
extension是iOS8新開放的一種對幾個固定系統區域的擴展機制,它可以在一定程度上彌補iOS的沙盒機制對應用間通信的限制。
extension的出現,為用戶提供了在其它應用中使用我們應用提供的服務的便捷方式,比如用戶可以在Today的widgets中查看應用展示的簡略信息,而不用再進到我們的應用中,這將是一種全新的用戶體驗;但是extension的出現可能會減少用戶啟動應用的次數。
了解幾個關鍵詞
extension point:
系統中支持extension的區域,extension的類別也是據此區分的,iOS上共有Today、Share、Action、Photo Editing、Storage Provider等多種,其中Today中的extension又被稱為widget。
每種extension point的使用方式和適合干的活都不一樣,因此不存在通用的extension。
app extension:
extension并不是一個獨立的app,它有一個包含在app bundle中的獨立bundle,extension的bundle后綴名是.appex。其生命周期也和普通app不同(后文將會描述)。
extension不能單獨存在,必須有一個包含它的containing app。
extension需要用戶手動激活,不同的extension激活方式也不同,比如: 比如Today中的widget需要在Today中激活和關閉;
containing app:
盡管蘋果開放了extension,但是在iOS中extension并不能單獨存在,要想提交到AppStore,必須將extension包含在一個app中提交,并且app的實現部分不能為空,這個包含extension的app就叫containing app。
extension會隨著containing app的安裝而安裝,同時隨著containing app的卸載而卸載。
host app
能夠調起extension的app被稱為host app,比如widget的host app就是Today。
extension和containing app之間的關系:
1、不能直接通信:盡管extension的bundle是放在containing app的bundle中,但是他們是兩個完全獨立的進程,之間不能直接通信。不過extension可以通過openURL的方式啟動containing app(當然也能啟動其它app),不過必須通過extensionContext借助host app來實現:
- (void)jumpToWidgetAPP:(UIButton *)sender {
[self.extensionContext openURL:[NSURL URLWithString:@"WidgetDemo://xxx"] completionHandler:^(BOOL success) {
NSLog(@"open url result: %d",success);
}];
}
2、可以共享資源數據:extension和containing app可以共同讀寫一個被稱為Shared resources的存儲區域,這是通過App Groups實現的,后文將會詳述。
3、containing app能夠控制extension的出現和隱藏:
讓隱藏的插件重新顯示YES/隱藏NO
[[NCWidgetController widgetController] setHasContent:YES forWidgetWithBundleIdentifier:@"com.w.app.extension"];
extension和containing app以及host app三者之間的關系:
三者之間的關系可以用官網的兩張圖片形象說明;
?二、關于App Groups
App Groups是iOS8新開放的功能,在OS X上早就可用了,它主要用于同一group下的app共享同一份讀寫空間,以實現數據共享。extension和containing app共同讀寫一份數據是很合理的需求,比如系統的股市應用,widget和app中都需要展示幾個公司的股票數據,這就可以通過App Groups實現。(如何開啟以及使用App Groups下文會說明 )
三、widget開發過程:
接下來我們從環境搭建和業務邏輯實現兩大方面來說一下widget開發的過程:(接下來的提到的主項目即為上面說到的containing app)
環境搭建:
1、添加widget項目
在APP中選擇target-》 添加一個類型為Today Extension的工程,就是我們想要的widget;含有widget的app目錄結構如下:
2、配置證書
由于widget項目和主項目其實是兩個獨立的appID,因為需要單獨給widget配置證書,配置證書的過程參考APP證書配置;
3、開啟APP Groups
開啟APP Groups是為了widget和app之間實現數據共享;為了便于后續操作,請先確保你的開發者賬號在Xcode上處于登錄狀態。
在app中開啟:
TARGETS-->AppExtensionDemo-->Capabilities-->App Groups
找到以后,將App Groups右上角的開關打開,然后選擇添加groups,注意命名要規范,比如:group.com.company.app;
在extension中開啟:假設創建widget target的名稱為TodayExtension,對應的App Group位于
TARGETS-->TodayExtension-->Capabilities-->App Groups
開啟的方式和APP中一樣,注意必須要保證這里的App Groups名稱和APP中相同。
業務邏輯實現:
搭建好開發環境后接下來主要從幾個功能實現的角度來談談如何進行widget開發。
1、純代碼實現布局
widget項目默認是使用storyboard作為布局的,如果喜歡用純代碼實現布局,則需要做如下處理:
(1)刪除Storyboard文件,并在widget項目的plist,刪除NSExtensionMainStoryboard;
(2)添加NSExtensionPrincipalClass字段 并設為TodayViewController;
2、設置展開/折疊:
在系統提供的方法中設置widget展開/折疊的高度,即可實現widget的展開/折疊,但是不能主動設置在什么情況下是展開的,在什么情況下的折疊的,也就是說不能代碼控制展開還是折疊,只能是用戶點擊按鈕來展開和折疊,蘋果沒有給出那么多的自由。
3、共享數據
widget做為主項目的擴展,必然要經常和主項目共享數據,上面我們已經提到可以通過App Groups來進行數據共享,具體實現上主要有NSUserDefault和NSFileManager兩種方式進行數據共享。
3.1 通過NSUserDefaults共享數據
主項目中存數據:通過以下方式向NSUserDefaults中保存數據:需要注意的是:
1、保存數據的時候必須指明group id;
2、而且要注意NSUserDefaults能夠處理的數據只能是可plist化的對象,詳情見Property List Programming Guide。
3、為了防止出現數據同步問題,不要忘記調用[shared synchronize];
widget項目中取數據 :對應的讀取數據方式:
3.2 通過NSFileManager共享數據
主項目中存數據:
widget項目中取數據:
4、使用主項目的自定義類,以及第三方庫
widget開發時,肯定會遇到想使用主項目的類的情況,只需要將要使用的類的.m文件,多加上一個target,選擇widget項目即可。(如圖)
使用主項目中的第三方庫:
我們知道,通過pods維護第三方庫的時候Podfile文件會指定每個第三方庫要加入的target,因此Widget如果想使用第三方庫,只需要在在widget的target中,加上要使用的第三方庫即可;
target 'CSMBP-Widget' do
pod 'AFNetworking', '3.1.0'
end
5、喚起主項目
可以通過URL Schemes的方式從widget項目跳轉到APP中;
(1)在主項目里配置一個對應widget的URL:
(2)在需要喚起主項目的地方通過self.extensionContext來實現跳轉:
[self.extensionContext openURL:[NSURL URLWithString:@"WidgetDemo://xxx"] completionHandler:^(BOOL success) {
NSLog(@"open url result: %d",success);
}];
(3)在主項目appdelegate的openURL代理方法中,截取URL并做處理:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
if ([url.description hasPrefix:@"WidgetDemo://"]) {
// 根據url處理具體跳轉頁面
}
return YES;
}