In the new iOS 7 SDK, you can perform background fetches, by implementing performFetchWithCompletionHandler (http://bit.ly/15ofOx8).
They say you are supposed to execute completionHandler when you are done. When exactly is that?
My performFetch looks like this:
NSString *result = [super.viewController.webView stringByEvaluatingJavaScriptFromString:#"loadFeeds();"];
loadFeeds is a JavaScript function in a WebView, that makes an AJAX call. Which means it's probably going to go to another thread and loose the completionHandler. How can I execute completionHandler when the Ajax call is done?
I thought about using UIWebView's shouldStartLoadWithRequest to catch a custom URL, but by then I have completely lost completionHandler.
Related
So I've recently come back to Swift & iOS after a hiatus and I've run into an issue with asynchronous execution. I'm using Giphy's iOS SDK to save myself a lot of work, but their documentation is pretty much nonexistent so I'm not sure what might be happening under the hood in their function that calls their API.
I'm calling my function containing the below code from the constructor of a static object (I don't think that's the problem as I've also tried calling it from a cellForItemAt method for a Collection View).
My issue is that my function is returning and execution continues before the API call is finished. I've tried utilizing DispatchQueue.main.async and removing Dispatch entirely, and DispatchGroups, to no avail. The one thing that worked was a semaphore, but I think I remember reading that it wasn't best practice?
Any tips would be great, I've been stuck on this for waaaaaay too long. Thanks so much in advance
GiphyCore.shared.gifByID(id) { (response, error) in
if let media = response?.data {
DispatchQueue.main.sync {
print(media)
ret = media
}
}
}
return ret
My issue is that my function is returning and execution continues before the API call is finished.
That's the whole point of asynchronous calls. A network call can take an arbitrary amount of time, so it kicks off the request in the background and tells you when it's finished.
Instead of returning a value from your code, take a callback parameter and call it when you know the Giphy call has finished. Or use a promise library. Or the delegate pattern.
The one thing that worked was a semaphore, but I think I remember reading that it wasn't best practice?
Don't do this. It will block your UI until the network call completes. Since you don't know how long that will take, your UI will be unresponsive for an unknown amount of time. Users will think your app has crashed on slow connections.
You could just add this inside a method and use a completion handler and therefore do you not need to wait for the response. You could do it like this:
func functionName(completion: #escaping (YOURDATATYPE) -> Void) {
GiphyCore.shared.gifByID(id) { (response, error) in
if let media = response?.data {
completion(media)
return
}
}
}
Call your method like this
functionName() { response in
DispatchQueue.main.async {
// UPDATE the UI here
}
}
In y iPhone App.
For search functionality. I am using UISearchBar and WebService call.
Whenever UISearchBar 'TextDidChange' happen the web service call happens.
Generally we are typing very fast, so there are many web service call happens, and I am using NSURLConnection, and I am loading table on Finished Loading.
eg,
WebService Call for M
WebService Call for Mo
WebService Call for Mor
Here, the problem is one webservice is going to finish, in between another web service called. This makes chaos.
Here, I solved the problem with writing.
**[connectionSearch cancel];**
connectionSearch=[[NSURLConnection alloc] initWithRequest:request delegate:self];
Write this code in textDidChange method for calling Webservice
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self webserviceCallMethod];
You can also call service like below after cancelPreviousPerformRequestsWithTarget:self
[self performSelector:#selector(webserviceCallMethod:) withObject:searchText afterDelay:0.3f]; // after delay can be an anything that helps.
Don't forget to remove all objects from your array, before you add objects to array again in response of your webservice. (Array that is used in cells of UITableView)
Hope it helps.
I changed the code like,
**[connectionSearch cancel];**
connectionSearch=[[NSURLConnection alloc] initWithRequest:request delegate:self];
So, whenever the same Search web service call happens, and if the call is not finished, call is cancelled and the new web service call happens.
I have an app that the first thing it does is to register itself in an API, with a simple HTTP POST. I've been doing this in func application(application: UIApplication, willFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?), as Apple states that this is the point do to URL calls. As it is important to never block the main thread, this call is done async.
The problem is that as it is done async, the first screen opens and immediately a call to the API is done. As this is faster then the first API call, the second call gets a 401.
In order to avoid that, I am doing a very cheesy thing before starting the second call:
dispatch_async(dispatch_get_global_queue(priority, 0)) {
// do some task
while (InformationFromFirsCall == nil)
{
sleep(1)
}
}
Is there a better strategy to do this? I was thinking about using a dispatch_once at the beginning of every call to the API and implementing the code inside the callback of InformationFromFirstCall.
Is that reasonable?
Thanks!
Instead of using sleep, you can save CPU time by using a semaphore:
// declared somewhere where you can access it
dispatch_semaphore_t sema;
// willFinishLaunching
sema = dispatch_semaphore_create(0);
// when your async call completes
dispatch_semaphore_signal(sema);
// in your main UI — this is the same as the sleep call you have, but doesn't waste CPU time
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
A semaphore is an efficient mechanism for forcing one part of your app to wait while another app is doing something — a call to dispatch_semaphore_wait causes the current thread to hang until the semaphore's value becomes non-zero, which happens when dispatch_semaphore_signal is called.
See this answer for more details.
Note: this is C code; you will need to adapt it to Swift syntax if you are coding your app in Swift.
In my app, I need to make https calls to a restful web api and process the results upon return. The number of simultaneous service calls is never fixed, hence the related code has been written accordingly. The data fetched from the service is temporarily stored on an SQLite DB within the app. Following is the structure how it works.
When the user navigates to any screen or UI component thereof for which data needs to be fetched, the view controller calls a method on its designated model object. This method then checks whether the data is already present in the DB or it needs to be fetched. In case data is present, it returns the same to the view controller. Otherwise, it initiates an asynchronous service request and waits till the response comes, after which it returns the data to the VC. Therefore, the VC initialises a loading indicator before calling the specified model, and dismisses the same after control is returned from this function.
Here it is important that the function on the model waits till the response is received from the web api. This is done by registering for an NSNotification which will be issued by the service module once returned data is written to the DB. A boolean variable it set to false upon making the service request and set to true once the response is received. An NSRunLoop runs on the false condition of this boolean variable. Hence once the variable is set to true, the rest of the processing can continue.
Following are the relevant pieces of code in which all this is implemented:
[serviceModule initServiceCall:#"25" withDictionary:[NSDictionary dictionaryWithObjects:#[asOfDate] forKeys:#[#"toDate"]]];
dataReady=NO;
NSString *notificationName = #"dataReady";
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(useNotificationFromServiceModule:) name:notificationName object:nil];
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (!dataReady && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
The rest of the function continues after this.
This is the function that handles the notification:
-(void)useNotificationFromServiceModule:(NSNotification *)notification {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
dispatch_async(queue, ^{
dataReady=YES;
});}
The usual process is that once the notification is sent, the NSRunLoop quits and the rest of the method completes, returning to the view controller which then dismissed the loading indicator. The problem is that sometimes this does not happen. While the notification is issued (I can see the console log), the NSRunLoop does not end. The loading indicator continues to appear on the screen and stays that way until the screen is tapped once. When the screen is tapped, the NSRunLoop ends and the rest of the process continues randomly.
This does not happen always. It happens quite randomly, maybe about 4-5 times out of 10. Kindly provide some inputs/pointers to indicate why this may be happening.
If you are using the run loop directly, you are either very clever or very stupid. In the first case, you'll find the answer yourself. In the second case, it would be much much better if you followed the same pattern as everyone else does, which is running your networking code on a background thread and using dispatch_async when the results arrive.
I am new to IOS development and am currently facing a problem.
When method A is called, it calls method B and then it wait for delegate connectionDidFinish which connectionDidFinish will execute MethodC.
My question is how do I ensure that methodA to methodC has finished executing before executing NSLog?
I found that a way to solve this problem is to use notification center. Send notification to me after finishing executing methodC. I don't think this is a good solution. Is there another way to do this?
Example:
[a methodA];
NSLog(#"FINISH");
If any of those methods perform actions asynchronously, you can't. You'll have to look into a different way of doing this. I personally try to use completion blocks when ever I can, although it's perfectly fine to do this other ways, like with delegate methods. Here's a basic example using a completion block.
- (void)someMethod
{
[self methodAWithCompletion:^(BOOL success) {
// check if thing worked.
}];
}
- (void)methodAWithCompletion:(void (^) (BOOL success))completion
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, kNilOptions), ^{
// go do something asynchronous...
dispatch_async(dispatch_get_main_queue(), ^{
completion(ifThingWorked)
});
});
}
In the code you posted, methodA must finish executing before the log statement will execute.
However, if methodA starts an asynchronous process that takes a while to finish and returns before it is finished, then you need to do something different. Usually you don't want to freeze the user interface while you are waiting, so you set up a delegate, pass in a completion block, or wait for an "ok, I'm done" notification.
All those are very valid, good ways to solve the problem of waiting for asynchronous tasks to finish running.
Newer APIs are starting to use completion blocks. Examples are:
presentViewController:animated:completion:, which takes a completion
block that gets called once the new view controller is fully
on-screen and "ready for business.
animateWithDuration:animations:completion:, which takes a completion
block that gets executed once the animation is finished, and
sendAsynchronousRequest:queue:completionHandler:, which starts an
asynchronous URL request (usually an HTTP GET or PUT request) and
provides a completion block that gets called once the request has
been completed (or fails)