Wait until multiple networking requests have all executed - including their completion blocks
Use dispatch groups.
dispatch_group_t group = dispatch_group_create();
MyCoreDataObject *coreDataObject;
dispatch_group_enter(group);
AFHTTPRequestOperation *operation1 = [[AFHTTPRequestOperation alloc] initWithRequest:request1];
[operation1 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
coreDataObject.attribute1 = responseObject;
sleep(5);
dispatch_group_leave(group);
}];
[operation1 start];
dispatch_group_enter(group);
AFHTTPRequestOperation *operation2 = [[AFHTTPRequestOperation alloc] initWithRequest:request1];
[operation2 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
coreDataObject.attribute2 = responseObject;
sleep(10);
dispatch_group_leave(group);
}];
[operation2 start];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
[context save:nil];
Wait until multiple request to be executed and their completion block
What you need is something called resource locking. You can achieve this by using DispatchGroup
.
First you need to create a DispatchGroup
. Add a property in your controller:
let dispatchGroup = DispatchGroup()
Then modify your refresh(param:)
function as: (I've modified some of the coding patterns)
func refresh(param: NSInteger) -> Void {
// You lock your resource by entering to the dispatch group
dispatchGroup.enter()
let absolutePath = "MY SAMPLE API"
var headers = [String: String]()
headers["Content-Type"] = "application/json"
print("Entered \(param)")
Alamofire.request(absolutePath, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: headers).responseString { [weak self] (response) in
switch response.result {
case .success:
print("SUCCESS \(param)")
break
case .failure(let error):
print(error)
}
// You release the resource as soon as you get the response so that other processes may be able to use the resource
self?.dispatchGroup.leave()
}
// The lock continues by invoking the wait method
dispatchGroup.wait()
}
So, this will work as:
Method 1
& Method 2
are requesting to use the same resource. When Method 1
is executing, Method 2
will wait for Method 1 to finish. When Method 1
is finished, Method 2
will be given the opportunity to start it's execution.
So, basically which method first starts executing will finish and then the other will be started. Though it's not guaranteed which will start the execution first (As, you don't need dependency on each other). But it will depend on the sequence you invoke this method.
Wait for multiple AFNetworking requests to complete
The dispatch queue for completionBlock
. If NULL
(default), the main queue is used.
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.app", DISPATCH_QUEUE_CONCURRENT);
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.completionQueue = queue;
for(int k = 0; k < 10; k++) {
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"%d", k);
[manager GET:@"http://baidu.com/" parameters:nil progress:nil success:^(NSURLSessionDataTask *_Nonnull task, id _Nullable responseObject) {
NSLog(@"success");
dispatch_group_leave(group);
} failure:^(NSURLSessionDataTask *_Nullable task, NSError *_Nonnull error) {
NSLog(@"failure");
dispatch_group_leave(group);
}];
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"DONE");
Need to make two HTTP network requests simultaneously (with a completion handler once both finish)
You should use dispatch groups, entering the group before you issue the request, and leaving the group in the completion handler for the request. So, let's assume, for a second, that you had some method that performed an asynchronous request, but supplied a completion handler parameter that was a closure that will be called when the network request is done:
func perform(request: URLRequest, completionHandler: @escaping () -> Void) { ... }
To start these two concurrent requests, and be notified when they're done, you'd do something like:
let group = DispatchGroup()
group.enter()
perform(request: first) {
group.leave()
}
group.enter()
perform(request: second) {
group.leave()
}
group.notify(queue: .main) {
print("both done")
}
Clearly, your implementation of perform(request:)
may vary significantly (e.g. you might have the closure pass the data back), but the pattern is the same whether you are writing your own networking code with URLSession
or using Alamofire. Just use GCD groups, entering the group when you create the requests, and leaving the group in the completion handler of the asynchronous request.
Wait until swift for loop with asynchronous network requests finishes executing
You can use dispatch groups to fire an asynchronous callback when all your requests finish.
Here's an example using dispatch groups to execute a callback asynchronously when multiple networking requests have all finished.
override func viewDidLoad() {
super.viewDidLoad()
let myGroup = DispatchGroup()
for i in 0 ..< 5 {
myGroup.enter()
Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
print("Finished request \(i)")
myGroup.leave()
}
}
myGroup.notify(queue: .main) {
print("Finished all requests.")
}
}
Output
Finished request 1
Finished request 0
Finished request 2
Finished request 3
Finished request 4
Finished all requests.
How to wait until a async call complete, including completion block (AFNetworking)
You're probably not getting any output because your program is never moving past the call to dispatch_group_wait
. If it did, then you'd see the "outside" log statement.
If dispatch_group_wait
is never returning, then there must still be something in the group. In your sample code, you add one thing to the group with dispatch_group_enter
and then remove it in either the success or failure handler for the api call with dispatch_group_leave
. This means that dispatch_group_leave
is not being called for some reason.
My suspicion is that the reason the blocks are not being called is that they will be invoked asynchronously on the same dispatch queue that your outer code runs on. If this is the case, then they can't run until dispatch_group_wait
returns and dispatch_group_wait
cannot return until the blocks run. This is called deadlock. (Edit: Alternatively, it could be that some part of the program that invokes the success or failure blocks is the part that is leading to deadlock. Either way, the result is that the blocks can't get called since dispatch_group_wait
never returns.)
The other possibility is that the method -dealWithZTStrangeJSON:
never returns for some reason. If this is the case, then the success block will be invoked (you could set a breakpoint on its first line to verify), but it will never make it to dispatch_group_leave
.
In either case, I would recommend that you think about solving your problem another way instead of waiting for the operation to finish. Perhaps you can do the things that you were planning to do after dispatch_group_wait
returns inside of the success handler instead (or another way of thinking about it would be that the success or failure handler could call a method that does the things you're currently doing after dispatch_group_wait
—either way will work, but sometimes I find that it's easier to keep my code organized by calling out to a method instead of putting all of the code in a block. This could be especially useful if you want to share some code between the success and failure blocks).
Wait until all iOS blocks are executed before moving on
I played around with dispatch groups the other day, here is a really helpful blog post by jrturton which should help you with the basics!
But basically it looks like your missing the line to enter/leave the dispatch group. So non of your runAPI
methods are being run as you have no items in the group and [[NSNotificationCenter defaultCenter] postNotificationName:@"apiDataUpdated" object:self];
gets called straight away.
dispatch_group_t group = dispatch_group_create();
for(MyAPIEndpoint __weak __block *ep in apiList)
{
dispatch_group_enter(group);
[self runAPI:ep withCompletionBlock:^(MyAPIEndpoint *api, NSError *err)
{
// update the data model here, code omitted as it's not relevant
dispatch_group_leave(group);
}];
}
});
dispatch_group_notify(group, dispatch_get_main_queue(),^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"apiDataUpdated" object:self];
});
Waiting until two async blocks are executed before starting another block
Use dispatch groups: see here for an example, "Waiting on Groups of Queued Tasks" in the "Dispatch Queues" chapter of Apple's iOS Developer Library's Concurrency Programming Guide
Your example could look something like this:
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block1
NSLog(@"Block1");
[NSThread sleepForTimeInterval:5.0];
NSLog(@"Block1 End");
});
dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block2
NSLog(@"Block2");
[NSThread sleepForTimeInterval:8.0];
NSLog(@"Block2 End");
});
dispatch_group_notify(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block3
NSLog(@"Block3");
});
// only for non-ARC projects, handled automatically in ARC-enabled projects.
dispatch_release(group);
and could produce output like this:
2012-08-11 16:10:18.049 Dispatch[11858:1e03] Block1
2012-08-11 16:10:18.052 Dispatch[11858:1d03] Block2
2012-08-11 16:10:23.051 Dispatch[11858:1e03] Block1 End
2012-08-11 16:10:26.053 Dispatch[11858:1d03] Block2 End
2012-08-11 16:10:26.054 Dispatch[11858:1d03] Block3
How to know now when multiple server call methods with nested loops have all finished
Didn't want to have to answer my own question... don't really like doing that, and was hoping for a more elegant solution... but it doesnt seem to be coming and someone even downvoted the question. So i'm just going to post my solution and close the question out. If you end up with a similar problem and are reading this, I dealt with it by:
creating 2 int's for each method. One int is immediately set with the count of the array being iterated through. at the end of each iteration, I am posting an NSNotification. In the NSNotification handler method, i am ++ incrementing the second int & each time running an if condition, checking to see if they match.
It's a pain to keep track of all these when there are many methods like this happening... So if anyone ever finds a better solution, I'd love to hear it. Thanks to everyone who answered and tried to be helpful, I appreciate it!
How to make multiple GET request and to wait that all operations are done?
I found a good solution with AFNetworking
, and I could do exactly what I wanted to do :
NSArray *operations = [AFURLConnectionOperation batchOfRequestOperations:mutableOperations progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {
NSLog(@"%lu of %lu complete", numberOfFinishedOperations, totalNumberOfOperations);
} completionBlock:^(NSArray *operations) {
[self.delegate dataRefreshingDone];
}];
[[NSOperationQueue mainQueue] addOperations:operations waitUntilFinished:NO];
It will execute all my GET
operations one by one.
I made a special getter
to create an operation according a url
.
- (AFHTTPRequestOperation *)operationGetForUrl:(NSString *)url
{
NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/api/%@", [NSString stringWithUTF8String:kBaseURL], url]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
[request setHTTPMethod:@"GET"];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation.responseSerializer setAcceptableContentTypes: [operation.responseSerializer.acceptableContentTypes setByAddingObject:@"text/html"]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// YOUR SUCCESS BLOCK
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// FAILURE BLOCK
}];
return operation;
}
So as you see, for each operation, I have a success block, its possible to make some generic things ... Or you can make specifics operation getter.
Related Topics
Testing Corelocation on iPhone Simulator
Insert CSS into Loaded HTML in Uiwebview/Wkwebview
Xcodebuild Different Provisioning Profile for Target Dependency
Swift Filter Dictionary Error: Cannot Assign a Value of Type '[(_, _)]' to a Value of Type '[_:_]'
Get Size of a View in React Native
Presenting a Uialertcontroller Properly on an iPad Using iOS 8
Uilabel Layer Cornerradius Negatively Impacting Performance
How to Draw a Line Programmatically from a View Controller
How to Capitalize Each Word in a String Using Swift iOS
Swift Equivalent to '[Nsdictionary Initwithobjects: Forkeys:]'
Extract Uiimage from Nsattributed String
How to Insert the Uitextview into Uialertview in iOS
Achieving Bright, Vivid Colors for an iOS 7 Translucent Uinavigationbar
How to Animate the Change of Image in an Uiimageview
Getting Data Out of a Closure That Retrieves Data from Firebase