2018年4月1日日曜日

使ってみた(App Extension編) おまけ

「使ってみた(App Extension編) その5」からのつづきです。AppExtensionの話題からどんどん離れていきますが、今回はおまけです。


繰り返しになりますが、AppExtensionと収容アプリケーションは別アプリ扱いです。

そのため、実際のユースケースでは、AppExtensionを使った後ですぐに収容アプリケーションを起動するとは限りません。AppExtensionでのコンテンツの保存を繰り返した後に、収容アプリケーションを起動するというケースも普通にあるでしょう。

そこで、もうひと工夫して、AppExtensionでUserDefaultに保存するときに、Contents型の配列でため込んで、収容アプリケーションで取得するときにまとめて取得するようにします。

好都合なことに、Codableの配列はCodableなのでそのままエンコード、デコードできます。

UserDefaultは上書き保存ですから、AppExtensionでデータを保存するときには、いったんデータを取得してから、その配列に新たにデータを追加してから保存し直します。

これでAppExtensionでどんどん追加保存できます。
    /// コンテンツをApp Groupのユーザーデフォルトに保存
    ///
    /// - Parameter content:
    private func saveContent(content: String) {
        guard let userDefaults = UserDefaults.init(suiteName: "group.jp.blowbend.ios.test.AwesomeApp") else {
            print("userdefault: app group suite name is nil")
            return
        }
        
        //保存済みのデータの取得
        var contents: [Contents] = []
        if let object = userDefaults.object(forKey: "user default key content") {
            if let data: Data = object as? Data {
                do {
                    contents = try JSONDecoder().decode([Contents].self, from: data)
                } catch {
                    print("ActionViewController: JSONDecoder decode error")
                }
            }
        }
        
        let c = Contents(key: 123, text: content, date: Date())
        contents.append(c) //新たにデータを追加
        do {
            let data = try JSONEncoder().encode(contents) //Data型に変換
            userDefaults.set(data, forKey: "user default key content")
            userDefaults.synchronize()
        } catch {
            print("userdefault: contents encode error!!")
        }
    }

収容アプリケーションでデータを取得するときは、単純に配列で受け取ります。貯まっていたデータを受け取ったら、removeObjectでデータを削除して空にします。
    /// App Groupのユーザーデフォルトからコンテンツを取得
    private func getContentFromAppGroup() {
        guard let userDefaults = UserDefaults.init(suiteName: "group.jp.blowbend.ios.test.AwesomeApp") else {
            print("userdefault: app group suite name is nil")
            return
        }
        guard let content = userDefaults.object(forKey: "user default key content") ?? nil else {
            print("userdefault: no content")
            return
        }
        guard let data = content as? Data else {
            print("userdefault: no data")
            return
        }
        
        do {
            let contents = try JSONDecoder().decode([Contents].self, from: data) //Content型の配列で取得
            for c in contents {
                print("c.key =\(c.key)")
                print("c.text=\(c.text)")
                print("c.date=\(c.date)")
            }
        } catch {
            print("userdefault: JSONDecoder decode error")
        }

        userDefaults.removeObject(forKey: "user default key content") //すべてのデータの削除
    }


どうでしょうか。これでAppExtensionの呼び出しごとに、(実は動いていない)収容アプリケーションにデータを送っているかのように見せられますね。このあたりは、その処理に合わせて色々工夫のしどころかもしれません。

以上で、使ってみた(App Extension編)は終わりです。おつかれさまでした。


こんな風にAppExtensionを使ったアプリをリリースしました。よろしくね。


「おまけのオマケ」
App Extensionを含むアプリをiTunes Storeにリリースするときは、ビルド番号を収容アプリケーションとApp Extensionとで揃える必要があります。

もし違うままアップロードすると、

WARNING ITMS-90473: "CFBundleVersion Mismatch. The CFBundleVersion value '2' of extension 'AwsomeApp.app/PlugIns/AwsomeAppShare.appex' does not match the CFBundleVersion value '1' of its containing iOS application 'AwsomeApp.app'."

というワーニングが出ます。ワーニングなので実害はないのかもしれませんが、まぁ、不用なトラブルは避けた方がいいですよね。お気を付けください。


♪♪♪


こちらもどうぞ。
使ってみた(App Extension編) その1
使ってみた(App Extension編) その2
使ってみた(App Extension編) その3
使ってみた(App Extension編) その4
使ってみた(App Extension編) その5

iOS 11 Programming
  • 著者:堤 修一,吉田 悠一,池田 翔,坂田 晃一,加藤 尋樹,川邉 雄介,岸川克己,所 友太,永野 哲久,加藤 寛人,
  • 発行日:2017年11月16日
  • 対応フォーマット:製本版,PDF
  • PEAKSで購入する