how to return result after OpenWithCompletionHandler: is complete - objective-c

Want to query a photo in the Coredata database
this is my code
this is the NSObjectSubclass category
//Photo+creak.h
#import "Photo+creat.h"
#implementation Photo (creat)
+(Photo *)creatPhotoByString:(NSString *)photoName inManagedObjectContext:(NSManagedObjectContext *)context{
Photo *picture = nil;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Photo"];
request.predicate = [NSPredicate predicateWithFormat:#"name = %#", photoName];
NSArray *matches = [context executeFetchRequest:request error:nil];
if (!matches || [matches count]>1) {
//error
} else if ([matches count] == 0) {
picture = [NSEntityDescription insertNewObjectForEntityForName:#"Photo" inManagedObjectContext:context];
picture.name = photoName;
} else {
picture = [matches lastObject];
}
return picture;
}
+ (BOOL)isPhoto:(NSString *)photoName here:(NSManagedObjectContext *)context{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Photo"];
request.predicate = [NSPredicate predicateWithFormat:#"name = %#", photoName];
NSArray *matches = [context executeFetchRequest:request error:nil];
switch ([matches count]) {
case 1:
return YES;
break;
default:
return NO;
break;
}
}
#end
code inside of view controller
//View Controller
- (IBAction)insertData:(UIButton *)sender {
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:#"test"];
UIManagedDocument *defaultDocument = [[UIManagedDocument alloc] initWithFileURL:url];
if (![[NSFileManager defaultManager] fileExistsAtPath:[url path]]) {
[defaultDocument saveToURL:defaultDocument.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:NULL];
}
[defaultDocument openWithCompletionHandler:^(BOOL success) {
[Photo creatPhotoByString:#"test" inManagedObjectContext:defaultDocument.managedObjectContext];
[defaultDocument saveToURL:defaultDocument.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:NULL];
}];
[sender setTitle:#"Okay" forState:UIControlStateNormal];
[sender setEnabled:NO];
}
- (IBAction)queryFromDatabase:(UIButton *)sender {
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:#"test"];
UIManagedDocument *defaultDocument = [[UIManagedDocument alloc] initWithFileURL:url];
BOOL isItWorking = [checkPhoto isPhoto:#"test" inManagedDocument:defaultDocument];
if (isItWorking) {
[sender setTitle:#"Okay" forState:UIControlStateNormal];
} else {
[sender setTitle:#"NO" forState:UIControlStateNormal];
}
}
The NSObject Class that hook them up.
// checkPhoto.m
#import "checkPhoto.h"
#implementation checkPhoto
+ (BOOL)isPhoto:(NSString *)photoToCheck inManagedDocument:(UIManagedDocument *)document{
__block BOOL isPhotoHere = NO;
if (document.documentState == UIDocumentStateClosed) {
[document openWithCompletionHandler:^(BOOL success) {
isPhotoHere = [Photo isPhoto:photoToCheck here:document.managedObjectContext];
}];
}
return isPhotoHere;
}
#end
The coredata only have on Entity named "Photo", and it got only one attribute "name".
The problem is that the return always get execute before the block is complete and always return NO.
Test code here
Or should I do something else than openWithCompletionHandler when querying?

You need to rework your method to work asynchronously, like -openWithCompletionHandler:. It needs to take a block which is invoked when the answer is known and which receives the answer, true or false, as a parameter.
Then, the caller should pass in a block that does whatever is supposed to happen after the answer is known.
Or, alternatively, you should delay the whole chunk of logic which cares about the photo being in the database. It should be done after the open has completed.
You'd have to show more code for a more specific suggestion.
So, you could rework the isPhoto... method to something like:
+ (BOOL)checkIfPhoto:(NSString *)photoToCheck isInManagedDocument:(UIManagedDocument *)document handler:(void (^)(BOOL isHere))handler {
if (document.documentState == UIDocumentStateClosed) {
[document openWithCompletionHandler:^(BOOL success) {
handler([Photo isPhoto:photoToCheck here:document.managedObjectContext]);
}];
}
else
handler(NO);
}
Then you can rework this:
- (IBAction)queryFromDatabase:(UIButton *)sender {
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:#"test"];
UIManagedDocument *defaultDocument = [[UIManagedDocument alloc] initWithFileURL:url];
[checkPhoto checkIfPhoto:#"test" isInManagedDocument:defaultDocument handler:^(BOOL isHere){
if (isHere) {
[sender setTitle:#"Okay" forState:UIControlStateNormal];
} else {
[sender setTitle:#"NO" forState:UIControlStateNormal];
}
}];
}

Try that
+(BOOL)isPhoto:(Photo *)photo inDataBase:(UIManagedDocument *)defaultDocument{
__block BOOL isPhotoThere = NO;
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[defaultDocument openWithCompletionHandler:^(BOOL success) {
[defaultDocument.managedObjectContext performBlock:^{
isPhotoThere = [Photo checkPhoto:photo];
dispatch_semaphore_signal(sema);
}];
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
dispatch_release(sema);
return isPhotoThere;
}

Related

how we make persistentStoreCoordinator synchronize in objective c

I am new in objective c and I want to know how we make persistentStoreCoordinator #synchronize to make methods thread safe. It's crash, i can clearly see crash reason on sigabort
This NSPersistentStoreCoordinator has no persistent stores (unknown). It cannot perform a save operation.
Please provide me any link or sample where we can check persistentStoreCoordinator is synchronize.
Any help is appreciated. Thanks in advance.
-(NSManagedObjectContext *)managedObjectContext
{
#try{
if (__managedObjectContext != nil) {
return __managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
__managedObjectContext = [[NSManagedObjectContext alloc] init];
[__managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return __managedObjectContext;
}
#catch(NSException *exception){
NSLog(#"Exception managedObjectContext : %#", exception);
}
}
-(NSManagedObjectModel *)managedObjectModel
{
#try{
if (__managedObjectModel != nil) {
return __managedObjectModel;
}
NSString *modelPath = [[NSBundle mainBundle] pathForResource:DBNAME ofType:#"momd"];
if([[TrackerUtility TrackerUtilityManager] getDebugMode]){
NSLog(#"modelPath %#",modelPath);
}
NSURL *modelURL = [NSURL fileURLWithPath:modelPath];
__managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return __managedObjectModel;
}
#catch(NSException *exception){
NSLog(#"Exception managedObjectModel : %#",exception);
}
}
-(NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
#try{
if (__persistentStoreCoordinator != nil) {
return __persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:[NSString stringWithFormat:#"%#.sqlite", DBNAME]];
NSError *error = nil;
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSDictionary *options = #{
NSMigratePersistentStoresAutomaticallyOption : #YES,
NSInferMappingModelAutomaticallyOption : #YES
};
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
if([[TrackerUtility TrackerUtilityManager] getDebugMode]){
NSLog(#"persistentStoreCoordinator error %#, %#", error, [error userInfo]);
}
abort();
}
return __persistentStoreCoordinator;
}
#catch(NSException *exception){
NSLog(#"Exception persistentStoreCoordinator : %#",exception);
}
}
(NSURL *)applicationDocumentsDirectory
{
#try{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
#catch(NSException *exception){
NSLog(#"Exception applicationDocumentsDirectory : %#",exception);
}
}

facebook sharing from iPhone app

In my iphone app i am accessing my facebook info and sending it to the server. From server facebook sharing should happen
I have created my app in FB and while clicking sync button i am able to go to the FB login page.After loged in it asks for the authentication
But it just asking for "basic info" not for public sharing etc (i have included that in my FB app)
-(IBAction)fbConnect:(id)sender{
flag = 1;
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
if (appDelegate.session.isOpen) {
[self updateView];
} else {
if (appDelegate.session.state != FBSessionStateCreated) {
appDelegate.session = [[FBSession alloc] init];
}
[appDelegate.session openWithCompletionHandler:^(FBSession *session,
FBSessionState status,
NSError *error) {
[self updateView];
}];
}
NSLog(#"string issss %#",string);
}
- (void)updateView {
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
if (appDelegate.session.isOpen) {
string = [NSString stringWithFormat:#"%#",
appDelegate.session.accessToken];
NSLog(#"string issss %#",string);
NSString *urlstrng;
if(flag == 1){
urlstrng = [NSString stringWithFormat:#"https://graph.facebook.com/me?access_token=%#",string];
[self dataFetching:urlstrng];
}
if(flag == 2){
urlstrng = [NSString stringWithFormat:#"https://graph.facebook.com/me/friends? access_token=%#",string];
[self dataFetching:urlstrng];
}
} else {
string = [NSString stringWithFormat:#"%#",
appDelegate.session.accessToken];
NSString *urlstrng;
if(flag == 1){
urlstrng = [NSString stringWithFormat:#"https://graph.facebook.com/me?access_token=%#",string];
[self dataFetching:urlstrng];
}
if(flag == 2){
urlstrng = [NSString stringWithFormat:#"https://graph.facebook.com/me/friends?access_token=%#",string];
[self dataFetching:urlstrng];
}
}
}
-(void)dataFetching:(NSString*)strng1{
NSURL *url = [NSURL URLWithString:strng1];
ProfileConnector *obj = [[ProfileConnector alloc] init];
obj.delegate1 = self;
[obj parsingJson:url];
}
I believe you are not requesting extra items through your code. You can do it by using openSessionWithAllowLoginUI.
- (BOOL)openSessionWithAllowLoginUI:(BOOL)allowLoginUI {
NSArray *permissions = [[NSArray alloc] initWithObjects:
#"user_location",
#"user_birthday",
#"user_likes",
#"email",
nil];
return [FBSession openActiveSessionWithPermissions:permissions
allowLoginUI:allowLoginUI
completionHandler:^(FBSession *session,
FBSessionState state,
NSError *error) {
[self sessionStateChanged:session
state:state
error:error];
}];
}

How can I extract metadata from mp3 file in ios development

I am working on an ios music player with cloud storage.
I need to extract the music information such as title, artist, artwork.
I have an action called playit which plays and pauses the mp3 file. It should also populate some UILables and UIImage with the metadtaa that is associated with the mp3 file. The problem is that I could not get the metadata extracted from more than different 25 mp3 files. Here is my code:
The file url is correct because the audio player is able to find and play it, but I do not know why avmetadataitem is not able to get the metadata.
- (IBAction)playIt:(id)sender {
AVAudioPlayer *audioPlayer;
AVAsset *assest;
NSString * applicationPath = [[NSBundle mainBundle] resourcePath];
NSString *secondParentPath = [applicationPath stringByDeletingLastPathComponent];
NSString *soundFilePath = [[secondParentPath stringByAppendingPathComponent:#"fisal1407"] stringByAppendingPathComponent:[musicFiles objectForKey:#"show_id"] ];
NSURL *fileURL = [NSURL URLWithString:[soundFilePath stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
assest = [AVURLAsset URLAssetWithURL:fileURL options:nil];
NSArray *metadata = [assest commonMetadata];
for (NSString *format in metadata) {
for (AVMetadataItem *item in [assest metadataForFormat:format]) {
if ([[item commonKey] isEqualToString:#"title"]) {
filename.text = (NSString *)[item value];
NSLog(#" title : %#", (NSString *)[item value]);
}
if ([[item commonKey] isEqualToString:#"artist"]) {
show_id.text = (NSString *)[item value];
}
if ([[item commonKey] isEqualToString:#"albumName"]) {
// _albumName = (NSString *)[item value];
}
if ([[item commonKey] isEqualToString:#"artwork"]) {
NSData *data = [(NSDictionary *)[item value] objectForKey:#"data"];
UIImage *img = [UIImage imageWithData:data] ;
imageView.image = img;
continue;
}
}
}
if (audioPlayer == nil) {
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:&error];
audioPlayer.numberOfLoops = -1;
[audioPlayer play];
[sender setImage:[UIImage imageNamed:#"player_044.gif"] forState:UIControlStateNormal];
}
else
{
if (audioPlayer.isPlaying)
{
[sender setImage:[UIImage imageNamed:#"player_04.gif"] forState:UIControlStateNormal];
[audioPlayer pause];
} else {
[sender setImage:[UIImage imageNamed:#"player_044.gif"] forState:UIControlStateNormal];
[audioPlayer play];
}
}
}
Try
for (NSString *format in [asset availableMetadataFormats])
Instead of
NSArray *metadata = [assest commonMetadata];
for (NSString *format in metadata) {

AVAudioPlayer breaking video capture

In one of the views of my app there's a button. When pressed it is supposed to begin taking a video, trigger a sound file to start, and hide itself from view while unhiding another button. The second button is supposed to stop the video recording and make it save. Here's the code I have for the video recording, which initially worked with no problems:
in viewDidLoad:
finishButton.hidden = TRUE;
session = [[AVCaptureSession alloc] init];
movieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
NSError *error;
AVCaptureDeviceInput *videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self cameraWithPosition:AVCaptureDevicePositionFront] error:&error];
if (videoInput)
{
[session addInput:videoInput];
}
AVCaptureDevice *audioCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
NSError *audioError = nil;
AVCaptureDeviceInput *audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioCaptureDevice error:&audioError];
if (audioInput)
{
[session addInput:audioInput];
}
Float64 TotalSeconds = 35; //Total seconds
int32_t preferredTimeScale = 30; //Frames per second
CMTime maxDuration = CMTimeMakeWithSeconds(TotalSeconds, preferredTimeScale);
movieFileOutput.maxRecordedDuration = maxDuration;
movieFileOutput.minFreeDiskSpaceLimit = 1024 * 1024;
if ([session canAddOutput:movieFileOutput])
[session addOutput:movieFileOutput];
[session setSessionPreset:AVCaptureSessionPresetMedium];
if ([session canSetSessionPreset:AVCaptureSessionPreset640x480]) //Check size based configs are supported before setting them
[session setSessionPreset:AVCaptureSessionPreset640x480];
[self cameraSetOutputProperties];
[session startRunning];
and for the button:
-(IBAction)start:(id)sender
{
startButton.hidden = TRUE;
finishButton.hidden = FALSE;
//Create temporary URL to record to
NSString *outputPath = [[NSString alloc] initWithFormat:#"%#%#", NSTemporaryDirectory(), #"output.mov"];
self.outputURL = [[NSURL alloc] initFileURLWithPath:outputPath];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:outputPath])
{
NSError *error;
if ([fileManager removeItemAtPath:outputPath error:&error] == NO)
{
//Error - handle if required
}
}
//Start recording
[movieFileOutput startRecordingToOutputFileURL:outputURL recordingDelegate:self];
finally, under the last button:
[movieFileOutput stopRecording];
and here's the code to save the video:
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput
didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
fromConnections:(NSArray *)connections
error:(NSError *)error
{
NSLog(#"didFinishRecordingToOutputFileAtURL - enter");
BOOL RecordedSuccessfully = YES;
if ([error code] != noErr)
{
// A problem occurred: Find out if the recording was successful.
id value = [[error userInfo] objectForKey:AVErrorRecordingSuccessfullyFinishedKey];
if (value)
{
RecordedSuccessfully = [value boolValue];
}
}
if (RecordedSuccessfully)
{
//----- RECORDED SUCESSFULLY -----
NSLog(#"didFinishRecordingToOutputFileAtURL - success");
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:outputURL])
{
[library writeVideoAtPathToSavedPhotosAlbum:outputURL
completionBlock:^(NSURL *assetURL, NSError *error)
{
if (error)
{
}
}];
}
}
}
All of this was working just fine. Then I added a few lines so that a song file would play when the start button was pressed.
in viewDidLoad:
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/Song.aiff", [[NSBundle mainBundle] resourcePath]]];
NSError *audioFileError;
player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&audioFileError];
player.numberOfLoops = 0;
[self.player prepareToPlay];
and under the start button:
if (player == nil)
NSLog(#"Audio file could not be played");
else
[player play];
Now when the start button is pressed the song plays with no problems, but the video capture is messed up. Before adding the AVAudioPlayer stuff I would get the "didFinishRecordingToOutputFileAtURL - enter" and "didFinishRecordingToOutputFileAtURL - success" logs when I pressed the finish button, and now I get the first log as soon as I press the start button, nothing happens when I press the finish button, and no video is recorded. If I comment out the lines that make the song play then the video capture works just fine again. Any ideas what's going on here?
- (void)setupAudioSession
{
static BOOL audioSessionSetup = NO;
if (audioSessionSetup)
{
return;
}
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error: nil];
UInt32 doSetProperty = 1;
AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(doSetProperty), &doSetProperty);
[[AVAudioSession sharedInstance] setActive: YES error: nil];
audioSessionSetup = YES;
}
- (void)playAudio
{
[self setupAudioSession];
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:#"btnClick" ofType:#"wav"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:soundFilePath];
AVAudioPlayer *newPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
[fileURL release];
self.audioPlayer = newPlayer;
[newPlayer release];
[audioPlayer setDelegate:self];
[audioPlayer prepareToPlay];
audioPlayer.volume=1.0;
[audioPlayer play];
}
NOTE: Add the framework: AudioToolbox.framework.
#import <AudioToolbox/AudioServices.h>

Passing a Value Asynchronously

I have the following method that uses blocks and completion handlers:
// Returns YES if photo is stored in a virtual vacation.
- (BOOL) photoIsOnVacation
{
__block BOOL photoOnFile = NO;
// Identify the documents folder URL.
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSError *errorForURLs = nil;
NSURL *documentsURL = [fileManager URLForDirectory:NSDocumentDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:NO
error:&errorForURLs];
if (documentsURL == nil) {
NSLog(#"Could not access documents directory\n%#", [errorForURLs localizedDescription]);
} else {
// Retrieve the vacation stores on file.
NSArray *keys = [NSArray arrayWithObjects:NSURLLocalizedNameKey, nil];
NSArray *vacationURLs = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:documentsURL
includingPropertiesForKeys:keys
options:NSDirectoryEnumerationSkipsHiddenFiles
error:nil];
if (!vacationURLs) photoOnFile = NO;
else {
// Search each virtual vacation for the photo.
for (NSURL *vacationURL in vacationURLs) {
NSError *errorForName = nil;
NSString *vacationName = nil;
[vacationURL getResourceValue:&vacationName forKey:NSURLNameKey error:&errorForName];
[VacationHelper openVacationWithName:vacationName usingBlock:^(UIManagedDocument *vacationDocument) {
NSError *error = nil;
NSManagedObjectContext *moc = vacationDocument.managedObjectContext;
// Build fetch request.
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Photo"];
NSString *currentPhotoID = [self.chosenPhoto objectForKey:FLICKR_PHOTO_ID];
request.predicate = [NSPredicate predicateWithFormat:#"unique = %#", currentPhotoID];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"unique" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
// Execute fetch request.
NSArray *checkPhotos = [moc executeFetchRequest:request error:&error];
if (error) {
NSLog(#"Error searching for photo:%#",error);
} else {
Photo *checkPhoto = [checkPhotos lastObject];
if ([checkPhoto.unique isEqualToString:currentPhotoID]) photoOnFile = YES;
}
}];
if (photoOnFile) break;
}
}
}
return photoOnFile;
}
My problem is that photoOnFile is always false because execution reaches the return before the block that contains the fetch request. I've tried embedding the photoOnFile assignment within dispatch_async(dispatch_get_main_queue(),^{ but that hasn't helped. Any guidance appreciated.
Update: here is the reworked code successfully incorporating Ken's recommended solution:
- (void)checkIfPhotoIsOnVacationAndDo:(void(^)(BOOL photoIsOnVacation))completionBlock
{
__block BOOL photoIsOnVacation = NO;
// Identify the documents folder URL.
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSError *errorForURLs = nil;
NSURL *documentsURL = [fileManager URLForDirectory:NSDocumentDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:NO
error:&errorForURLs];
if (documentsURL == nil) {
NSLog(#"Could not access documents directory\n%#", [errorForURLs localizedDescription]);
} else {
// Retrieve the vacation stores on file.
NSArray *keys = [NSArray arrayWithObjects:NSURLLocalizedNameKey, nil];
NSArray *vacationURLs = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:documentsURL
includingPropertiesForKeys:keys
options:NSDirectoryEnumerationSkipsHiddenFiles
error:nil];
if (!vacationURLs) photoIsOnVacation = NO;
else {
// Search each virtual vacation for the photo.
for (NSURL *vacationURL in vacationURLs) {
NSError *errorForName = nil;
NSString *vacationName = nil;
[vacationURL getResourceValue:&vacationName forKey:NSURLNameKey error:&errorForName];
[VacationHelper openVacationWithName:vacationName usingBlock:^(UIManagedDocument *vacationDocument) {
NSError *error = nil;
NSManagedObjectContext *moc = vacationDocument.managedObjectContext;
// Build fetch request.
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Photo"];
NSString *currentPhotoID = [self.chosenPhoto objectForKey:FLICKR_PHOTO_ID];
request.predicate = [NSPredicate predicateWithFormat:#"unique = %#", currentPhotoID];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"unique" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
// Execute fetch request.
NSArray *checkPhotos = [moc executeFetchRequest:request error:&error];
if (error) {
NSLog(#"Error searching for photo:%#",error);
} else {
Photo *checkPhoto = [checkPhotos lastObject];
if ([checkPhoto.unique isEqualToString:currentPhotoID]) {
photoIsOnVacation = YES;
completionBlock(photoIsOnVacation);
}
}
}];
if (photoIsOnVacation) break;
}
completionBlock(photoIsOnVacation);
}
}
}
Asynchronicity tends to spread. Once you make an API asynchronous, all of its callers have to be redesigned to work asynchronously, too. Therefore, a method like your - (BOOL) photoIsOnVacation is untenable because its interface is synchronous – the caller expects to have an answer as soon as the call completes – but the implementation doesn't work that way.
You have to redesign to something like - (void) checkIfPhotoIsOnVacationAndDo:(void(^)(BOOL photoIsOnVacation))block. That takes a block from the caller and invokes the block with the answer when it is known.
This is what semaphores are for:
bool waitForBlockToExecute()
{
__block bool value = false;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// sleep for a bit
sleep(1);
value = true;
// notify that the block is finished
dispatch_semaphore_signal(semaphore);
});
// wait for the semaphore
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(semaphore); // clean up the semaphore
return value;
}
Obviously, the dispatch_async block would be replaced with your callback block, but I assume you get the picture from the above code.