pit
pit

Reputation: 3

Returning an object from inside block within category class method implementation

I have run into a certain problem with my implementation which I don't really know how to solve. Could You please advise.

I'm trying to implement an NSManagedObject category class Photo+Flickr.m with one class method +(void)photoWithFlickrData:inManagedObjectContext:

What I would like to do is download data from Flickr API using NSURLSessionDownloadTask and then create Photo object and insert this new created object into database (if it's not already there). This part works fine.

And at the end I would like to return new created (or object that was found in db) Photo object. And this is where I run into problem. Since I'm using category I can't use instance variables. I can't really find any good solution to get this Photo object from inside this completionHandler block.

My code:

@implementation Photo (Flickr)

+ (void)photoWithFlickrData:(NSDictionary *)photoDictionary
    inManagedObjectContext:(NSManagedObjectContext *)context

{
NSString *placeId = [photoDictionary valueForKeyPath:FLICKR_PHOTO_PLACE_ID];
NSURL *urlInfoAboutPlace = [FlickrFetcher URLforInformationAboutPlace:placeId];

NSURLRequest *request = [NSURLRequest requestWithURL:urlInfoAboutPlace];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURLSessionDownloadTask *task =
[session downloadTaskWithRequest:request
               completionHandler:^(NSURL *localfile, NSURLResponse *response, NSError *error) {
                if(!error) {
                    NSData *json = [NSData dataWithContentsOfURL:localfile];
                    NSDictionary *flickrPlaceDictionary = [NSJSONSerialization JSONObjectWithData:json
                                                                                          options:0
                                                                                            error:NULL];
                    dispatch_async(dispatch_get_main_queue(), ^{

                        Photo *photo = nil;

                        // flickr photo unique id
                        NSString *uniqueId = [photoDictionary valueForKeyPath:FLICKR_PHOTO_ID];

                        NSFetchRequest *dbRequest = [NSFetchRequest fetchRequestWithEntityName:@"Photo"];
                        dbRequest.predicate = [NSPredicate predicateWithFormat:@"uniqueId = %@", uniqueId];

                        NSError *error;

                        NSArray *reqResults = [context executeFetchRequest:dbRequest error:&error];

                        if (!reqResults || error || [reqResults count] > 1) {
                            //handle error
                        } else if ([reqResults count]) {
                            //object found in db
                            NSLog(@"object found!");
                            photo = [reqResults firstObject];
                        } else {
                            //no object in db so create a new one
                            NSLog(@"object not found, creating new one");
                            photo = [NSEntityDescription insertNewObjectForEntityForName:@"Photo"
                                                                  inManagedObjectContext:context];
                            //set its properties
                            photo.uniqueId = uniqueId;
                            photo.title = [photoDictionary valueForKey:FLICKR_PHOTO_TITLE];
                            photo.region = [FlickrFetcher extractRegionNameFromPlaceInformation:flickrPlaceDictionary];

                            NSLog(@"title: %@", photo.title);
                            NSLog(@"ID: %@", photo.uniqueId);
                            NSLog(@"region: %@", photo.region);

                        }

                    });
                }
            }];
[task resume];

//how to get Photo *photo object???
//return photo;
}

I would really appreciate any suggestions on how to implement this.

Upvotes: 0

Views: 531

Answers (2)

Mike S
Mike S

Reputation: 42345

Since you have async operations happening inside your blocks, you'll need to pass a completion handler (block) to your photoWithFlickrData:inManagedObjectContext: method and call it when you have valid photo data.

You'll need to add a new parameter to your method so you can pass in the completion handler. I'd do something like this:

+ (void)photoWithFlickrData:(NSDictionary *)photoDictionary
     inManagedObjectContext:(NSManagedObjectContext *)context
      withCompletionHandler:(void(^)(Photo *photo))completionHandler

Then, when you have a valid photo object, call completionHandler like so:

completionHandler(photo);

It looks like you'd want to put that at the very end of the block you're passing to dispatch_async:

/* ... */
dispatch_async(dispatch_get_main_queue(), ^{
    Photo *photo = nil;

    /* ... */

    completionHandler(photo);
});
/* ... */

Then, you can call your method like so:

[Photo photoWithFlickrData:photoDictionary
    inManagedObjectContext:context
     withCompletionHandler:^(Photo* photo) {
         /* use your valid photo object here */
     }
];

Upvotes: 2

chriszumberge
chriszumberge

Reputation: 873

Outside of your block before you call [session downloadTaskWithRequest:.....] define a variable like this

__block Photo *photoObject = nil;

Then inside the block after you finish setting its properties, set

photoObject = photo;

Now you can do whatever you want with the photoObject variable outside of the block. Check out this Apple developer documentation on Blocks and Variables.

Upvotes: -1

Related Questions