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];
}
Related
I have four api calls to make. They should be in following order:
apiSyncDataToCloud;
apiSyncImagesToServer;
apiDeleteDataFromCloud;
apiSyncDataFromCloudInBackground;
Each one of them is to be called irrespective of the fact that previous one finishes successfully or fails.
Also, each one of them have success and failure completion blocks.
In success completion block database is updated.
All this process has to be performed in background and has to be done a no of times.
Api calls are of course performed in background but once a call completes database update is performed on main thread thereby freezing the app.
So, I went with several solutions:
Tried following code:
NSOperationQueue *queue = [NSOperationQueue new];
queue.maxConcurrentOperationCount = 1;
[queue addOperationWithBlock:^{
[self apiSyncDataToCloud];
}];
[queue addOperationWithBlock:^{
[self apiSyncImages];
}];
[queue addOperationWithBlock:^{
[self apiDeleteDataFromCloud];
}];
[queue addOperationWithBlock:^{
[self apiSyncDataFromCloudInBackground];
}];
But this only guarantees that api method calls will be performed in order. But their result follows no specific order. That is, method calls will be in the order specified but success block of apiSyncImagesToServer may be called before success block of apiSyncDataToCloud.
Then I went with following solution:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self apiSyncDataToCloud];
});
and in the success and failure blocks of apiSyncDataToCloud I have called apiSyncImagesToServer. This too did'nt work.
Now I am simply going with my last solution. I am just calling apiSyncDataToCloud.
In success completion block this method first updates the database and then calls other api.
In failure completion block this method simply makes the api call without updating the database.
For example-
structure of apiSyncDataToCloud is as follows:
-(void)apiSyncDataToCloud{
NSLog(#"method 1");
NSMutableDictionary *dicDataToBeSynced = [NSMutableDictionary dictionary];
dicDataToBeSynced = [self getDataToBeSynced];
if (dicDataToBeSynced.count!=0) {
if ([[StaticHelper sharedObject] isInternetConnected]) {
[[ApiHandler sharedObject] postRequestWithJsonString:API_SYNC_DATA_TO_CLOUD andHeader:[UserDefaults objectForKey:kAuthToken] forHeaderField:kAccessToken andParameters:dicDataToBeSynced WithSuccessBlock:^(NSURLResponse *response, id resultObject, NSError *error) {
NSLog(#"Data synced successfully to server");
[self updateColumnZSYNC_FLAGForAllTables];//updating db
[self apiSyncImagesToServer];//api call
} andFailureBlock:^(NSURLResponse *task, id resultObject, NSError *error) {
NSLog(#"Data syncing to cloud FAILED");
[self apiSyncImagesToServer];//simply make api call without updating db
}];
}
}else{
[self apiSyncImagesToServer];make api call even if no data to be synced found
}
}
Similary, inside apiSyncImagesToServer I am calling apiDeleteDataFromCloud.....
As a result my problem remained as it is. App freezes when it comes to success block updating db, downloading images...all operations being performed on main thread.
Plz let me know a cleaner and better solution.
You can create your own custom queue and call request one by one.
i.e.
dispatch_queue_t myQueue;//declare own queue
if (!myQueue) {//check if queue not exists
myQueue = dispatch_queue_create("com.queue1", NULL); //create queue
}
dispatch_async(myQueue, ^{[self YOUR_METHOD_NAME];});//call your method in queue block
If you want update some UI after receiving data then update UI on main Thread.
1) Better to use AFNetworking for this kind of situations. Because AFNetworking provides better way to handle Main & Background Threads.
AFNetworking supports success and failure blocks so you can do one by one WS Api calls from success and failure of previous WS Api call. So during this time period show progress HUD. Success of last API then update DB and hide progress HUD.
2) If you need to use NSOperationQueue and NSInvocationOperation
and follow this link. https://www.raywenderlich.com/76341/use-nsoperation-nsoperationqueue-swift
Api calls are of course performed in background but once a call
completes database update is performed on main thread thereby freezing
the app.
Then why not perform it in a separate queue?
Try using
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//your code
});
to perform time-consuming tasks and
dispatch_async(dispatch_get_main_queue(), ^{
//your code
});
to only update UI.
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
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
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.
I have a unit test in which I need to wait for an async task to finish. I am trying to use NSConditionLock as it seems to be a pretty clean solution but I cannot get it to work.
Some test code:
- (void)testSuccess
{
loginLock = [[NSConditionLock alloc] init];
Login login = [[Login alloc] init];
login.delegate = self;
// The login method will make an async call.
// I have setup myself as the delegate.
// I would like to wait to the delegate method to get called
// before my test finishes
[login login];
// try to lock to wait for delegate to get called
[loginLock lockWhenCondition:1];
// At this point I can do some verification
NSLog(#"Done running login test");
}
// delegate method that gets called after login success
- (void) loginSuccess {
NSLog(#"login success");
// Cool the delegate was called this should let the test continue
[loginLock unlockWithCondition:1];
}
I was trying to follow the solution here:
How to unit test asynchronous APIs?
My delegate never gets called if I lock. If I take out the lock code and put in a simple timer it works fine.
Am I locking the entire thread and not letting the login code run and actually make the async call?
I also tried this to put the login call on a different thread so it does not get locked.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[login login];
});
What am I doing wrong?
EDIT adding login code. Trimmed do the code for readability sake. Basically just use AFNetworking to execute a POST. When done will call delegate methods.
Login make a http request:
NSString *url = [NSString stringWithFormat:#"%#/%#", [_baseURL absoluteString], #"api/login"];
[manager POST:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (_delegate) {
[_delegate loginSuccess];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (_delegate) {
[_delegate loginFailure];
}
}];
The answer can be found in https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFHTTPRequestOperation.m.
Since you are not setting the completionQueue property of the implicitly created AFHTTPRequestOperation, it is scheduling the callbacks on the main queue, which you are blocking.
Unfortunately, many answers (not all) in the given SO thread ("How to unit test asynchronous APIs?") are bogus and contain subtle issues. Most authors don't care about thread-safity, the need for memory-barriers when accessing shared variables, and how run loops do work actually. In effect, this leads to unreliable and ineffective code.
In your example, the culprit is likely, that your delegate methods are dispatched on the main thread. Since you are waiting on the condition lock on the main thread as well, this leads to a dead lock. One thing, the most accepted answer that suggests this solution does not mention at all.
A possible solution:
First, change your login method so that it has a proper completion handler parameter, which a call-site can set in order to figure that the login process is complete:
typedef void (^void)(completion_t)(id result, NSError* error);
- (void) loginWithCompletion:(completion_t)completion;
After your Edit:
You could implement your login method as follows:
- (void) loginWithCompletion:(completion_t)completion
{
NSString *url = [NSString stringWithFormat:#"%#/%#", [_baseURL absoluteString], #"api/login"];
[manager POST:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (completion) {
completion(responseObject, nil);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (completion) {
completion(nil, error);
}
}];
Possible usage:
[self loginWithCompletion:^(id result, NSError* error){
if (error) {
[_delegate loginFailure:error];
}
else {
// Login succeeded with "result"
[_delegate loginSuccess];
}
}];
Now, you have an actual method which you can test. Not actually sure WHAT you are trying to test, but for example:
-(void) testLoginController {
// setup Network MOCK and/or loginController so that it fails:
...
[loginController loginWithCompletion:^(id result, NSError*error){
XCTAssertNotNil(error, #"");
XCTAssert(...);
<signal completion>
}];
<wait on the run loop until completion>
// Test possible side effects:
XCTAssert(loginController.isLoggedIn == NO, #""):
}
For any other further steps, this may help:
If you don't mind to utilize a third party framework, you can then implement the <signal completion> and <wait on the run loop until completion> tasks and other things as described here in this answer: Unit testing Parse framework iOS