一般的情況下,iOS App 只能存取自己資料夾下的東西,無法存取其它 App 的內容。Apple 這麼做主要是為了安全,比方彼得潘如果不小心裝了虎克船長開發的邪惡 App,它也無法讀取我在 LINE 裡傳給溫蒂的私密照片。
不過有時我們會開發好幾個 App,如果自己開發的 App 能夠共享資料,應該會方便許多。Apple 其實提供一些方法讓不同的 App 可以共享資料,比方我們接下來要介紹的 App Group。Different ways to share data between apps — iOSA good idea is about ten percent and implementation and hard work, and luck is 90 percent. — Guy Kawasakimedium.com
App Group 讓不同的 App 或是 App & App extension 之間可以透過以下兩種方法共享資料(不過必須是同一個帳號開發的 App):
1 利用 UserDefaults 共享資料。
2 共同存取某個特別的資料夾。
接下來就讓我們建立兩個專案,梁山伯和茱麗葉,讓茱麗葉 App 可以收到梁山伯 App 輸入的情話吧。
1 建立兩個專案。
使用者可在梁山伯 App 的畫面上打字,輸入他想對茱麗葉說的話。
茱麗葉 App 打開後,可看到最近一次梁山伯 App 輸入的文字。
2 製作梁山伯 App
(1) 從 Project navigator 選擇專案檔,切換到 Capabilities 頁面後,將 App Groups 的開關打開。
App 必須設定 Team 才能加入 App Groups,因此如果之前沒設定,此時 Xcode 會跳出視窗要我們選擇。此功能不一定要付費的開發帳號,所以免費帳號也可以測試。
打開開關後,App Groups 的開關變成 ON 的狀態。
(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 名字。init(suiteName:) – UserDefaults | Apple Developer DocumentationYou can use this method when developing an app suite, to share preferences or other data among the apps, or when…developer.apple.com
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。containerURL(forSecurityApplicationGroupIdentifier:) – FileManager | Apple Developer DocumentationIf you call the method with an invalid group identifier, namely one for which you do not have an entitlement, the…developer.apple.com
讀檔的程式範例
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。)