個人用アカウントでAppGroupを用いたチーム開発を行なう


## 問題点
iOS8でアプリ間でデータを共有する仕組みとしてAppGroupが導入された。カスタムキーボードやAppleWatchアプリ等の拡張は別アプリとなるため、これらを開発をする際はAppGroupの利用が事実上必須である。 (参考: http://www.toyship.org/archives/1845)

AppGroupは開発者アカウントごとのデータであり、かつ、世界中で一意となる名前を持たねばならない。 個人(Individual)アカウントは複数人で共有できないため、同じAppGroupを使うことができない。企業(Company/Organization)アカウントならばAppGroupの共有が可能だが、取得には法人である必要がある。

そのため、法人でない団体(例: 大学サークル等)では、AppGroupの利用が難しい。

## 解決策
以下の方針で、開発を複数人で行なえるようにする。

 * 開発者ごとにAppGroup名/Bundle Identifierを変更する。
 * AppGroup名/Bundle Identifierを具体的に書く箇所を一箇所にすることで、容易に変更できるようにする。

ただし、AppStoreに提出できるのは誰か一人という制約は解決できないため、誰かが正式版のビルド係/AppStoreへの提出係となる必要がある。

## 手順
Xcode 6.3.2をもとに手順を記述する。

### Config.xcconfigを作成する
以下の内容の `Config.xcconfig` を作成し、プロジェクトに追加する。

```
// 以下の行を開発者ごとに変更すること
APP_IDENTIFIER = org.example.mzp.some_app

GCC_PREPROCESSOR_DEFINITIONS = $(inherited) APP_IDENTIFIER=${APP_IDENTIFIER}
```

gitで管理している場合は以下のようにするとよい。

 * 上記の内容をConfig.xcconfig.exampleに記述する
 * .gitignoreでConfig.xcconfigを無視するようにするように設定する
 * 開発者がcloneした直後にConfig.xcconfigをConfig.xcconfig.examlpeを元に作成するようにするようREADME等に記述する

### Config.xcconfigを追加する
プロジェクト -> Info -> Configurationsで `Config.xcconfig` を使うように設定する。

![config.png](https://qiita-image-store.s3.amazonaws.com/0/5920/fb5bb383-661c-00a5-f31e-5e5d270de27b.png "config.png")


### Info.plistの変更
各ターゲットの `Info.plist` 内のBundle identifierを用いている部分を `$(APP_IDENTIFIER)` で置換する。
ターゲットの種類によって置き換えなければならない箇所は異なる。

![info.png](https://qiita-image-store.s3.amazonaws.com/0/5920/6a1df0e4-c2ae-a1d5-2f65-f02eb40da0b9.png "info.png")


### AppGroupの有効化
各ターゲットのCapabilitiesからAppGroupを有効にする。
また、下の「+」ボタンで、AppGroupの作成も行なえる。

![export.png](https://qiita-image-store.s3.amazonaws.com/0/5920/d7083974-bd92-b3b9-5da2-55c625e67c5f.png "export.png")


### *.entitlementsの変更
`<アプリ名>.entilements` が追加されるので、AppGroup名を`group.$(APP_IDENTIFIER)` で置換する。

![entitlements.png](https://qiita-image-store.s3.amazonaws.com/0/5920/91591211-4b3f-de97-fc71-bd84fd493437.png "entitlements.png")


### ラッパクラスの作成
アプリ内でAppGroupを用いた処理をできるようラッパクラスを作成する。CPPマクロを利用するため、Objective-Cでなければならない。

Objectvie-Cのため、Swiftから利用する場合はBridge Headerを適切に設定する必要がある。

#### AppGroup.h
```objc
#import <Foundation/Foundation.h>

// AppGroupに関係する情報のラッパ。
//
// 個人アカウントを使っているため、ユーザに応じてAppGroup名を変える必要がある。
// AppGroup名はConfig.xcconfig内でCPPマクロとして定義されているため、Swiftからは読むことができない。
// そのためObjective-Cで記述する。
@interface AppGroup : NSObject

// AppGroup名
+ (NSString *)appGroupID;

// AppGroupで共有された領域のパスを取得
+ (NSString *)pathForResource:(NSString *)subpath;

// AppGroupで共有されたNSUserDefaultsを取得
+ (NSUserDefaults*)userDefaults;
@end
```

#### AppGroup.m
```objc
#import "AppGroup.h"

#define STR(x) @#x
#define STR2(x) STR(x)
static NSString * const kAppIdentifier = STR2(APP_IDENTIFIER);

@implementation AppGroup

+ (NSString *)appGroupID
{
    return [NSString stringWithFormat:@"group.%@", kAppIdentifier];
}

+ (NSString *)pathForResource:(NSString *)subpath
{
    NSString *containerPath = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:[self appGroupID]].path;
    return [containerPath stringByAppendingPathComponent:subpath];
}

+ (NSUserDefaults*)userDefaults
{
    return [[NSUserDefaults alloc] initWithSuiteName: [self appGroupID]];
}

@end
```