How to wait for completion dataTaskWithRequest - Swift? - swift

I try to compare files (tiles) replace the old file on the phone new file from the site, if necessary. But returnPathFile in func pathDir is assigned reference to a file without waiting for the check for and download a new file. How do I get to wait until the completion of testing and loading (if necessary) of the new file?
let urls = { (x: UInt, y: UInt, zoom: UInt) -> NSURL in
let pathtodir = "tiles/\(zoom)/\(x)"
let pathtofile = "\(y).png"
let url = FileManagement.pathDir(pathtodir, file: pathtofile)
return url
}
layerTile = GMSURLTileLayer(URLConstructor: urls)
layerTile.map = self.viewMap!
Method of analyzing whether there is a file in iPhone:
class func pathDir(dir: String, file: String) -> NSURL {
var returnPathFile = NSURL(string: "http://****.***/\(dir)/\(file)")
let url = NSURL(string: "http://*****.***/\(dir)/\(file)")
let pathFile = applicTempDir().stringByAppendingPathComponent("\(dir)/\(file)")
let documentsUrl = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent("\(dir)/\(file)")
var attributesToInt: Int!
if filemgr.fileExistsAtPath("\(pathFile)") {
returnPathFile = NSURL(fileURLWithPath: pathFile)
let fileManager = NSFileManager.defaultManager()
do {
let attributes = try fileManager.attributesOfItemAtPath(pathFile)["NSFileCreationDate"] as! NSDate
attributesToInt = Int(attributes.timeIntervalSinceReferenceDate)
}
catch let error as NSError {
print("Ooops! Something went wrong: \(error)")
}
JsonData.isConnectedToNetwork(url!, dateFileOnTemp: attributesToInt, urlFileOnTemp: documentsUrl, pathFileOnTemp: pathFile, completion:{(path:String, error:NSError!) in
})
if filemgr.fileExistsAtPath("\(pathFile)") {
returnPathFile = NSURL(fileURLWithPath: pathFile)
return returnPathFile!
} else {
return returnPathFile!
}
}
}
Method compere two files:
class func isConnectedToNetwork(url: NSURL, dateFileOnTemp: Int, urlFileOnTemp: NSURL, pathFileOnTemp: String, completion:(path:String, error:NSError!) -> Void) {
var dates: String!
let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil)
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "HEAD"
let task = session.dataTaskWithRequest(request, completionHandler: { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
if (error == nil) {
if let response = response as? NSHTTPURLResponse {
dates = response.allHeaderFields["Last-Modified"] as! String
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'"
dateFormatter.timeZone = NSTimeZone(abbreviation: "GMT")
let dateFileOnSite = dateFormatter.dateFromString(dates)
let dateFileOnSiteInt = Int(dateFileOnSite!.timeIntervalSinceReferenceDate)
if dateFileOnSiteInt > dateFileOnTemp {
FileManagement.reDelFile(pathFileOnTemp)
JsonData.reLoadFileAsync(url, destinationUrl: urlFileOnTemp as NSURL, completion:{(path:String, error:NSError!) in
})
}
}
}
else {
completion(path: urlFileOnTemp.path!, error:error)
}
})
task.resume()
}
Method loads new file:
class func reLoadFileAsync(url: NSURL, destinationUrl: NSURL, completion:(path:String, error:NSError!) -> Void) {
let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil)
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "GET"
let task = session.dataTaskWithRequest(request, completionHandler: { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
if (error == nil) {
if let response = response as? NSHTTPURLResponse {
if response.statusCode == 200 {
if data!.writeToURL(destinationUrl, atomically: true) {
completion(path: destinationUrl.path!, error:error)
} else {
let error = NSError(domain:"Error saving file", code:1001, userInfo:nil)
completion(path: destinationUrl.path!, error:error)
}
}
}
}
else {
completion(path: destinationUrl.path!, error:error)
}
})
task.resume()
}

Try to use NSOperationQueue and add your operations accordingly to the queue. This thread might help you NSOperation and NSOperationQueue working thread vs main thread

Related

How to properly cancel and restart an URLSessionDataTask Swift

This is my request funtion:
func receiptValidation(completion: #escaping(_ isPurchaseSchemeActive: Bool, _ error: Error?) -> ()) {
let receiptFileURL = Bundle.main.appStoreReceiptURL
guard let receiptData = try? Data(contentsOf: receiptFileURL!) else {
//This is the First launch app VC pointer call
completion(false, nil)
return
}
let recieptString = receiptData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
let jsonDict: [String: AnyObject] = ["receipt-data" : recieptString as AnyObject, "password" : "7bb160f1c8ec4d929fbc751c507d24fd" as AnyObject]
do {
let requestData = try JSONSerialization.data(withJSONObject: jsonDict, options: JSONSerialization.WritingOptions.prettyPrinted)
let storeURL = URL(string: self.verifyReceiptURL)!
var storeRequest = URLRequest(url: storeURL)
storeRequest.httpMethod = "POST"
storeRequest.httpBody = requestData
let session = URLSession(configuration: URLSessionConfiguration.default)
let task = session.dataTask(with: storeRequest, completionHandler: { [weak self] (data, response, error) in
do {
if let jsonResponse = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary {
print("json response \(jsonResponse)")
if let expiresDate = self?.getPurchaseAndExpirationDateFromResponse(jsonResponse, keyString: "expires_date") {
//print("expiresDate \(expiresDate)")
let purchaseStatus = self?.isSubscriptionActive(expireDate: expiresDate)
if let purchaseStatus = purchaseStatus {
completion(purchaseStatus, nil)
}
}
}
} catch let parseError {
completion(false, parseError)
}
})
task.resume()
} catch let parseError {
completion(false, parseError)
}
}
This is how I am calling it:
func callForVal() {
receiptValidation() { isPurchaseSchemeActive, error in
if let err = error {
self.onBuyProductHandler?(.failure(err))
} else {
self.onBuyProductHandler?(.success(isPurchaseSchemeActive))
}
}
}
But sometimes It takes a long time to give a response back. Now I want to call it with a 60 seconds timer If do not get any response within these 60 seconds. How can I do it?

Decode multiple images from URL in Swift 4

I have an API endpoint that outputs 2 image URLs. I have the code below that decodes and parses through one of the image URLs, but I require to get and parse through both of the images.
How do I modify the code below so that the final return is 2 images
import Foundation
import SwiftUI
enum ApodImageResponse {
case Success(image: UIImage, title: String)
case Failure
}
struct ApodApiResponse: Decodable {
var url: String
var title: String
}
class ApodImageProvider {
static func getImageFromApi(completion: ((ApodImageResponse) -> Void)?) {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
formatter.timeZone = TimeZone(abbreviation: "UTC")
let date = Date()
let urlString = "https://api.nasa.gov/planetary/apod?api_key=eaRYg7fgTemadUv1bQawGRqCWBgktMjolYwiRrHK&date=\(formatter.string(from: date))"
let url = URL(string: urlString)!
let urlRequest = URLRequest(url: url)
let task = URLSession.shared.dataTask(with: urlRequest) { data, urlResponse, error in
parseResponseAndGetImage(data: data, urlResponse: urlResponse, error: error, completion: completion)
}
task.resume()
}
static func parseResponseAndGetImage(data: Data?, urlResponse: URLResponse?, error: Error?, completion: ((ApodImageResponse) -> Void)?) {
guard error == nil, let content = data else {
print("error getting data from API")
let response = ApodImageResponse.Failure
completion?(response)
return
}
var apodApiResponse: ApodApiResponse
do {
apodApiResponse = try JSONDecoder().decode(ApodApiResponse.self, from: content)
} catch {
print("error parsing URL from data")
let response = ApodImageResponse.Failure
completion?(response)
return
}
let url = URL(string: apodApiResponse.url)!
let urlRequest = URLRequest(url: url)
let task = URLSession.shared.dataTask(with: urlRequest) { data, urlResponse, error in
parseImageFromResponse(data: data, urlResponse: urlResponse, error: error, apodApiResponse: apodApiResponse, completion: completion)
}
task.resume()
}
static func parseImageFromResponse(data: Data?, urlResponse: URLResponse?, error: Error?, apodApiResponse: ApodApiResponse, completion: ((ApodImageResponse) -> Void)?) {
guard error == nil, let content = data else {
print("error getting image data")
let response = ApodImageResponse.Failure
completion?(response)
return
}
let image = UIImage(data: content)!
let response = ApodImageResponse.Success(image: image, title: apodApiResponse.title)
completion?(response)
}
}

Thread 1: EXC_BREAKPOINT (code=1, subcode=0x10136bb50) - swift

I got this error when I want to send value with GET method:
Thread 1: EXC_BREAKPOINT (code=1, subcode=0x10136bb50)
To get values:
var flname = self.txt_field_flname.text
var job_title = self.txt_field_job_title.text
var mobile = self.txt_field_mobile.text
var des = self.txt_field_des.text
var lat = self.lat
var lon = self.lon
self.sendNewJob(fname: flname!, title: job_title!, mobile: mobile!, des: des!, lat: String(lat), lon: String(lon) )
func sendNewJob(fname:String,title:String,mobile:String,des:String,
lat:String,lon:String)
{
print("fname \(fname) title \(title) mobile \(mobile) des \(des) lat \(lat) lon \(lon)") //output is well
RestApiManager.sharedInstance.sendNewJob(fname: fname,title: title,mobile:mobile,
des:des,lat:lat,lon:lon) { (json: JSON) in
}
}
func sendNewJob(fname:String,title:String,mobile:String,des:String,
lat:String,lon:String,onCompletion: #escaping (JSON) -> Void) {
let route = baseURL+"up=1&Name=\(fname)&BusinessName=\(title)&MobileNumber=\(mobile)&latitude=\(lat)&longitude=\(lon)&Description=\(des)"
makeHTTPGetRequest(path: route, onCompletion: { json, err in
onCompletion(json as JSON)
})
}
// MARK: Perform a GET Request
private func makeHTTPGetRequest(path: String, onCompletion: #escaping ServiceResponse) {
let request = NSMutableURLRequest(url: NSURL(string: path)! as URL) // line of my error
let session = URLSession.shared
let task = session.dataTask(with: request as URLRequest, completionHandler: {data, response, error -> Void in
if let jsonData = data {
let json:JSON = JSON(data: jsonData)
onCompletion(json, error as NSError?)
} else {
onCompletion(nil, error as NSError?)
}
})
task.resume()
}
This happens when the code executes a nil value. Here the code NSURL(string: path)! value might be nil. You can use optional binding (if let) to check whether the NSURL is a valid one. It happens when the string is not valid and does not make a valid URL.
You can use like this :
private func makeHTTPGetRequest(path: String, onCompletion: #escaping ServiceResponse) {
if let encodedPath = path.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
if let url = URL(string: encodedPath) {
let request = URLRequest(url: url)
let session = URLSession.shared
let task = session.dataTask(with: request as URLRequest, completionHandler: {data, response, error -> Void in
if let jsonData = data {
let json:JSON = JSON(data: jsonData)
onCompletion(json, error as NSError?)
} else {
onCompletion(nil, error as NSError?)
}
})
task.resume()
} else {
print("url is nil")
onCompletion(nil)
}
} else {
print("unable to encode url")
onCompletion(nil)
}
}

Callback syntax in swift 3

I am trying to create a callback on swift 3 but haven't had any luck so far. I was taking a look at this question: link which is similar, but the answer gives me an error.
Basically I have an API struct with a static function that I need to have a callback.
import UIKit
struct API {
public static func functionWithCallback(params: Dictionary<String, String>, success: #escaping ((_ response: String) -> Ticket), failure: #escaping((_ error:String) -> String) ) {
let app_server_url = "http://api.com" + params["key"]!
let url: URL = URL(string: app_server_url)!
var request: URLRequest = URLRequest(url: url)
request.httpMethod = "POST"
do {
request.httpBody = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
}
request.addValue("application/json charset=utf-8", forHTTPHeaderField: "Content-Type")
request.addValue("application/json charset=utf-8", forHTTPHeaderField: "Accept")
let session = URLSession.shared
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard error == nil else {
return
}
guard let data = data else {
return
}
DispatchQueue.main.async {
do {
let json = try JSONSerialization.jsonObject(with: data) as! [String: Any]
print(json)
var message = ""
if let result = json["result"] as? String {
if(result == "success") {
//attempt to call callback gives me an error: extra argument in call
success("") {
let ticket = json["ticket"] as! NSDictionary
var date = ticket["date"] as! String
var ticket: Ticket = nil
ticket.setDate(date: date)
return ticket
}
}
else {
message = json["message"] as! String
print(message)
}
} catch let error {
print(error.localizedDescription)
let description = error.localizedDescription
if let data = description.data(using: .utf8) {
do {
let jsonError = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
let message = jsonError?["message"] as! String
} catch {
}
}
}
}
})
task.resume()
}
}
So I basically can't call the callback success because it gives me an error: Extra argument in call. Any idea on how to fix it?
My goal is to call:
API.functionWithCallback(params: params, success() -> Ticket {
//do something with the returned ticket here
},
error() -> () {
//do something with the error message here
}
)
I believe you have it wrong on how to use call back closures, from what I can understand of your question you want to do something with the ticket in the call back closure and to do that it should be a parameter of the closure not the return type of the closure.
Replace your function declaration with this:
public static func functionWithCallback(params: Dictionary<String, String>, success: #escaping ((_ response: String, _ ticket: Ticket) -> Void), failure: #escaping((_ error:String) -> Void) ) {
And inside the function replace this:
success("") {
let ticket = json["ticket"] as! NSDictionary
var date = ticket["date"] as! String
var ticket: Ticket = nil // Im not sure what you are trying to do with this line but this will definitely give an error
ticket.setDate(date: date)
return ticket
}
With:
let ticket = json["ticket"] as! NSDictionary
var date = ticket["date"] as! String
var ticket: Ticket = nil // fix this line
ticket.setDate(date: date)
success("",ticket)
And then you can call the function like this:
API.functionWithCallback(params: params, success: { response, ticket in
// you can use ticket here
// and also the response text
}) { errorMessage in
// use the error message here
}
Try this :
func uploadImage(api: String,token : String, methodType : String, requestDictionary: [String:AnyObject],picData:[Data], successHandler: #escaping (AnyObject) -> Void,failureHandler: #escaping (NSError) -> Void)
{
if Common_Methods.Reachability1.isConnectedToNetwork() == false
{
let del :AppDelegate = (UIApplication.shared.delegate as? AppDelegate)!
let nav : UINavigationController = (del.window?.rootViewController as? UINavigationController)!
let alert = UIAlertController(title: "", message: "The Internet connection appears to be offline" , preferredStyle: UIAlertControllerStyle.alert)
// Create the actions
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default)
{
UIAlertAction in
}
alert.addAction(okAction)
nav.present( alert, animated: true, completion: nil)
return
}
let apiUrl = "\(KbaseUrl)\(api)"
let session = URLSession.shared
let url: NSURL = NSURL(string: apiUrl as String)!
print(url)
let request = NSMutableURLRequest(url: url as URL)
request.httpMethod = methodType
let boundary = NSString(format: "---------------------------14737809831466499882746641449") as String
//-------- add token as perameter and set a check if token not nill then set token in header -------
if(token.characters.count > 0)
{
request.setValue(token, forHTTPHeaderField: "x-logintoken")
}
request.setValue("Keep-Alive", forHTTPHeaderField: "Connection")
request.setValue("multipart/form-data; boundary="+boundary, forHTTPHeaderField: "Content-Type")
let data = createBodyWithParameters(parameters: requestDictionary, filePathKey:nil, imageDataKey: picData.count > 0 ? picData : [], boundary: boundary)
print(data)
request.httpBody = data
let task = session.dataTask(with: request as URLRequest) { data, response, error in
// handle fundamental network errors (e.g. no connectivity)
guard error == nil && data != nil else {
successHandler(data as AnyObject )//completion(data as AnyObject?, error as NSError?)
print(error)
DispatchQueue.main.async {
Common_Methods.hideHUD(view: (topVC?.view)!)
}
return
}
// check that http status code was 200
if let httpResponse = response as? HTTPURLResponse , httpResponse.statusCode != 200 {
do {
let responseObject = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary
if let responseDictionary = responseObject as? [String:AnyObject]
{
if responseDictionary["statusCode"] as! Int == 401
{
// self.objDelegate.sessionExpire(msgStr: "Session Expired. Please login again to continue.")
}
else
{
//completion(String(data: data!, encoding: String.Encoding.utf8) as AnyObject?, nil)
}
}
} catch let error as NSError {
print(error)
DispatchQueue.main.async {
Common_Methods.hideHUD(view: (topVC?.view)!)
}
// completion(String(data: data!, encoding: String.Encoding.utf8) as AnyObject?, nil)
}
}
// parse the JSON response
do {
DispatchQueue.main.async {
Common_Methods.hideHUD(view: (topVC?.view)!)
}
let responseObject = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary
successHandler(responseObject! )
} catch let error as NSError {
DispatchQueue.main.async {
Common_Methods.hideHUD(view: (topVC?.view)!)
}
// completion(String(data: data!, encoding: String.Encoding.utf8) as AnyObject?, error)
failureHandler(error)
}
}
task.resume()
// return task
}
and function Call is :
WebService.sharedInstance.uploadImage(api: KEditEmployerProfile,token: token,methodType: "PUT", requestDictionary: parameters1 as! [String : AnyObject], picData: [imageData as Data], successHandler: { (responseObject) in
print(responseObject)
}) { (error) in
print(error)
}
}

NSURLSession Response String completion block - Swift

I want to wait for a responseString to complete before calling the next function "nextScreen()" (segue). At the moment I have an if statement to make sure it is not nil before proceeding, but sometimes the the next function/segue is called because the responseString is still downloading.
Could you help with a completion block? I have found completion blocks for NSURLSession, but these just wait for the initial HTTP call to complete, not the response string.
func getProfiles(){
func post(completion: (message: String?) -> Void) {
let request = NSMutableURLRequest(URL: NSURL(string: "http://**.**.**.**/EPG/XML/QueryProfile")!)
request.HTTPMethod = "POST"
let postString = "<QueryProfileReq><type>1</type></QueryProfileReq>"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task: Void = NSURLSession.sharedSession().dataTaskWithRequest(request,
completionHandler: {(data: NSData!,
response: NSURLResponse!,
error: NSError!) in
if error != nil {
println("error=\(error)")
let alert = UIAlertView()
alert.delegate = self
alert.title = "Login Error"
alert.message = "\(error)"
alert.addButtonWithTitle("OK")
alert.show()
self.view.endEditing(true)
return
}
if let responseString = NSString(data: data, encoding: NSUTF8StringEncoding) {
if response != nil {
println("got profiles")
self.nextScreen()
}
self.dataVar = data // UPDATES VARIABLE TO SEND
}
}).resume()
}
}
The convenience method of dataTaskWithRequest essentially returns data or error, with usually some response header type information. If you have an error then you won't have data (99% sure about this). I have re formatted your method to help. The NSString Init Convenience method is synchronous so not quite sure by what you mean by waiting to complete instead of http call?
func getStringFromRequest(completionHandler:(success:Bool, data: NSData?) -> Void) {
let request = NSMutableURLRequest(URL: NSURL(string: "http://##.##.##.##/EPG/XML/QueryProfile")!)
request.HTTPMethod = "POST"
let postString = "<QueryProfileReq><type>1</type></QueryProfileReq>"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { (data, response, error) -> Void in
if let unwrappedError = error {
print("error=\(unwrappedError)")
}
else {
if let unwrappedData = data {
completionHandler(success: true, data: unwrappedData)
return
}
}
completionHandler(success: false, data: nil)
}
task?.resume()
}
func performPost() {
getStringFromRequest { (success, data) -> Void in
if (success) {
if let unwrappedData = data {
self.dataVar = unwrappedData
if let responseString = NSString(data: unwrappedData, encoding: NSUTF8StringEncoding) {
self.nextScreen()
}
}
}
else {
print("Failed")
}
}
}