Cancel NSURLSession dataTask when request is taking too long - ios

On certain websites the below method hangs indefinitely, I'd like to cancel the request when its taking too long, I thought that timeoutIntervalForRequest and timeoutIntervalForResource would control this but setting either of them doesn't seem to have any effect.
This is an example website that the request hangs indefinitely on: http://www.samtoft.co.uk/showpic.php?id=542&order=&search=#header
// fetch data from URL with NSURLSession
class func getDataFromServerWithSuccess(myURL: String, noRedirect: Bool, callback: Result<String> -> Void) {
var loadDataTask: NSURLSessionDataTask? = nil
let sessionConfig: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
sessionConfig.timeoutIntervalForRequest = 10.0
sessionConfig.timeoutIntervalForResource = 10.0
var myDelegate: MySession? = nil
if noRedirect {
myDelegate = MySession()
}
let session = NSURLSession(configuration: sessionConfig, delegate: myDelegate, delegateQueue: nil)
loadDataTask = session.dataTaskWithURL(NSURL(string: myURL)!) { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
if let checkedData = data {
let success = Result.Success(NSString(data: checkedData, encoding: NSASCIIStringEncoding) as! String)
callback(success)
} else {
let redirectError = NetworkError.FailedUrl("\(myURL) + \(error)")
if let request = loadDataTask?.currentRequest {
guard let urlExtension = request.URL?.pathExtension else {return}
guard let domain = request.URL?.host else {return}
guard let finalURLAsString = request.URL?.description else {return}
let failure = Result.Failure("\(finalURLAsString) + \(redirectError)")
callback(failure)
}
}
}
loadDataTask!.resume()
}
EDIT: for anyone who is having the same issue I was, the problem was that I was accounting for the success case and the error case, but not for when the request returned no response, so I added the below:
if response == nil {
print("NO RESPONSE \(myURL)")
let noResponse = Result.NoResponse("NO RESPONSE")
callback(noResponse)
}

I have extracted your code as
func getDataFromServerWithSuccess(myURL: String) {
var loadDataTask: NSURLSessionDataTask? = nil
let sessionConfig: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
//sessionConfig.timeoutIntervalForRequest = 11.0
sessionConfig.timeoutIntervalForResource = 11.0
let session = NSURLSession(configuration: sessionConfig)
loadDataTask = session.dataTaskWithURL(NSURL(string: myURL)!) { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
print("end")
}
loadDataTask!.resume()
}
It does work sensitively for the timeout. I suppose there may be something wrong with the delegator which cannot receive the response properly. Hope this helps.

Related

swift function return multiple values

Below is a function that I am trying to create that will get the values in JSON data based on the key value entered. The problem I am having is I need Need to be able to use all the rates in another calculation. I am not sure how to get each rate saved to a global variable. At this point, the function is giving me an error - "Missing Return in a function expecting to return a string". Any ideas how to solve this problem
func getLenderData(selectedLenderKey: String) -> String
{
let url = ""
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "GET"
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) { (data, response, error) in
if error != nil {
print("Error")
}
else{
do{
//let fetchedData = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as! NSArray
//print(fecthdata)
let swiftyJSON = try JSON(data:data!)
let lenderName = swiftyJSON["lenders"][selectedLenderKey]["financial_institution"].stringValue
let oneYear = swiftyJSON["lenders"][selectedLenderKey]["one_year"].stringValue
let twoYear = swiftyJSON["lenders"][selectedLenderKey]["two_year"].stringValue
let threeYear = swiftyJSON["lenders"][selectedLenderKey]["three_year"].stringValue
let fourYear = swiftyJSON["lenders"][selectedLenderKey]["four_year"].stringValue
let fiveYear = swiftyJSON["lenders"][selectedLenderKey]["five_year"].stringValue
print(lenderName)
print(oneYear)
print(twoYear)
print(threeYear)
print(fourYear)
print(fiveYear)
}
catch{
print("Error 2")
}
}
}
task.resume()
}// end function
You could create a model and return that model:
struct Rates {
let lenderName: String?
let oneYear: String?
let twoYear: String?
let threeYear: String?
let fourYear: String?
let fiveYear: String?
required init() {
lenderName = nil
oneYear = nil
twoYear = nil
threeYear = nil
fourYear = nil
fiveYear = nil
}
init(lenderName: String?, oneYear: String?, twoYear: String?, threeYear: String?, fourYear: String?, fiveYear: String?) {
self.lenderName = lenderName
self.oneYear = oneYear
self.twoYear = twoYear
self.threeYear = threeYear
self.fourYear = fourYear
self.fiveYear = fiveYear
}
}
Then return the model you created:
func getLenderData(selectedLenderKey: String, onCompletion: #escaping (Rates?) -> Void, onError: #escaping (NSError) -> Void) {
let url = ""
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "GET"
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) { (data, response, error) in
if error != nil {
print("Error")
}
else{
do{
//let fetchedData = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as! NSArray
//print(fecthdata)
let swiftyJSON = try JSON(data:data!)
let lenderName = swiftyJSON["lenders"][selectedLenderKey]["financial_institution"].stringValue
let oneYear = swiftyJSON["lenders"][selectedLenderKey]["one_year"].stringValue
let twoYear = swiftyJSON["lenders"][selectedLenderKey]["two_year"].stringValue
let threeYear = swiftyJSON["lenders"][selectedLenderKey]["three_year"].stringValue
let fourYear = swiftyJSON["lenders"][selectedLenderKey]["four_year"].stringValue
let fiveYear = swiftyJSON["lenders"][selectedLenderKey]["five_year"].stringValue
print(lenderName)
print(oneYear)
print(twoYear)
print(threeYear)
print(fourYear)
print(fiveYear)
let rate = Rates(lenderName: lenderName, oneYear: oneYear, twoYear: twoYear, threeYear: threeYear, fourYear: fourYear, fiveYear: fiveYear)
onCompletion(rate)
}
catch{
print("Error 2")
onError(NSError(domain: "Some error description...", code: 0, userInfo: nil))
}
}
}
task.resume()
}// end function
Now you can just use the return value as you want. To use it use the following code:
getLenderData(selectedLenderKey: "someVal", onCompletion: { (rate) in
// you can use the rate here
let oneYear = rate.oneYear
}, onError: { (error) in
// Error
})
You probably want to use Promise for this sort of implementation. Look into PromiseKit. Your code would become something like this
func getLenderData(selectedLenderKey: String) -> Promise<String> {
return Promise { fulfill, reject in
let url = ""
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "GET"
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) { (data, response, error) in
if error != nil {
print("Error")
reject({ERROR})
}
else{
do{
//let fetchedData = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as! NSArray
//print(fecthdata)
let swiftyJSON = try JSON(data:data!)
let lenderName = swiftyJSON["lenders"][selectedLenderKey]["financial_institution"].stringValue
let oneYear = swiftyJSON["lenders"][selectedLenderKey]["one_year"].stringValue
let twoYear = swiftyJSON["lenders"][selectedLenderKey]["two_year"].stringValue
let threeYear = swiftyJSON["lenders"][selectedLenderKey]["three_year"].stringValue
let fourYear = swiftyJSON["lenders"][selectedLenderKey]["four_year"].stringValue
let fiveYear = swiftyJSON["lenders"][selectedLenderKey]["five_year"].stringValue
print(lenderName)
print(oneYear)
print(twoYear)
print(threeYear)
print(fourYear)
print(fiveYear)
fulfill({STRING})
}
catch{
print("Error 2")
reject({ERROR})
}
}
}
task.resume()
}
}

SWIFT: Error EXC_BAD_INSTRUCTION

the following error appears after running my app and trying to register an user:
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_l386_INVOP,s subcode=0x0)
This appears at the end of the code:
} as! (Data?, URLResponse?, Error?) -> Void )
let myUrl = NSURL(string: "http://127.0.0.1/MySQL_PHP/userRegister.php")
var request = URLRequest(url: myUrl as! URL)
request.httpMethod = "POST"
// let config = URLSessionConfiguration.default
let session = URLSession.shared
let postString = "email=\(userEmail)&passwort=\(userPasswort)&vorname=\(userVorname)&nachname=\(userName)&benutzer=\(userBenutzer)"
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) in
if error != nil {
print("error=\(error)")
return
}
var err: NSError?
var json = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary
if let parseJSON = json {
var resultValue = parseJSON["status"] as? String
print("result: \(resultValue)")
var isUserRegistered: Bool = false
if (resultValue == "Success") {
isUserRegistered = true
}
var messageToDisplay: String = parseJSON["message"] as! String!
if (!isUserRegistered){
messageToDisplay = parseJSON["message"] as! String!
}
DispatchQueue.main.async(execute: {
var myAlert = UIAlertController(title:"Alert", message: messageToDisplay, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default){
action in
self.dismiss(animated: true, completion: nil)
}
myAlert.addAction(okAction)
self.present(myAlert, animated: true, completion: nil)
})
}
} as! (Data?, URLResponse?, Error?) -> Void )
task.resume()
The error is misleading. It's not directly related to the data task closure.
First of all do not cast at all
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
...
})
or even still shorter using trailing closure syntax:
let task = session.dataTask(with: request) { (data, response, error) in
...
}
The error occurs because the do - catch block is missing around try JSONSerialization
The error goes away either by adding the do - catch block or using try!
And finally a suggestion: Please, please, please do not use NSArray / NSDictionary in Swift. You are fighting the strong type system.

Swift - NSURLSession for Windows Authentication

I have this class here and inside the class is a method and I am trying to do an NSURLSession on an API that requires windows authentication username and password. I have followed the tutorial here https://gist.github.com/n8armstrong/5c5c828f1b82b0315e24
and came up with this:
let webservice = "https://api.com"
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let urlSession = NSURLSession(configuration: config)
class WebService: NSObject {
func loginUser(username: String, password: String) -> Bool {
let userPasswordString = "username#domain.com:Password"
let userPasswordData = userPasswordString.dataUsingEncoding(NSUTF8StringEncoding)
let base64EncodedCredential = userPasswordData!.base64EncodedStringWithOptions([])
let authString = "Basic \(base64EncodedCredential)"
config.HTTPAdditionalHeaders = ["Authorization" : authString]
let requestString = NSString(format:"%#", webservice) as String
let url: NSURL! = NSURL(string: requestString)
let task = urlSession.dataTaskWithURL(url) {
(let data, let response, let error) in
if (response as? NSHTTPURLResponse) != nil {
let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print(dataString)
}
}
task.resume()
return true
}
}
but when I run this I get a 401 error: 401 - Unauthorized: Access is denied due to invalid credentials.
I have confirmed the URL to the API is correct. Same with the username and password. What am I doing wrong?
I was able to fix this by doing the following:
var credential: NSURLCredential!
func loginUser(username: String, password: String) -> Bool {
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)
credential = NSURLCredential(user:username, password:password, persistence: .ForSession)
let requestString = NSString(format:"%#", webservice) as String
let url: NSURL! = NSURL(string: requestString)
let task = session.dataTaskWithURL(url, completionHandler: {
data, response, error in
dispatch_async(dispatch_get_main_queue(),
{
if(error == nil)
{
print("Yay!")
}
else
{
print("Naw!")
}
})
})
task.resume()
return true
}
and then adding NSURLSessionDelegate methods:
func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
if challenge.previousFailureCount > 0
{
completionHandler(NSURLSessionAuthChallengeDisposition.CancelAuthenticationChallenge, nil)
}
else
{
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust:challenge.protectionSpace.serverTrust!))
}
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential,credential)
}

How to run through a get file redirect in swift

I am using swift to access the youtubeinmp3 api and I am stuck. I am quite new to this so please be gentle. Using the api I get a json response and use the link property that gets returned to download a file. However the link I get looks like this : http://www.youtubeinmp3.com/download/get/?i=PL6sPTHlt1KHYj6hUsDxW8zjAgcNiU1SXVHIzxnSALX8%2FKNV35SZqd9l5qxk7LOiD%2FcrlIUe5JJvgZxKg0WeMw%3D%3D
The link works fine in a browser like Chrome but the swift app downloads an unknown file, definitely not an mp3.
I used this question to find the code.
Any ideas on how I could go through the redirect to get to the mp3 download link?
Thank you in advance.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let videoURL1 = (videosArray[indexPath.row]).objectForKey("videoID") as! String
//let videoURL = "https://www.youtube.com/watch?v=\(videoURL1)"
let videoURL = "//www.youtubeinmp3.com/fetch/?video=https://www.youtube.com/watch?v=\(videoURL1)"
let urlString = "http://www.youtubeinmp3.com/fetch/?format=JSON&video=\(videoURL)"
let url = NSURL(string: "http://www.youtubeinmp3.com/fetch/?format=JSON&video=\(videoURL)")
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 {
print("response=\(response)")
if response.statusCode == 200 {
if data != nil {
do {
let responseJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.AllowFragments) as! NSDictionary;
//let urlString = NSURL(string:"\(responseJSON["link"] as! String)")
//print("URLString: \(urlString)")
//let directDownloadURL = NSURL(string: urlString)
// Call your method loadFileAsync
// code here
let urlString = responseJSON["link"] as! String
//let finalURLString = urlString.stringByReplacingOccurrencesOfString("get", withString: "mp3")
print(urlString)
dispatch_async(dispatch_get_main_queue(), {
let identifier = NSUUID().UUIDString
let downloadItem = DownloadsTableViewCellItem(identifier: identifier, urlString: urlString, infoLabelTitle: selectedName, stateLabelTitle: "Press Download", progressLabelTitle: "", action: DownloadsTableViewCellAction.Download)
DownloadsViewController().addDownloadItem(downloadItem, withIdentifier: identifier)
SVProgressHUD.showSuccessWithStatus("Download Added")
print("Download Task Added")
})
}
catch let JSONError as NSError {
print("\(JSONError)")
}
catch {
print("unknown error in JSON Parsing");
}
}
}
}
}
else {
print("Failure: \(error!.localizedDescription)");
}
})
task.resume()
}

Random error on dataTaskWithRequest completion handler?

I'm working on a passbook capable swift application and i'm in front of a strange problem.
Randomly, the passbook i download from my server is corrupted (the same downloaded can be good or corrupted).
Here is the code :
func openPass(pass: PKPass)
{
let passname = "Benight Ticket"
var passcontroller = PKAddPassesViewController(pass: pass)
passcontroller.delegate = self
self.presentViewController(passcontroller, animated: true, completion: nil)
}
func getTicketPassbook()
{
let TicketID: String = "2gBOZqWwNj"
let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil)
let request = NSMutableURLRequest(URL: NSURL(string: "https://exemple.com/index.php")!)
request.HTTPMethod = "POST"
let postString: String = "ObjectId=" + TicketID + "&authKey=\"exemple\""
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = session.dataTaskWithRequest(request, completionHandler: { (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in
println("response = \(response)")
if (error == nil) {
// Success
let statusCode = (response as! NSHTTPURLResponse).statusCode
if (statusCode == 200)
{
println("Success: \(statusCode)")
var pkfile : NSData = NSData(data: data)
var pass: PKPass = PKPass(data: pkfile, error: nil)
self.openPass(pass)
}
// This is your file-variable:
// data
}
else {
// Failure
println("Faulure: %#", error.localizedDescription);
}
})
task.resume()
}
The error is the following : 2015-08-22 16:18:28.328 Application[10302:1018965] BOM could not extract archive: Couldn't read PKZip signature
And other times it works.... It's really at random times that it works or fail...
EDIT : Sometis i've also the following error : 2015-08-23 01:19:19.483 Benight[3467:994898] Invalid data error reading pass pass.com.apple.demo/8j23fm3. The passTypeIdentifier or teamIdentifier provided may not match your certificate, or the certificate trust chain could not be verified.
fatal error: unexpectedly found nil while unwrapping an Optional value

Resources