App Groupsというのは、同一ディベロッパーがリリースしたアプリ間でデータのストレージを共有できる機能です。
このストレージには任意のファイルの書き込みが可能で、UserDefaultsや画像データなどを格納して共有する事が可能です。

前回記事から引き続き、弊社アプリ「ファミキャプ」にウィジェット機能を実装していますが、containing app(ファミキャプ本体)でキャプチャし保存された画像をToday Extension(ウィジェット)側で表示したかったのですが、相互に保存データのアクセスを行うにはApp Groupsを使用しなければなりませんでした。

App Groupsの設定を行うには2通りの方法があります。
まずは簡単な方法としてXcode上で設定する方法を説明します。

XcodeでApp Groupsを設定する

Xcode上でApp Groupsを設定する条件として、会社などでMember CenterのPeopleとして追加されたアカウントを使用している場合はこの方法はできないかと思われます。

  1. Teamの設定

    1. XcodeのプロジェクトでToday ExtensionでないホストのアプリにTARGETSを合わせた状態でGeneralを選択し、Identity > Teamの部分をクリックします。

      alt text

    2. すると以下の画面のようになるかと思います。

      alt text

    3. ここで以前にアカウントを設定した事があるのであれば一覧にアカウントが表示されるかと思いますので、クリックしてそのアカウントを選択しましょう。
      もしアカウントを作成した事がないのであれば、一番下のAdd an Account…をクリックしてアカウント入力を行ってください。
      アカウント入力画面は以下の画像のようになっていますので、ここにApple IDとパスワードを入力すればOKです。

      alt text

    4. 続いてToday Extension用のアプリにTARGETSを設定して、こちらのTeamも同じように変更します。

      alt text

    5. Teamを選択したら次のステップに進みます。

  2. App Groupsの設定

    1. TARGETSを再びホストのアプリに設定し、Capabilitiesを選択します。

      alt text

    2. ここにApp Groupsを設定できる項目があるので、スイッチをONにします。

      alt text

    3. ここでApp Groupsの設定を行う必要があります。
      +ボタンをクリックすると以下のような画面になるかと思います。

      alt text

    4. ここで入力した項目は、コード上でホストのアプリ、Today Extensionのアプリの両方からアクセスできるストレージ領域にアクセスするために必要となります。
      入力が完了すると以下のような画面になります。

      alt text

    5. 続いてTARGETSにToday Extension用のアプリに合わせます。

    6. 同様にしてCapabilitiesを選択して、App Groupsの項目のスイッチをONにします。

    7. ONに設定すると、ホストのアプリで作成したGroupIDが表示されると思うので、そのIDにチェックを入れます。

    8. 設定が完了した画面が以下になります。

      alt text

  3. Provisioning Profileの設定

    1. Xcode上でApp Groupsの設定を行うと自動的にProvisioning Profileが自動的に作成されます。
      XcodeでTARGETSをホストのアプリに合わせてBuild Settingsの項目を選択します。

    2. Code Signingを開いてProvisioning ProfileCode Signing Identityの項目を以下のように設定します。
      Provisioning ProfileにはBundle IDと同一のものがあるはずなのでそれを選択してください。

      alt text

    3. 続いてTARGETSをToday Extension用のアプリに合わせて同じように設定します。
      Provisioning ProfileにはToday Extension用のBundle IDと同じものを選択します。

      alt text

これでXcode上でApp Groupsの設定が完了しました!



Xcode上でApp Groupsを設定しない方法

会社などでMember CenterのPeopleとして追加されたアカウントを使用している場合、Xcode上でApp Groupsを設定する事ができません。
会社でiOS Dev Centerを管理している人に依頼して色々設定してもらう必要が出てきます。
その手順について説明していきたいと思います。

  1. App IDの作成

    まずはApp IDを作成する必要があります。
    App IDはホストアプリとExtensionアプリの2つ必要となります。
    最初にホストアプリの方の設定を行います。

    1. iOS Dev CenterにアクセスしてApp IDsの項目をクリックします。

      alt text

    2. 右上の+ボタンをクリックして登録します。

      • App ID Description → 表示名を入力
      • App ID Prefix → 適切なものを選択
      • App ID SuffixExplicit App IDWildcard App IDが選択できますが、App Groupsを使用するアプリではワイルドカードは使用できないのでExplicit App IDの方を選択します
      • Bundle ID → ホストアプリのBundle IDを入力
      • App Services → 有効にするサービスの一覧が選択できるので、ここではApp Groupsの項目にチェック
    3. 全て入力が完了したら一番下のContinueボタンをクリックします。
      すると入力内容の確認画面が表示されます。 現段階ではApp Groupsの項目がConfigurableになっていますがこのままで問題ないのでSubmitボタンを押下します。

      alt text

    4. 同様の手順でToday Extension用のApp IDも作成します。

      alt text

  2. App Groupsの設定

    続いてApp Groupsの設定を行います。

    1. Identifiers > App Groupsを選択して、右上の+ボタンをクリックします。
      Descriptionには表示名を入力します。
      IdentifierにはApp Groupsの一意なIDを入力してください。
      入力したらContinueボタンをクリックします。

      alt text

    2. 確認画面が表示されるので、問題ないのであればRegisterボタンをクリックします。

  3. App IDの再設定

    1. 作成したApp GroupsのIDをApp IDに紐づける必要があります。
      「1. App IDの作成」で作成したApp IDのうち、ホストアプリのIDを選択してください。

      alt text

    2. 一番下のEditボタンをクリックします。
      すると以下のような画面が表示されます。

      alt text

    3. App Groupsの項目の右側にあるEditボタンをクリックします。
      以下のような画面が表示されるので「2. App Groupsの設定」で作成したIDにチェックを入れてContinueボタンをクリックします。
      すると確認画面が表示されるので問題なければAssignボタンをクリックします。

      alt text

    4. Today Extension用に作成したApp IDの方にも同様の設定を行ってください。
      設定が完了すると、App IDのApp Groupの項目がConfigurableからEnabledになっているのが確認できるかと思います。

      alt text

  4. Provisioning Profileの作成

    1. Provisioning Profiles > Developmentを選択し、右上の+ボタンをクリックします。
    2. ラジオボタンでDevelopmentのiOS App Developmentを選択してContinueボタンをクリックします。
    3. Select App IDの画面では「1. App IDの作成」で作成したホストアプリのApp IDを選択しContinueボタンをクリックします。
    4. Select certificatesの画面ではDevelopment用の証明書にチェックをいれてContinueボタンをクリックします。
    5. Select devicesの画面では実機で確認したい端末にチェックを入れてContinueボタンをクリックします。
    6. Name this profile and generateの画面ではProfile Nameに名前を入力してGenerateボタンをクリックします。
    7. DownloadボタンをクリックしてPCにダウンロードしておいてください。
    8. 続いてToday Extension用のProvisioning Profileも作成する必要があるので、Add Anotherボタンをクリックして同様の手順で作成します。
    9. 作成が完了したら先ほど同様DownloadボタンをクリックしてPCにダウンロードしてください。
    10. 最後にダウンロードした2つのProvisioning Profileをダブルクリックします。
  5. Xcode上での設定

    1. Teamの設定
      • Teamの設定は「XcodeでApp Groupsを設定する」>「1. Teamの設定」を参考に設定してください。
    2. Provisioning Profileの設定
      • Provisioning Profileの設定は「XcodeでApp Groupsを設定する」>「3. Provisioning Profileの設定」を参考に設定してください。
    3. App Groupsの設定
      • App Groupsの設定は「XcodeでApp Groupsを設定する」>「2. App Groupsの設定」を参考に設定してください。
        IDはすでにiOS Dev Center上で作成しているので、+ボタンをクリックして新しく作成しなくてもApp Groupsの設定をONにしたら自動的に表示されます。 表示されたIDにチェックを入れればOKです。

App Groupsを使用したデータ共有の実装

App Groupsの設定が完了したら、その機能を実際に使用してみたいと思います。


NSUserDefaultsを使用したデータ共有

今回ファミキャプでは使用していませんが、まずはUserDefaultsを使用したデータ共有の方法を紹介します。

  • ホストアプリ側の処理
// App Groupsを使用したデータ共有処理.
// 引数にはApp Groupsで設定したグループIDを指定する事.
NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.jp.gaprot.famicap.AppGroupTest02"];
[userDefaults setObject:@"AppGroupTest" forKey:@"AppGroupTestKey"];
  • ウィジェットアプリ側の処理
// App Groupsを使用したデータ共有処理.
// 引数にはApp Groupsで設定したグループIDを指定する事.
NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.jp.gaprot.famicap.AppGroupTest02"];
NSString *str = [userDefaults objectForKey:@"AppGroupTestKey"];

上記コードのようにホストアプリ側で保存したデータを、ウィジェット側で参照する事が可能となります。
もちろんウィジェット側で保存したデータをホストアプリ側で参照する事も可能です。


NSFileManagerを使用したデータ共有

今回ファミキャプではNSFileManagerを使用してホストアプリ側でキャプチャした画像をApp Groups専用のストレージに保存し、ウィジェット側で保存された画像を参照するといった処理を行っています。

まずは保存する側の処理から見ていきます。

  • ホストアプリ側の処理
// imgDataには画像のデータ.
NSData *imgData = ...;

// App Groupsを使用したデータ共有処理.
// 引数にはApp Groupsで設定したグループIDを指定する事.
NSURL *url = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.jp.gaprot.famicap.AppGroupTest02"];
url = [url URLByAppendingPathComponent:@"test.png"];

NSString *path = [url path];

// 画像データをストレージに保存.
BOOL success = [data writeToFile:path atomically:YES];
  • ウィジェット側の処理
// App Groupsを使用したデータ共有処理.
// 引数にはApp Groupsで設定したグループIDを指定する事.
NSURL *url = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.jp.gaprot.famicap.AppGroupTest02"];
url = [url URLByAppendingPathComponent:@"test.png"];

NSString *path = [url path];

// ストレージから画像データを抽出.
UIImage *image = [UIImage imageWithContentsOfFile:path];

以下、注意点を記載しておきます。

  • App Extension自体iOS8からの機能なので、ストレージ領域に保存する際にはiOS8以降とそれ未満で保存領域を切り分ける必要があります。 (一応、上記コードで出てきたNSUserDefaultsのinitWithSuiteName:メソッドやNSFileManagerのcontainerURLForSecurityApplicationGroupIdentifier:メソッドはiOS7以降からのサポートみたいです。)
  • NSFileManagerのcontainerURLForSecurityApplicationGroupIdentifier:メソッドで取得したディレクトリに対して、ディレクトリを作成する事は出来ない模様です。
    • こちらは試してみての結果ですが、取得したディレクトリに対してディレクトリを作成したらパーミッションエラーとなったため、どうやらディレクトリを作成する権限はないようです。

さいごに

前回・今回と、ファミキャプで実装した内容を元に iOS8 の新機能のひとつを紹介させていただきました。
ウィジェットを実装する際にこの記事の内容が参考になれば幸いです。