Wait For Asynchronous Operation To Complete in Swift
you have to pass your async function the handler to call later on:
func application(application: UIApplication!, performFetchWithCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)!) {
loadShows(completionHandler)
}
func loadShows(completionHandler: ((UIBackgroundFetchResult) -> Void)!) {
//....
//DO IT
//....
completionHandler(UIBackgroundFetchResult.NewData)
println("Background Fetch Complete")
}
OR (cleaner way IMHO)
add an intermediate completionHandler
func application(application: UIApplication!, performFetchWithCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)!) {
loadShows() {
completionHandler(UIBackgroundFetchResult.NewData)
println("Background Fetch Complete")
}
}
func loadShows(completionHandler: (() -> Void)!) {
//....
//DO IT
//....
completionHandler()
}
Waiting until the task finishes
Use DispatchGroup
s to achieve this. You can either get notified when the group's enter()
and leave()
calls are balanced:
func myFunction() {
var a: Int?
let group = DispatchGroup()
group.enter()
DispatchQueue.main.async {
a = 1
group.leave()
}
// does not wait. But the code in notify() gets run
// after enter() and leave() calls are balanced
group.notify(queue: .main) {
print(a)
}
}
or you can wait:
func myFunction() {
var a: Int?
let group = DispatchGroup()
group.enter()
// avoid deadlocks by not using .main queue here
DispatchQueue.global(attributes: .qosDefault).async {
a = 1
group.leave()
}
// wait ...
group.wait()
print(a) // you could also `return a` here
}
Note: group.wait()
blocks the current queue (probably the main queue in your case), so you have to dispatch.async
on another queue (like in the above sample code) to avoid a deadlock.
Wait for asynchronous block before continuing
You can use completion closure for this
func a(completion: @escaping (_ value:String)->()) {
var x: String = ""
async block {
x = "test"
completion(x) //when x has new value
}
}
//Call like this (value will be executed when the completion block is returned
a { (value) in
print(value)
}
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.
Swift: OperationQueue - wait for asynchronous calls to finish before starting next (have maximum of 2 URLRequests running at a time)
Your original code with the operation queue and your original iamgeFromUrl
method are all you need if you make one small change to imageFromUrl
. You need to add a couple lines of code to ensure that imageFromUrl
doesn't return until the download is complete.
This can be done using a semaphore.
public func imageFromUrl(_ urlString: String) {
if let url = URL(string: urlString) {
let request = URLRequest(url: url)
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let semaphore = DispatchSemaphore(value: 0)
let task = session.dataTask(with: request, completionHandler: {(data, response, error) in
semaphore.signal()
if let imageData = data as Data? {
DispatchQueue.main.async {
self.setImageData(imageData)
}
}
});
task.resume()
semaphore.wait()
}
}
As written now, the imageFromUrl
will only return once the download completes. This now allows the operation queue to properly run the 2 desired concurrent operations.
Also note the code is modified to avoid using NSURL
and NSURLRequest
.
DispatchQueue does not wait for async function to complete
Your problem is that callRestService
will dispatch an asynchronous network operation, so your group.leave
will be called immediately, firing your group.notify
.
You could put the group.leave
in a completion handler, but you should avoid blocking code. I would suggest you structure your getTrackingInfo
as asynchronous function that takes a completion handler:
public func getTrackingInfo(_ trackingNumber: String, completion:(TrackingInfo?,Error?) -> Void) {
self.callRestService(requestUrl: self.getRequest(trackingNumber)) { (data, response, error) in
guard error == nil, let returnData = data else {
completion(nil,error)
return
}
completion(TrackingInfo(returnData),nil)
}
}
private func getRequest(_ trackingNumber: String) -> String {
let APIUsername = "Intentionally Omitted"
let trackingXmlLink = "http://production.shippingapis.com/ShippingAPI.dll?API=TrackV2&XML=%3CTrackFieldRequest%20USERID=%22" + APIUsername + "%22%3E%20%3CRevision%3E1%3C/Revision%3E%20%3CClientIp%3E127.0.0.1%3C/ClientIp%3E%20%3CSourceId%3EFaiz%20Surani%3C/SourceId%3E%20%3CTrackID%20ID=%22" + trackingNumber + "%22%3E%20%3CDestinationZipCode%3E66666%3C/DestinationZipCode%3E%20%3CMailingDate%3E2010-01-01%3C/MailingDate%3E%20%3C/TrackID%3E%20%3C/TrackFieldRequest%3E"
return trackingXmlLink
}
public func callRestService(requestUrl:String, completion:(Data? , URLResponse? , Error? ) -> Void) ->Void
{
var request = URLRequest(url: URL(string: requestUrl)!)
request.httpMethod = "GET"
let session = URLSession.shared
let task = session.dataTask(with: request, completionHandler: completion)
task.resume()
}
How to make async / await in Swift?
Thanks to vadian's comment, I found what I expected, and it's pretty easy. I use DispatchGroup()
, group.enter()
, group.leave()
and group.notify(queue: .main){}
.
func myFunction() {
let array = [Object]()
let group = DispatchGroup() // initialize
array.forEach { obj in
// Here is an example of an asynchronous request which use a callback
group.enter() // wait
LogoRequest.init().downloadImage(url: obj.url) { (data) in
if (data) {
group.leave() // continue the loop
}
}
}
group.notify(queue: .main) {
// do something here when loop finished
}
}
Related Topics
Issue with Unsafepointer<Uint8> in SQLite Project in Swift
Pass Parameter in Webservice in Swift
Does App Store Reject Submission If Nsallowsarbitraryloads Set to Yes
Dial Ussd Code from iPhone Programmatically
How to Post Nested JSON by Swiftyjson and Alamofire
How to Handle iPhone Screen Sizes/Resolution for Background Images
iOS Swift Multiple Dimension Arrays - Compiliing Takes Ages. What Should I Change
Coca Pod Chart Not Appearing (Swift4)
How to Disable the Network in iOS Simulator
Specifying One Dimension of Cells in Uicollectionview Using Auto Layout
Fixing Xcode 9 Issue: "iPhone Is Busy: Preparing Debugger Support for Iphone"
I Get Conflicting Provisioning Settings Error When I Try to Archive to Submit an iOS App
Itsappusesnonexemptencryption Export Compliance While Internal Testing
Cocoa Touch: How to Change Uiview's Border Color and Thickness