Photos Framework を使って写真.appと画像をやりとりする

iOS8で `Photos Framework` が追加されました。
`Photos Framework` は「写真.app」や「iCloud」の写真/ビデオに対し、読み書きやプロパティの編集、変更の監視などができます。
`App Extension`が追加されたのもあり、写真やビデオを複数のアプリで扱うための機能が用意されています。

## 写真.appと画像をやりとりするサンプルを作成してみました
`Photos Framework` では、 主に3つの階層があってその上でデータをやりとりします。
基本的な読み書きでは、`PHAssetColleciton`と`PHAsset`を扱うことになります。

|クラス|なにを扱うのか|
|:--|:--|
|PHCollectionList|PHAssetCollectionの集まり|
|PHAssetCollection|カメラロール自体やユーザ作成のフォルダ、モーメントの年など|
|PHAsset|写真やビデオ自体|

### アルバムから画像を取得して UIImageView に表示する
````Objective-C:
- (void)readImage {
    // PHAssetCollection を取得します
    PHAssestCollection * myAlbum = [self getMyAlbum];

    // PHAsset をフェッチします
    PHFetchResult *assets = [PHAsset fetchAssetsInAssetCollection:myAlbum options:nil];

    // フェッチ結果から assets を取り出します
    NSArray * assetArray = [self getAssets:assets];
    
    // asset を使って、UIImageView をランダムに更新します
    [self updateImageViewWithAsset:assetArray[arc4random() % assets.count]];
}

- (PHAssetCollection *)getMyAlbum {
        // ユーザ作成のアルバム一覧を指定して、PHAssetCollection をフェッチします
    PHFetchResult *assetCollections = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum
                                                                               subtype:PHAssetCollectionSubtypeAlbumRegular
                                                                               options:nil];
    
    // [My Album]の AssetCollection を取得します
    __block PHAssetCollection * myAlbum;
    [assetCollections enumerateObjectsUsingBlock:^(PHAssetCollection *album, NSUInteger idx, BOOL *stop) {
        if ([album.localizedTitle isEqualToString:@"My Album"]) {
            myAlbum = album;
            *stop = YES;
        }
    }];
    
    return myAlbum;
}

- (NSArray *)getAssets:(PHFetchResult *)fetch {
    // フェッチ結果を配列に格納します
    __block NSMutableArray * assetArray = NSMutableArray.new;
    [fetch enumerateObjectsUsingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop) {
        [assetArray addObject:asset];
    }];
    return assetArray;
}

- (void)updateImageViewWithAsset:(PHAsset *)asset {
    typeof(self) __weak wself = self;
    [[PHImageManager defaultManager] requestImageForAsset:asset
                                               targetSize:CGSizeMake(300,300)
                                              contentMode:PHImageContentModeAspectFill
                                                  options:nil
                                            resultHandler:^(UIImage *result, NSDictionary *info) {
                                                if (result) {
                                                    // imageVivew を更新します
                                                    wself.imageView.image = result;
                                                }
                                            }];
}
````

### アルバムに画像を保存する
「写真.app」へ変更を要求する場合は、`xxxChangeRequest`というクラスを使用します。
保存するときに、`identifier`が取得できます。これを使って画像を取り出すこともできます。

````Objective-C:
- (void)addNewAssetWithImage:(UIImage *)image toAlbum:(PHAssetCollection *)album
{
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
        // PHAssetChangeRequest を作ります
        PHAssetChangeRequest *createAssetRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:image];
        
        // PHAssetCollectionChangeRequest を作ります
        PHAssetCollectionChangeRequest *albumChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:album];
        
        // placeholder を取得して、PHAssetCollection に asset を追加します
        PHObjectPlaceholder * placeHolder = [createAssetRequest placeholderForCreatedAsset];
        [albumChangeRequest addAssets:@[ placeHolder ]];
        
        // placeholder から取得できる identifier を
        // 保存しておくと後から画像を一意に取得することができます
        NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
        [defaults setObject:placeHolder.localIdentifier forKey:kAssetIdentifier];
        [defaults synchronize];
        
    } completionHandler:^(BOOL success, NSError *error) {
        NSLog(@"Finished adding asset. %@", (success ? @"Success" : error));
    }];
}
````

### 保存した画像をアルバムから Identifier で読み込む
`getAssets:`と`updateImageViewWithAsset:`は上で記述したメソッドです。

````Objective-C:
- (void)readImageWidthIdentifier {
    // Identifier を指定して PHAsset をフェッチします
    NSString * identifier = [[NSUserDefaults standardUserDefaults]objectForKey:kAssetIdentifier];
    PHFetchResult *assets = [PHAsset fetchAssetsWithLocalIdentifiers:@[identifier] options:nil];
    
    // assets を取り出します
    NSArray * assetArray = [self getAssets:assets];
    
    // UIImageView を更新します
    [self updateImageViewWithAsset:assetArray.lastObject];
}
````

`ALAssestLibrary` との違いとして、フェッチ結果の取り出しが同期になっているのが使いやすく感じました。
もっと使いこなせるようになっていきたいですね。

### サンプルコード
https://github.com/taketomato/photosFWsample

## 参考URL
[Photos Framework 公式ドキュメント](https://developer.apple.com/library/ios/navigation/#section=Frameworks&topic=Photos)
[Developers.IO PhotoKit 特集](http://dev.classmethod.jp/referencecat/ios8-api-photokit/)