3

In Xcode, when I'm trying to add more than 5 images to my library, it gives me the following error:

Error Domain=ALAssetsLibraryErrorDomain Code=-3301 "Write busy" UserInfo=0xa706aa0 {NSLocalizedRecoverySuggestion=Try to write again, NSLocalizedFailureReason=There was a problem writing this asset because the writing resources are busy., NSLocalizedDescription=Write busy, NSUnderlyingError=0xa770110 "Write busy"}

In order to solve this problem, I figured out threads would solve my problems. The documentation states that I can use POSIX threads or NSThreads. When I try using POSIX threads, I set my threads to be joinable, and I'm creating a void * function:

void * myFunc (void * image)
{
       UIImageWriteToSavedPhotosAlbum((__bridge UIImage *)(image),self,nil,nil);
       pthread_exit(NULL);
       return NULL;
}

I am also waiting for the thread to end. But still only 5 images are written.

I've tried using NSThreads and did:

[self performSelectorOnMainThread:@selector(myFunc:) withObject:image waitUntilDone:YES];

But still it doesn't work.

Is there an answer to my problem? It's crucial to my work.

Thanks.

Edit:

Tried dispatch_async too. Is it wrong?

dispatch_queue_t myQueue = dispatch_queue_create("com.cropr.myqueue", 0);

for (UIImage * image in images) {

        dispatch_async(myQueue, ^{

            [self.library saveImage:image toAlbum:@"Cropr" withCompletionBlock:^(NSError *error) {
                if (error!=nil) {
                    NSLog(@"Big error: %@", [error description]);
                }
            }];

        });

    }

What do I need to add?

Coldsteel48
  • 3,482
  • 4
  • 26
  • 43
omesi37
  • 112
  • 8

3 Answers3

3

You may try to write all your images subsequently, instead of simultaneously. The following code utilizes ALAssetsLibrary, and implements an "asynchronous loop" which invokes a number of asynchronous methods in sequence.

typedef void (^completion_t)(id result);

- (void) writeImages:(NSMutableArray*)images 
          completion:(completion_t)completionHandler {
    if ([images count] == 0) {
        if (completionHandler) {
            // Signal completion to the call-site. Use an appropriate result,
            // instead of @"finished" possibly pass an array of URLs and NSErrors 
            // generated below  in "handle URL or error".
            completionHandler(@"finished");  
        }
        return;
    }

    UIImage* image = [images firstObject];
    [images removeObjectAtIndex:0];

    [self.assetsLibrary writeImageToSavedPhotosAlbum:image.CGImage 
                                         orientation:ALAssetOrientationUp
                                     completionBlock:^(NSURL *assetURL, NSError *error)
    {
        // Caution: check the execution context - it may be any thread,
        // possibly use dispatch_async to dispatch to the main thread or
        // any other queue.

        // handle URL or error
        ...
        // next image:
        [self writeImages:images completion:completionHandler];
    }];
}

Usage:

[foo writeImages:[foo.images mutableCopy] completion:^(id result){
    // Caution: check the execution context - it may be any thread
    NSLog(@"Result: %@", result);   
}];
CouchDeveloper
  • 18,174
  • 3
  • 45
  • 67
1

I'd recommend using an NSOperationQueue and play with the value of maxConcurrentOperationCount. This way you can control the number of simultaneous writes to the library, and not overwhelm it.

If you use threads, or even GCD, you'd need to implement this logic yourself. More code --> more chance of introducing a bug.

TotoroTotoro
  • 17,524
  • 4
  • 45
  • 76
  • When using NSOperationQueue, one needs to create a *NSOperation*, which MUST be a concurrent one, since the actual task is already asynchronous. The weird combination of the "call back selector" in the `UIImageWriteToSavedPhotosAlbum` function doesn't make the implementation of the NSOperation subclass easier. In fact, creating a proper concurrent subclass for this case is elaborate, error prone and tricky. So, I would discourage using a NSOperationQueue. – CouchDeveloper Dec 18 '13 at 23:04
0

Dispatch_async is something to do in background , so I put your for{} and function call inside the dispatch_async body so putting for inside the dispatch async will store your imaged in the album,like just running for. but on seperate thread.

    dispatch_queue_t myQueue = dispatch_queue_create("com.cropr.myqueue", 0);
    dispatch_async(myQueue, ^{

      for (UIImage * image in images) 
          UIImageWriteToSavedPhotosAlbum((__bridge UIImage *)(image),self,nil,nil);
    });

}

You can also try this. i think will be better, watch if it works , then add what you want to handle the errors.

Also i think here is your desired answer : iOS: dispatch_async and UIImageWriteToSavedPhotosAlbum

Community
  • 1
  • 1
Coldsteel48
  • 3,482
  • 4
  • 26
  • 43