I'm attempting to create a simple package tracking app that uses the USPS API to fetch data. The callRestService method successfully fetches the data and its completion handler serviceCallback (which sets the unparsedXml property) works. However, the method that I call callRestService from does not wait for it and its completion handler to complete before moving on, resulting in my print(unparsedXml) statement returning nil.
As shown below, I tried to use a DispatchGroup object and DispatchQueue to make the function wait for callRestService's completion but it continues regardless. How can I make the function wait for the call to complete?
var unparsedXml:String?
public func getTrackingInfo(_ trackingNumber: String) -> TrackingInfo {
let group = DispatchGroup()
group.enter()
DispatchQueue.global(qos: DispatchQoS.default.qosClass).async {
self.callRestService(requestUrl: self.getRequest(trackingNumber))
group.leave()
}
group.wait()
print(unparsedXml)
return TrackingInfo()
}
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%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) ->Void
{
var request = URLRequest(url: URL(string: requestUrl)!)
request.httpMethod = "GET"
let session = URLSession.shared
let task = session.dataTask(with: request, completionHandler: serviceCallback)
task.resume()
}
private func serviceCallback(data:Data? , response:URLResponse? , error:Error? ) -> Void
{
unparsedXml = String(data: data!, encoding: .utf8)
//print(unparsedXml) Works correctly when uncommented
}
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()
}