###### tags: `第14屆IT邦鐵人賽文章`
# 【在 iOS 開發路上的大小事2-Day17】Application Extension 簡介

## 前情提要
Application Extension 是從 iOS 8 開始出現的一種使用者互動介面
Extension 顧名思義就是擴展的意思,而 Application Extension 就是 App 的擴展
所以說 Application Extension 本身並不是單獨一個 App,而是需要仰賴 App 才能運作的
而 Application Extension 本身的 LifeCycle (生命週期) 也跟一般 App 不同
有些是需要使用者手動啟動,有些則是可以在各個 App 中被啟動
像是鍵盤輸入法 (Custom Keyboard Extension)、密碼自動填入 (AutoFill Credential Provider) 等就是需要使用者手動去啟動
而 Share Extension、Action Extension 這兩個則是可以在各個 App 中被啟動
## LifeCycle 生命週期
前面有說到 Application Extension 的 LifeCycle 跟一般 App 是不一樣的
讓我們來看一下 Application Extension 是怎麼運作的

▲ 圖取自 [Apple Developer Documentation](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionOverview.html)
1. 使用者啟動 Application Extension
2. 系統啟動 Application Extension
3. 執行 Application Extension 內的程式
4. 系統關閉 Application Extension
## 與 App 之間的溝通
開頭有說到 Application Extension 是需要依賴 App 本體才能運作
既然是這樣,那麼 Application Extension 跟 App 也是可以相互溝通的
讓我們來看一下 Application Extension 跟 App 之間是怎麼相互溝通
### 簡單版

▲ 圖取自 [Apple Developer Documentation](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionOverview.html)
### 進階版

▲ 圖取自 [Apple Developer Documentation](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionOverview.html)
### 名詞解說
```
Containing App:App 本體
App Extension:Containing App 的擴展,需依賴於 Containing App
Host App:呼叫 App Extension 的 App
```
在簡單版中,可以看到 Host App 呼叫的是 App Extension 而不是 Containing App
App Extension 與 Host App兩者之間可以進行 Request 跟 Response 溝通
而 App Extension 與 Containing App 之間以虛線標示是因為
App Extension 執行的時候 Containing App 是可以不用執行的
也就是 App Extension 與 Containing App 之間是可以不用有溝通的
接著,我們來看進階版,App Extension 與 Host App 之間的溝通一樣
但與 Containing App 之間卻不一樣了,兩者之間多了 ```Open URL```、```Shared resources``` 互動
#### Open URL
首先是 Open URL,這個是只有 Widget Extension 才能使用
透過下面這個 Function 來實作,用於點擊 Widget 後,跳回 Containing App
```swift
func open(_ URL: URL, completionHandler: ((Bool) -> Void)? = nil)
```
#### Shared resources
Containing App 與 App Extension 之間有時候會需要有資料共用、傳輸的需求
那一般來說是做不到的,主要是因為 iOS 系統讓 App、App Extension 都是透過 Sandbox (沙盒) 運作的,所以兩者之間是八竿子打不著的
我們可以透過下面這個例子,來知道如果 App 中有使用 SQLite 的話,會在兩邊都建立一個 db 檔
```
# Containing App 建立的 db 檔
/var/mobile/Containers/Data/Application/E5E6E516-0163-4754-9D10-A5F6C33A6261/Documents/app.db
# App Extension 建立的 db 檔
/var/mobile/Containers/Data/PluginKitPlugin/0A99BB23-E261-4AB9-8DA1-29EFA6E2649E/Documents/app.db
```
從上面路徑可以看到從「/var/mobile/Containers/Data/」後面兩者的路徑就不一樣了
Containing App 是建立在「Application/」下,而 App Extension 是建立在「PluginKitPlugin/」
難怪會在 Containing App 跟 App Extension 兩邊都各建立一個 db 檔
那該怎麼處理呢?
我們可以透過 **App Groups** 來讓 Containing App 跟 App Extension 共同讀寫同一個區域
### App Groups
這裡就稍微說一下,App Groups 要如何開啟 (Containing App 跟 Extension 開啟方法都一樣)
Containing App 跟 App Extension 兩邊都要開啟 App Groups 才行喔!

```
0. TARGETS -> <TARGETS NAME> -> Signing & Capabilities -> Signing 選 Apple 付費開發者帳號
1. TARGETS -> <TARGETS NAME> -> Signing & Capabilities -> App Groups
2. App Groups -> + -> 輸入 group.<你想取的名稱>
```
接著有兩個方式可以讓 Containing App 跟 App Extension 共同存取資料
1. UserDefaults
2. FileManager
#### UserDefaults
```swift
UserDefaults(suiteName: "group.<你想取的名稱>")
```
#### FileManager
```swift
FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.<你想取的名稱>")
```
## 不能在 Application Extension 做的事情
從 Apple Developer Documentation 中可以知道
有些事情是不能在 Application Extension 做的,如下圖

▲ 圖取自 [Apple Developer Documentation](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionOverview.html)
不負責翻譯如下
1. 不能存取 sharedApplication 物件,所以不能使用 sharedApplication 物件中的任何 Methods
2. 不能使用任何被標記 NS_EXTENSION_UNAVAILABLE 的 API 或類似的標記,
以及 unavailable Framework 中的任何 API,像是 HealthKit、EventKit UI Framework
3. 不能在 iOS 裝置上使用相機與麥克風
(iMessage App 可以,只需要在 Info.plist 中新增 NSCameraUsageDescription、NSMicrophoneUsageDescription)
4. 執行長時間的背景任務
5. 透過 AirDrop 來接收資料
## 總結
Application Extension 幫 App 新增許多可玩性
在下一篇會以 AutoFill Credential Provider 來當作範例實作 App Extension