讓不同 App 共享資料的 App Group

一般的情況下,iOS App 只能存取自己資料夾下的東西,無法存取其它 App 的內容。Apple 這麼做主要是為了安全,比方彼得潘如果不小心裝了虎克船長開發的邪惡 App,它也無法讀取我在 LINE 裡傳給溫蒂的私密照片。

不過有時我們會開發好幾個 App,如果自己開發的 App 能夠共享資料,應該會方便許多。Apple 其實提供一些方法讓不同的 App 可以共享資料,比方我們接下來要介紹的 App Group。

App Group 讓不同的 App 或是 App & App extension 之間可以透過以下兩種方法共享資料(不過必須是同一個帳號開發的 App):

1 利用 UserDefaults 共享資料。

2 共同存取某個特別的資料夾。

接下來就讓我們建立兩個專案,梁山伯和茱麗葉,讓茱麗葉 App 可以收到梁山伯 App 輸入的情話吧。

1 建立兩個專案

使用者可在梁山伯 App 的畫面上打字,輸入他想對茱麗葉說的話。

茱麗葉 App 打開後,可看到最近一次梁山伯 App 輸入的文字。

2 製作梁山伯 App

(1) 從 Project navigator 選擇專案檔,點選 TARGETS 下的 App,切換到 Signing & Capabilities 頁面後,點選 + Capability。

加入 App Groups。

App 必須設定 Team 才能加入 App Groups,因此如果之前沒設定,此時 Xcode 會跳出視窗要我們選擇。此功能不一定要付費的開發帳號,所以免費帳號也可以測試。

加入 App Groups 後,長出下圖的 App Groups 區塊。

(2) 點選 App Groups 左下角的 +,新增 container。

此時輸入的 container 名字(group id)十分重要,到時候不同的 App 將使用此名字搭配 UserDefaults 共享資料。

經過以上步驟,此時 Xcode 將產生 entitlements 檔,裡面包含剛剛設定的 App container 名字。

如果是付費的開發帳號,此時還可以連到 Apple 的開發網站查詢,Apple 會幫此 App 的 App ID 加入 App Groups 功能,並且綁定剛剛在 Xcode 指定的 container 名字。

(3) 在 button 按下時將 text filed 的文字存到共享的 UserDefaults。

class ViewController: UIViewController {
@IBOutlet weak var textField: UITextField!

@IBAction func buttonPressed(_ sender: Any) {
let userDefaults = UserDefaults(suiteName: "group.peter.boy")
userDefaults?.set(textField.text, forKey: "loveMessages")
}
}

原本不同的 App 無法共享 UserDefaults 的資料。然而在這裡我們卻做到了,因為我們利用 UserDefaults(suiteName: "group.peter.boy") 建立 UserDefaults,在參數 suitName 傳入 container 的名字。

此 UserDefaults 厲害多了,存在裡面的資料可以讓不同的 App 共享,只要這些 App 是同一個帳號開發,而且在 Xcode 的 App Groups 下設定一樣的 container 名字。

3 製作茱麗葉 App

(1) 設定跟梁山伯專案一樣的 App Groups。

由於我們曾經建立過 App Group group.peter.boy,所以打開 App Groups 的開關時,Xcode 將自動幫我們加入 group.peter.boy。

不過它並不會勾選,所以請記得勾選 group.peter.boy,如此才代表我們的 App 要使用它。

(2) 從 UserDefaults 讀取文字,顯示到 label 上。

class ViewController: UIViewController {
@IBOutlet weak var label: UILabel!

override func viewDidLoad() {
super.viewDidLoad()
let userDefaults = UserDefaults(suiteName: "group.peter.boy")
if let messages = userDefaults?.string(forKey: "loveMessages") {
label.text = "茱麗葉聽到梁山伯說:\n\(messages)"
}
}
}

記得用 UserDefaults(suiteName: "group.peter.boy") 生成 UserDefaults,並且傳入一樣的 container name,如此茱麗葉 App 才能讀取梁山伯 App 儲存的內容。

4 執行 App

如下圖所示,當我們用梁山伯 App 輸入文字,然後再打開茱麗葉 App,果然看到了梁山伯輸入的文字,原來他們是在 Christmas 時一見鍾情的 !

container name(group id) 必須要獨一無二

在 Capabilities 頁面設定 App Group 時,有些人會遇到問題。比方以下的錯誤。

錯誤訊息說,An Application Group with Identifier ‘group.peter.boy’ is not available. Please enter a different string.。原因是此 group id 已經被別的開發帳號使用了,所以我們必須取別的名字,group id 必須要獨一無二,不能跟別人一樣。

將檔案存到共享的 container 資料夾

剛剛我們利用 UserDefaults 實現資料的共享,可惜它只適合儲存少量的資料,如果想儲存大量的資料,則可另外將檔案存在共享的 container 資料夾,方法如下。

存檔的程式範例。

if let shareUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.peter.boy") {
let imagePath = shareUrl.appendingPathComponent("image.png")
try? UIImage(named: "chocolate")?.pngData()?.write(to: imagePath)
}

利用 FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.peter.boy") 得到共享的 container 資料夾 url,參數 forSecurityApplicationGroupIdentifier 傳入 App Group 下設定的 container name。

讀檔的程式範例。

if let shareUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.peter.boy") {
let imagePath = shareUrl.appendingPathComponent("image.png")
let image = UIImage(contentsOfFile: imagePath.path)
imageView.image = image
}

執行 App

茱麗葉 App 顯示梁山伯 App 儲存在 container 資料夾的圖片。

刪除 App Group 的 group id

雖然我們在 Xcode 建立 group id,不過若想刪除它,卻必須另外登入 Apple 的開發網站,在它的 App Groups 頁面做刪除的動作。(因此只有付費的帳號能夠刪除 group id。)

--

--

彼得潘的 iOS App Neverland
彼得潘的 Swift iOS App 開發問題解答集

彼得潘的iOS App程式設計入門,文組生的iOS App程式設計入門講師,彼得潘的 Swift 程式設計入門,App程式設計入門作者,http://apppeterpan.strikingly.com