BAD_ACCESS

おもにiOS、ときどき変な電子工作、ガジェット話。

ALAssetGroupの「addAsset」が返す値が時々NOになって困る。

需要ないと思うけど…

写真をフォトライブラリにひとまとめにして保管(ー>アルバムとして保管)する際にカメラロール側に余分に保管されてしまう現象について。
あるアプリでダウンロードしてきた写真をアルバム単位で保管する機能がある。保管する際にアルバム側は保管しようとしている枚数が正しく保管されるが、フォトライブラリのカメラロール側にまれに数枚余分に多く保管されている時がある。
デバッグしてみたところ、下記の判別部分でNOの判定となり、写真を保管する処理を再度実行していることが原因の様子。

 if ([group addAsset:aAsset]) 
 {
     //save complete    
 {
 else
 {
     //save failed. retry!
     [self save];
 }

ただしその判別部分が「NO」になる理由がわからない。

- (BOOL)addAsset:(ALAsset *)asset

はALAssetGroupクラスのインスタンスメソッドとなっている。このあたりを詳細にみていく必要がありそうだ。
ドキュメントによるとNOになる理由は下記の通り。

Add an existing ALAsset to the group.  An asset can belong to multiple ALAssetsGroups.
The caller should check the editable property of the group to see if it is possible to add an asset to the group.
Returns YES if the asset was added successfully.  Returns NO if the group is not editable, or if the asset was not able to be added to the group.

このドキュメントの記載をもとに2つの推理をしてみた。
・推理1:追加しようとしているAssetGroup側が何かおかしい。
・推理2:追加しようとしているAsset側が何かおかしい。

推理1:追加しようとしているAssetGroup側が何かおかしい。

推理1を証明するためにAssetを追加する時に再度追加する先のAssetGroupのURLを取得することにした。何らかの現象によって追加する先のAssetGroupに変更が生じているなら、追加するタイミングで新たにURLを取り直せば問題なく追加できるはずである。

推理1 結果

結果はAssetGroup側には問題が無いことが分かった。
追加するたびに新たに取得したURLに対して保存の処理をかけても同じく- (BOOL)addAsset:(ALAsset *)assetはNOを返してくる。

推理2:追加しようとしているAsset側が何かおかしい。

Assetを追加するフローは下記の通り。
・AssetLibraryを初期化する。
・AssetGroupを初期化する。(追加するアルバム名でGroupが作られている)
・追加しようとしている写真をもとにAssetURLをつくる。
・AssetURLをもとにAssetを取り出す
・AssetをAssetGroupに追加する。<ーココでおかしい。
なので追加したときに- (BOOL)addAsset:(ALAsset *)assetの値としてNOを返すのであれば、
さらにカメラロールから同じAssetを引っ張り出してきて再度AssetGroupに追加させてやる。
まず通常のAssetGroupへの保管処理

- (void)saveAsset:(ALAsset *)aAsset
{
    [assetsLibrary_ groupForURL:groupURL_ resultBlock:^(ALAssetsGroup *group){
        LOG( @"group: %@", group );
        //addAsset:ここで[group addAsset:aAsset]=NOが返ってくる謎。
        if ([group addAsset:aAsset]) {
            LOG(@"GroupPropertyURL:%@",[group valueForProperty:ALAssetsGroupPropertyURL]);
            [self saveComplete];
        }else{
            //再試行
            LOG(@"再試行 editable: %d", group.editable);
            LOG(@"GroupURL:%@",groupURL_);
            //aAssetの追加に失敗。aAssetをカメラロールから再び取得してGroupに追加する。
            [self resaveAsset];
        }
        
    } failureBlock:^(NSError *error){
        LOG(@"%@", error.description);
    }];
}

つづいてNOを返した時の再試行

- (void)resaveAsset
{
    //カメラロールに入っている全てのPhotoデータにアクセス。
    [assetsLibrary_ enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *aaGroup, BOOL *stop) {
        //ビデオ、写真全て含む(フィルタの意味ある?)
        [aaGroup setAssetsFilter:[ALAssetsFilter allAssets]];
        [aaGroup enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:[aaGroup numberOfAssets]-1] options:0 usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
            
            if (result) {
                //saveAssetの処理でアセットの追加に失敗したアセットをカメラロールから引っ張ってきて再度登録。
                [self saveAsset:result];
            }
        }];
    } failureBlock:^(NSError *error) {
        LOG(@"%@", error.description);
    }];
}

上記の通り再試行のメソッドresaveAssetによって改めてカメラロールより取得したアセットをassetGroupに追加したところ
問題なく追加が出来、さらにカメラロールに余分に写真が保管されるということも無くなった。
これはハマった。。。