iOS應用間通信:URL Schemes
拋開越獄不談,URL Schemes幾乎是iOS應用間通信(Inter-app Communication)的唯一選擇(另一種是Air Drop,但主要用于共享大文件),其重要性毋庸置疑。
更新:Apple在iOS9推出了Universal Links,同樣基于URL,力求統一原生應用和web服務的用戶體驗,可視為URL Schemes的全面升級。當然,其實現也更為復雜。
A. 什么是URL Schemes
URL用于定位資源,譬如網絡資源。以下面的URL為例:
http://www.example.com/index.php?key1=value1&key2=value2
根據RFC1808標準,其包含如下組成部分:
內容 | 角色 | 作用 |
---|---|---|
http | scheme | 服務類型。注意,http是一種互聯網協議,但理論上任意合法字符串都可以充當scheme |
www.example.com | host | 主機域名 |
index.php | path | 資源路徑 |
key1=value1&key2=value2 | query | 參數 |
iOS中,你可以為自己的應用定義URL schemes,供外界調用。URL格式必須符合標準(即能夠通過NSURL解析)。
總體來說,URL schemes可劃分為兩類:系統定義&自定義。
B. 系統定義的URL Schemes
有些系統應用天生支持URL schemes,例如電話,郵件,短信,Safari,地圖等。
/ | scheme | 范例 | 效果 | 備注 |
---|---|---|---|---|
電話 | tel | tel:16812345678 | 撥打號碼16812345678 | 電話號碼必傳 |
郵件 | mailto | mailto:frank@163.com | 給frank@163.com寫郵件 | 郵件地址必傳 |
短信 | sms | sms:16812345678 | 編寫發送給號碼16812345678的短信 | 電話號碼不是必傳,如不傳,則僅打開短信應用 |
Safari | http(s) | https://www.baidu.com | 在Safari中打開網頁 | 絕大部分http地址都默認使用Safari打開 |
地圖 | http(s) | http://maps.apple.com/?q=四川菜 | 搜索附近的四川菜。注意,url含有中文時要編碼 | 地圖的scheme并不是map,而是一個host為maps.apple.com 的http地址 |
更多關于系統定義的URL schemes的信息,詳見官方文檔Apple URL Scheme Reference。
C. 調用URL Scheme
調用URL scheme其實很簡單,分為兩步:
- 創建URL;
- 要求UIApplication打開它;
注意事項:
- 調用特定app,必須事先知道其scheme;
- 除scheme外,有些app還要求傳遞額外信息;信息錯誤,可能無法達到預期效果;
- 自定義schemes與系統schemes發生沖突,默認以后者為準;
- 多個app注冊同一個scheme,調用結果未知;
打開URL的方法如下:
- (void)openURL:(NSURL *)URL completionHandler:(void (^)(BOOL success))completionHandler;
- 此方法自iOS10引入,低版本請使用openURL:;
- 回調completionHandler攜帶一個布爾參數success,表示是否成功打開URL。注意,這里的成功意味著有app響應URL scheme,從而被調起;至于URL是否被成功處理,不得而知;
- 也就是說,只要scheme正確,一定會有app被調起,回調一定顯示成功;
我們可以嘗試通過Safari調用某個scheme,具體做法為:在地址欄里輸入targetScheme://,將targetScheme替換為具體scheme即可。注意,://不可省略;如果包含中文,必須編碼。
D. 自定義URL Schemes
自定義URL schemes也可以分為兩步:
- 注冊schemes;
- 處理調用請求。
D.1 注冊schemes
iOS以URL type為單位管理URL schemes。一個type下可以有多個scheme,但一個scheme只對應一個type。注冊URL schemes,實際上是注冊URL type。
在Info.plist
中添加鍵值對CFBundleURLTypes
,其對應一個數組,每個元素都是一個字典,代表一個type。例如:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLIconFile</key>
<string>iconGinx</string>
<key>CFBundleURLName</key>
<string>cn.com.rap.ginx</string>
<key>CFBundleURLSchemes</key>
<array>
<string>wb2522720237</string>
<string>wxd3a1541d4423bf8f</string>
<string>ddyc</string>
<string>tencent1101352712</string>
<string>navigationMapBack</string>
</array>
</dict>
<!--其他URL type...-->
<array>
一個URL type字典包含如下鍵值對:
鍵 | 值 | 必填 | 備注 |
---|---|---|---|
CFBundleURLSchemes | 字符串數組,一個字符串代表一個scheme | 是 | 一個type下可以有多個scheme |
CFBundleURLName | type的識別符,必須唯一。推介使用反向DNS風格的命名方式,如com.myhost.myscheme。 | 是 | 具體作用不詳 |
CFBundleURLIconFile | type的圖標名稱 | 否 | 圖標用途不詳 |
CFBundleTypeRole | app在type中所扮演的角色 | 是 | 具體作用不詳,使用默認值即可 |
更多關于CFBundleURLTypes的信息,詳見Information Property List Key Reference中章節CFBundleURLTypes的敘述。
此外,還可以針對scheme定義啟動圖片。眾所周知,app啟動時會顯示圖片。如果app因為響應某個scheme而啟動,可以根據scheme定義圖片。圖片命名格式如下:
<basename> -<url_scheme> <other_modifiers> .png
更多關于URL scheme啟動圖片的信息,詳見App Programming Guide for iOS中章節Displaying a Custom Launch Image When a URL is Opened的敘述。
D.2 處理調用請求
D.2.1 處理邏輯
收到調用請求后,相應的UIApplication代理方法會被調用,所以這里也是處理邏輯的所在:
// UIApplicationDelegate
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options
注意事項:
- 此方法自iOS9引入,低版本請使用application:openURL:sourceApplication:annotation:
; - url是調用者傳入的url,利用NSURL解析,獲取所需信息;
- options是字典,通過鍵值對UIApplicationOpenURLOptionsSourceApplicationKey可以獲取調用者的Bundle ID;
- 此方法返回一個布爾值,代表處理成功與否;但不影響調用者的回調結果(方法
openURL:completionHandler:
)。所以這個值的具體作用不詳。
D.2.2 生命周期
被調用時,app可能處于下列狀態之一:
- app未運行;
- app運行中,但在后臺或被掛起;
D.2.2.1 app未運行時被調用
app先啟動,再處理請求,但受到下面方法影響:
// UIApplicationDelegate
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
- 如果任意方法返回NO,則不處理請求(即方法
application:openURL:options:
不調用); - 如果只實現其中一個,則以實現的那個為準;
- 即使不處理請求,app仍會啟動,進入前臺;調用者收到成功回調(方法
openURL:completionHandler:
回調參數顯示成功);
D.2.2.1 app運行中被調用
app必定
會處理請求,進入前臺(即方法application:openURL:options:
一定會被調用);
E. LSApplicationQueriesSchemes與canOpenURL:
UIApplication方法canOpenURL:可以判斷當前設備上是否有能夠響應特定URL的應用。
于是乎,有人利用這個方法過濾大量scheme,判斷設備上安裝了哪些應用。為防止濫用,自iOS9,Apple要求這個方法只能檢測特定名單內的scheme(當然,系統定義的scheme不在此列),開發者需要通過鍵值對LSApplicationQueriesSchemes在Info.plist
中定義這個名單。例如:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>alipay</string>
<string>tencentweibo</string>
<string>sinaweibo</string>
<string>weibo</string>
<string>mqq</string>
<string>iosamap</string>
<string>baidumap</string>
<string>wechat</string>
<string>weixin</string>
<string>sinaweibohd</string>
<string>weibosdk</string>
<string>weibosdk2.5</string>
<string>BestPay</string>
</array>
另外,還要注意:
- 方法
canOpenURL:
的返回值僅表示當前設備上是否有能夠響應特定URL的應用。并不能反映URL能否被成功處理; - 方法
openURL:completionHandler:
(或openURL:
)不受此名單限制;
更多關于LSApplicationQueriesSchemes的信息,詳見Information Property List Key Reference中章節LSApplicationQueriesSchemes的敘述。