iOS Stop block execution from progressing until block has finished - ios

I have a method in an iOS app that is supposed to return a bool value depending upon whether or not a web call succeeds.
The web call is structured in a way such that it takes a block as a callback parameter and that callback is called when the web call has a result. Based on that result my method needs to return a True/False value.
So, I need to stop execution from progressing any further without first having a result to return.
I am trying to achieve this via semaphores, after looking at some examples that others have shared, but the callback is never called, if I remove the semaphore then the callback is always called.
What am I missing here?
+ (BOOL)getUserInformation {
__block BOOL flag = false;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[[WebServicesManager sharedManager] getUserInformationWithCallback:^(NSInteger statusCode, NSString *response, NSDictionary *responseHeaders, id obj, NSError *error) {
if (error) {
//Handle error case and perform appropriate cleanup actions.
}
else
{
//Save user information
flag = true;
}
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return flag;
}
I put a break point on if(error) to check if the callback gets called, it doesnt, unless I remove the semaphore.
I could give this method its own callback block or I could give the containing class a delegate and achieve what I need but I would really like to make this approach work.

The WebServicesManager is probably dispatching it's block on the same thread the semaphore is waiting on.
As #Rob is correctly mentioning in the comments, this is most likely not a good idea to do on the main thread; rather make use of the asynchronous model and not block the main thread for possibly minutes until the connection may time out under certain circumstances, freezing your UI.

You are undoubtedly deadlocking because you're using semaphore on same thread to which the web services manager (or the API that that is using) dispatches its completion handler.
If you want a rendition that avoids the deadlock scenario, but also avoids the pitfalls of blocking the main thread, you can do something like:
+ (void)getUserInformation:(nonnull void (^)(BOOL))completionHandler {
[[WebServicesManager sharedManager] getUserInformationWithCallback:^(NSInteger statusCode, NSString *response, NSDictionary *responseHeaders, id obj, NSError *error) {
if (error) {
completionHandler(false);
} else {
//Save user information
completionHandler(true);
}
}];
}
Then, rather than doing something like:
BOOL success = [YourClass getUserInformation];
if (success) {
...
}
You can instead do:
[YourClass getUserInformation:^(BOOL success) {
if (success) {
...
}
}];
// but do not try to use `success` here ... put everything
// contingent upon success inside the above completion handler

Related

Executes dispatch_after when using dispatch_semaphore

I'm having a problem when I want to execute a code inside my dispatch_after block.
First of all, I'm calling a UIActivityIndicator when a button is pressed in order to show it in screen and after the uiactivityindicator starts runnning I want to execute a server call, when I get a response from the server I return that value.
The problem is: When I call my UIAtivityIndicator to run and after that I make my server call, the UIActivityIndicator doesn't show in screen even when the [UIActivityIndicatorInstance startAnimating]; was called and after that the server operation was called.
So I decided to use a dispatch_after in order to wait a certain time after de [UIActivityIndicatorInstance startAnimating]; It works whe I do this, the problem becomes when I have to return the value, so for that reason a use dispatch_semaphore to tell me when the operation has finished and then return the value.
The big problem here is that the dispatch_after is not called.
This is my code, I appreciate you can help me with this problem or some other solution you have in mind.
The main idea that I want to accomplish is that I want to show an UIActivityIndicator while the server operation is executing and when it finishes I want to return that value in the same method.
- (BOOL)getUserSatatus {
// This is when the UIActivityIndicator is starts running
[Tools startActivityIndicator];
double delayInSeconds = 0.5;
// This is used to save server response.
__block BOOL serverResponse;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_time_t executionTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
// I want to execute the server call after a perios of time in order to show first de indicator on screen
dispatch_after(executionTime, dispatch_get_main_queue(), ^{
NSLog(#"This is where the server will call");
// This is when I perform the service call and it returns a values that is
// assigned to server response.
serverResponse = [_backendManager getStatus];
// This is the signal for the semaphore in order to execute the next lines.
dispatch_semaphore_signal(semaphore);
});
// Wait until the signal in order to execute the next line.
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return serverResponse; // Here will be the server return response.
}
You say:
The big problem here is that the dispatch_after is not called.
Yes, that's because you're blocking the main thread with dispatch_semaphore_wait, so the dispatch_after never has a chance to run on the main thread and you're deadlocking.
We can walk you through ways to get around this, but you really shouldn't have synchronous network calls or semaphores in your code at all (for a myriad of reasons, not just for your activity indicator and for solving your deadlocking issue).
You should remove these synchronous network requests, remove the dispatch_after, and remove the semaphores. If you do all of that, and instead follow asynchronous patterns (like using completion blocks), your activity indicator view stuff will then work properly and you won't have any deadlock either.
The correct answer is to refactor the "back end manager" to perform its requests asynchronously (with completion blocks) and then use completion block pattern with getUserStatus method, too.
For example, let's say you fixed getStatus of the _backendManager to behave asynchronously:
- (void)getStatusWithCompletion:(void (^)(BOOL))completion {
NSMutableURLRequest *request = ... // build the request however appropriate
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
BOOL status = ...; // parse the response however appropriate
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) completion(status);
});
}];
[task resume];
}
Then you can refactor the getUserStatus from your question to also take a completion handler:
- (void)getUserStatusWithCompletion:(void (^)(BOOL))completion {
// This is when the UIActivityIndicator is starts running
[Tools startActivityIndicator];
[_backendManager getStatusWithCompletion:^(BOOL status){
[Tools stopActivityIndicator];
if (completion) completion(status);
}
}
And then the code that needs to get the user status would do something like:
[obj getUserStatusWithCompletion:^(BOOL success) {
// use `success` here
}];
// but not here

How to wait for a Parse deleteAllInBackground call to finish?

I am building a simple messaging app using Parse's framework. I have a method called displayMessages. This is called each time the phone receives a push.
However, as this message is doing work in the Parse database I don't want to call it again if it's already running. I want to wait until it is finished and then call it.
I am using the following code:
-(void)receivedPush
{
[self displayMessages];
}
and:
-(void)displayMessages
{
//code here
}
If received push is called I want it to wait until displayMessages is finished before calling it. In displayMessages I have a Parse call:
[PFObject deleteAllInBackground:toDelete block:^(BOOL succeeded, NSError *error) {
}];
It's actually this that I need to wait for, deleteAllInBackground. How can I get around this? I tried using NSOperation queue and that's fine for queuing displayMessages but this won't give my app the desired result because although displayMessages finishes execution at some point it still has deleteAllInBackground running.
If I understand correctly, your requirement is that you only want one "instance" of displayMessages running at one time. What you are asking to do is make displayMessages "threadsafe" What you should do is wrap the code in displayMessages inside #synchronized tags.
What does #synchronized() do?
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html
Alternately, you could create some kind of queueing system that uses flags, so recievedPush would actually add the push to a queue and there would be some kind of timer loop that calls display messages repeatedly but only fires when it is done processing.
Edit in response to comment
Create a manual "operation queue" that does what you want it to
- (void) methodThatRunsWhenFiredByTimer {
if (self.flag) {
[self displayMessages];
}
}
- (void) displayMessages {
PushData *data = [self dequeueData]; //maybe, not sure how you're accessing the push data
self.flag = NO;
//Code Here
[PFObject deleteAllInBackground:toDelete block:^(BOOL succeeded, NSError *error) {
//Code Here...
self.flag = YES;
}];
}
- (void) recievedPush:(Pushdata) data {
[self enqueuePushData:data];
}

Thread safety in method with asynchronous operations

I have this method
- (void) checkIfShouldSendReadingsToServerAsync
{
// Check if I want to send the data
// All other threads should wait hear until I signal them to continue
if (!self.lastSendRequestToServerDateTime ||
([[NSDate date] timeIntervalSinceDate:self.lastSendRequestToServerDateTime]/60.0) > intervalBetweenRequestsToServerInMinutes.doubleValue)
{
// Read data from the SQLite database asynchronous and a completion block
// This call uses a NSOperationQueue block
[self.sqliteStore readingsToSendToServer:^(NSArray *readingsArray)
{
// Loaded the values, now check if I should send them
if (readingsArray &&
readingsArray.count > 0)
{
// Convert array of objects to JSON
NSString* json = [self.sqliteStore readingsArrayToJSON:readingsArray];
// Send the JSON to the server using NSURLSession
[[WEEServiceRepository sharedInstance] sendJSONReadings:json withCompletionHandler:^(NSError *error, NSURLResponse *response)
{
// REST POST call ended, check the status code
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
NSInteger statusCode = httpResponse.statusCode;
self.lastSendRequestToServerDateTime = [NSDate date];
switch (statusCode)
{
case 200:
case 201:
case 204:
{
// This call uses a NSOperationQueue block
[self.sqliteStore updateReadingsWithSentToServerYes:readingsArray andCompletionBlock:^(BOOL succeed)
{
// Here allow all other threads or the same thread to enter
}];
break;
}
default:
{
// Or here signal to continue
}
}
}];
}
}];
}
}
I want this whole method to be thread safe, until the service call returns and signals that it is OK for other threads to continue it should keep them in line.
This code will run when the application is in the foreground but in the background as well.
I tried semaphores but for a reason after a while I found that it stopped in the background, deadlock maybe? But it shouldn't! I don't really want to use #synchronized, or am I wrong and go ahead and try? Maybe just use NSLock or NSRecursiveLock?
I try to find the most appropriate way to lock and unlock the section of code, especially when using asynchronous methods.
I ensured thread safety when you have to deal with a series of dependent asynchronous operation with the help of this example, overriding from NSOperation and implementing the properties isExecuting, isFinished and the start method can give you control when the queue will release the operation and proceed to the next one with in my case setting the NSOperationQueue.maxConcurrentOperationCount to 1.

Asynchronous http call is not ready when I try to use its response

My app fetches some items from web server like below:
for (photo in photoList) {
NSArray *comment = [self fetchCommentsFromServer:photo.photoId];
[photo setComment:comment];
}
fetchCommentFromServer makes asynchronous http call with dispatch_async.
dispatch_queue_t queue = dispatch_queue_create("autantication_queue", 0);
dispatch_async( queue, ^{
[manager POST:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (success) {
success(responseObject);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (failure) {
failure(error);
}
}
];
});
It gives error because of comment isn't ready when I try to attach it to photo.
How can I guarantee that the comment is ready when it's attached to photo?
I tried to use semaphores but it made the call too slow.
fetchCommentsFromServer needs a completion block. This can be executed whenever the network call has finished. Alternatively, you can work on each photo after the fetch is complete, as part of the network call completion block.
The only way you can guarantee the network fetch is finished when you want to work on the results is to not try and do any work until the network fetch is finished. This is pretty much the point of all these completion blocks and delegate methods.
The method you call to get the data executes then the next line sets the message without the response coming back. You should modify fetchCommentsFromServer to have a completion block where you then set the comment inside the block (ensuring the request has completed before trying to modify it).
On a side note, make sure to jump back on the main thread to modify any UI elements (AKA your label).
fetchCommentsFromServer: can't return anything because the data that it wants to return isn't available until after the method has completed. Instead, the method should take a completion block as a parameters and pass the data back using that. In your case, this will be called from the AFNetworking success block.

Testing background save of Core Data entity with Kiwi

I'm struggling to figure out the best method to test interacting with Core Data in a background thread. I have the following class method:
+ (void)fetchSomeJSON
{
// Download some json then parse it in the block
[[AFHTTPClient sharedClient] fetchAllThingsWithCompletion:^(id results, NSError *error) {
if ([results count] > 0) {
NSManagedObjectContext *backgroundContext = //... create a new context for background insertion
dispatch_queue_t background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(background, ^{ // If I comment this out, my test runs just fine
//... insert and update some entities
for (NSString *str in results) {
NSManagedObject *object = //...
}
});
}
}];
}
I'm currently testing this method with the following Kiwi code:
describe(#"MyAction", ^{
__block void (^completionBlock)(NSArray *array, NSError *error);
beforeEach(^{
// Stub the http client
id mockClient = [AFHTTPClient mock];
[WRNAPIClient stub:#selector(sharedClient) andReturn:mockClient];
// capture the block argument
KWCaptureSpy *spy = [mockClient captureArgument:#selector(fetchAllThingsWithCompletion:) atIndex:0];
[MyClass fetchSomeJSON]; // Call the method so we can capture the block
completionBlock = spy.argument;
// run the completion block
completionBlock(#[#"blah"], nil);
})
// If I remove the dispatch_async block, this test passes fine.
// If I add it in again the test fails, probably because its not waiting
it(#"should return the right count", ^{
// entityCount is a block that performs a fetch request count
NSInteger count = entityCount(moc, #"Task");
[[theValue(count) should] equal:theValue(4)];
})
// This works fine, but obviously I don't want to wait a second
it(#"should return the right count after waiting for a second", ^{
sleep(1);
NSInteger count = entityCount(moc, #"Task");
[[theValue(count) should] equal:theValue(4)];
});
};
If I remove the dispatch_async line, then I can get my test to run quickly. The only way I can get my test suite to run when using dispatch_async is to sleep(1) after calling the completion block. Using sleep() makes me think that I'm not approaching it in the right way. I have tried using shouldEventually but this doesn't seem to re-fetch my count value.
Have you tried these asynchronous block macros?
#define TestNeedsToWaitForBlock() __block BOOL blockFinished = NO
#define BlockFinished() blockFinished = YES
#define WaitForBlock() while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) && !blockFinished)
I have tried several approaches to solving this, none feel right.
1) Move the dispatch_async to its own class
+ (void)dispatchOnMainQueue:(Block)block
{
if ([NSThread currentThread] == [NSThread mainThread]) {
block();
} else {
dispatch_sync(dispatch_get_main_queue(), block);
}
}
+ (void)dispatchOnBackgroundQueue:(Block)block
{
dispatch_queue_t background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(background, block);
}
Then during test execution, swizzle the background dispatch to occur on the main queue. This worked, but was unpredictable. It also felt so wrong!
2) Move the setup code to Kiwi's beforeAll block, then sleep the main thread. This works as the Kiwi tests are run on the main thread, so we're effectively saying "let the background operations happen before carrying on with the tests". I think this is what I'm going to use. Yes it makes my unit tests run slower, but they pass when they should do, and fail when they should
describe(#"MyAction", ^{
__block void (^completionBlock)(NSArray *array, NSError *error);
beforeAll(^{
// Stub the http client
id mockClient = [AFHTTPClient mock];
[WRNAPIClient stub:#selector(sharedClient) andReturn:mockClient];
// capture the block argument
KWCaptureSpy *spy = [mockClient captureArgument:#selector(fetchAllThingsWithCompletion:) atIndex:0];
[WRNTaskImporter importAllTasksFromAPI];
completionBlock = spy.argument;
// run the completion block
completionBlock(#[#"blah"], nil);
// Wait for background import to complete
[NSThread sleepForTimeInterval:0.1];
})
// This works
it(#"should return the right count", ^{
// entityCount is a block that performs a fetch request count
NSInteger count = entityCount(moc, #"Task");
[[theValue(count) should] equal:theValue(4)];
})
};
The caveat of this approach is that it only works when you aren't changing any data before a test. Say for example I insert 4 entities, and want to check each entity was inserted as expected. This option would work here. If I needed to re-run the import method and check that the count hadn't increased, I would need to add another [NSThread sleepForTimeInterval:0.1] after calling the insertion code.
For normal block based Kiwi tests you should probably use either the expectFutureValue shouldEventually method, or KWCaptureSpy to test your code, but this may not help when calling nested blocks.
If anyone has a more appropriate method for testing cases like these I'm happy to hear it!

Resources