NSURL completion event is not being called - ios

I'm currently starting to learn swift.
My first "test-app" is a simple application, which does a NSURL Session request on startup and displays the received data in a tableview.
So far, my tableView does work and also my request method runs without any compilation errors.
My problem now is, that the event handler is not being called:
func doRequest(url: String) -> String
{
var url = NSURL(string: url);
var request = NSURLRequest(URL: url!);
var config = NSURLSessionConfiguration.defaultSessionConfiguration();
var session = NSURLSession(configuration: config);
var task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
println("CALLED");
});
return "";
}
The above method doesn't print anything into the console.
Since using google did not help me to find a solution, I try my luck here.
What's the issue with my code? Am I doing something wrong without realising it?
The URL for my tests is: api.football-data.org/alpha/soccerseasons/354/leagueTable
which returns a simple json object.
Any help is appreciated!

Add task.resume() to start the download.
After you create the task, you must start it by calling its resume method.
var task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
println("CALLED");
});
task.resume()

Related

What is the better way to call multiple services in iOS?

I have 5 different services requests to load into same UItableView for each cells.
What is the best way to approach in order to do this.
https://example.com/?api/service1
https://example.com/?api/service2
https://example.com/?api/service3
https://example.com/?api/service4
https://example.com/?api/service5
let url = "https://example.com/?api/service1
Alamofire.request(url, method: .get, parameters:nil encoding: JSONEncoding.default, headers: nil)
.responseJSON { response in
print(response.result.value as Any) // result of response serialization
}
repeat the same Alamofire five times with different services name there is another way to implement it.
Look at using a DispatchGroup to perform multiple async requests and wait for them all to complete.
For each task you call group.enter() and in its completion handler when you know that request has finished you call group.leave(). Then there is a notify method which will wait for all requests to call leave to tell you that they have all finished.
I have created an example in a Playground (which will fail with errors because of the URL's used)
import UIKit
import PlaygroundSupport
let serviceLinks = [
"https://example.com/?api/service1",
"https://example.com/?api/service2",
"https://example.com/?api/service3",
"https://example.com/?api/service4",
"https://example.com/?api/service5"
]
// utility as I've not got alamofire setup
func get(to urlString: String, completion: #escaping (Data?, URLResponse?, Error?) -> Void) {
let url = URL(string: urlString)!
let session = URLSession.shared
let task = session.dataTask(with: url) { data, response, error in
completion(data, response, error)
}
task.resume()
}
let group = DispatchGroup()
for link in serviceLinks {
group.enter() // add an item to the list
get(to: link) { data, response, error in
// handle the response, process data, assign to property
print(data, response, error)
group.leave() // tell the group your finished with this one
}
}
group.notify(queue: .main) {
//all requests are done, data should be set
print("all done")
}
PlaygroundPage.current.needsIndefiniteExecution = true
You probably won't be able to just loop through the URL's like I have though because the handling of each service is probably different. You'll need to tweak it based on your needs.
There is alot more information about DispatchGroups available online such as this article

swift 3+, URLsession, in the background fails apparently at random

I am fairly new to swift(1 week) and iOS programming, and my problem is that I seem to miss some basic understanding. Below you see a function that is triggered by a background notification. I can and have verified that I receive the background notification reliably and the app comes active (printout of the raw data values on the console) As long as the app is in the foreground everything is working just as expected, it gets fired, and sends a single https request. The background triggers come on a timer every minute.
Now the whole thing changes when the app enters into the background. In this case I am still getting the triggers through the notification (console printout) and I can see in the debugger the same function that works like a charm in the foreground stumbles. It still works, it still gets fired, but a data packet is sent only so often, randomly as it seems between 2 and 30 minutes.
let config = URLSessionConfiguration.background(withIdentifier: "org.x.Reporter")
class queryService {
let defaultSession = URLSession(configuration: config)
var dataTask: URLSessionDataTask?
var errorMessage = ""
func getSearchResults(baseURL: String, searchTerm: String) {
dataTask?.cancel()
config.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheData;
config.timeoutIntervalForRequest = 10
if var urlComponents = URLComponents(string: "https://host.com/reportPosition.php") {
urlComponents.query = "\(searchTerm)"
guard let url = urlComponents.url else { return }
dataTask = defaultSession.dataTask(with: url)
}
// 7
dataTask?.resume()
}
}
Try using dataTaskWithCompletion so you can see what's going wrong in the error.
URLSession.shared.dataTask(with: URL.init(string: "")!) { (data, response, error) in
if error != nil {
// Error
}
}.resume()
https://developer.apple.com/documentation/foundation/urlsession/1410330-datatask
EDIT
What you want to do is for background you get completions via delegate call backs so when you init ur URLSession do so using the following func
URLSession.init(configuration: URLSessionConfiguration.init(), delegate: self, delegateQueue: OperationQueue.init())
https://developer.apple.com/documentation/foundation/urlsession/1411597-init
Then conform ur class to the URLSessionDelegate like so
class queryService, URLSessionDelegate {
then implement the delegate methods listed here for call backs
https://developer.apple.com/documentation/foundation/urlsessiondelegate
EDIT2
Here is good tutorial about it
https://www.raywenderlich.com/158106/urlsession-tutorial-getting-started

Completing webservice in background state iOS

Is there any possible way to complete a webservice task when ios application goes to background state using URLSession ?
I am not trying to upload a huge sized file to the server , instead i am trying to upload a 4 digit key.
i have tried the below method for background task.
var bgTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {})
For testing purpose , i am calling webservice in a loop and logged. when i go to background mode the webservice calls are getting stuck.
Big upload/download files
Introduced in iOS 7.
It really depends on how much the task will take time. If it's a big download or upload task (for example downloading a video) you should execute the task using NSURLSession with background configuration. The iOS system will handle the upload/download task. Once the download completes, the downloaded file will be save into a temporary file (so you might need to copy it to another place later on). Downloading in background and waking app when finished
Normal WebServices (I think that this is what you want)
Introduced in iOS 6
For finite length operation, you can use beginBackgroundTaskWithExpirationHandler.
var task = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler(){}
//do your job (call the service)
//on completion you shouldcall the bellow two lines
UIApplication.sharedApplication().endBackgroundTask(task)
task = UIBackgroundTaskInvalid
No need to have configure an NSURLSession with a background configuration.
class func generalPost(url: String, postCompleted : (response: String) -> ()) {
let request = NSMutableURLRequest(URL: NSURL(string: url)!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
let dataString = ""
request.HTTPBody = dataString.dataUsingEncoding(NSUTF8StringEncoding)
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
let strData = NSString(data: data!, encoding: NSUTF8StringEncoding)
postCompleted(response: strData)
})
task.resume()
}
And you can use
generalPost(url: "http://stackoverflow.com/") { (response) in
dispatch_async(dispatch_get_main_queue(), { () -> Void in
UIAlertView(title: "Message", message: response, delegate: nil, cancelButtonTitle: "Ok").show()
})
}

NSURLSession Show Tasks In Suspended State?

I know that invoking getTasksWithCompletionHandler on an NSURLSession:
Asynchronously calls a completion callback with all outstanding data,
upload, and download tasks in a session.
However, from experimentation, I can see the data, upload, and download task arrays returned by the method;
func getTasksWithCompletionHandler(_ completionHandler: ([NSURLSessionDataTask], [NSURLSessionUploadTask], [NSURLSessionDownloadTask]) -> Void)
are in fact only show executing tasks. I.e., tasks which have received resume().
This can be seen by copy pasting this code in a Playground:
import UIKit
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let url = NSURL(string: "http://jsonplaceholder.typicode.com")
let request: NSURLRequest = NSURLRequest(URL: url!)
let task : NSURLSessionDataTask = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
NSLog("Done")
})
session.getAllTasksWithCompletionHandler { (tasks:[NSURLSessionTask]) -> Void in
NSLog("\(tasks)")
XCPlaygroundPage.currentPage.finishExecution()
}
//task.resume()
Note that getTasksWithCompletionHandler and getAllTasksWithCompletionHandler - the latter released in iOS 9 - seem to only differ in the signature of the completion closure.
Also, somewhat tantalisingly, NSURLSessionTask has a state property NSURLSessionTaskState that gives us Running, Suspended, Canceling, Completed.
Is there a way to access the list of non-executing tasks; those that have been aded to the NSURLSession yet have not resumed?

Failure to return to calling function from NSURLSession delegate without killing the task

I'm using Swift in Xcode 6.2 (beta) but had the same problem on the 6.1 release version. I'm trying to use NSURLSession and believe I have it set up correctly (see code below). The problem is that I have a delegate setup to deal with a redirect happening through the code. I actually need to capture the cookies prior to the final redirection and I'm doing this through the delegate:
func URLSession(_:NSURLSession, task:NSURLSessionTask, willPerformHTTPRedirection:NSHTTPURLResponse, newRequest:NSURLRequest, completionHandler:(NSURLRequest!) -> Void )
This works and I'm able to execute code successfully and capture the cookies I need. The problem is that I need to add task.cancel() at the end of the function or else it never seems to complete and return to the delegator (parent?) function. Because of this I lose the results from the redirect URL (although in my current project it is inconsequential). The strange thing is that this was working for a while and seemingly stopped. I don't believe I entered any code that changed it, but something had to happen. Below is the relevant code.
NSURLSession Function:
func callURL (a: String, b: String) -> Void {
// Define the URL
var url = NSURL(string: "https://mycorrecturl.com");
// Define the request object (via string)
var request = NSMutableURLRequest(URL: url!)
// Use default configuration
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
// Create the NSURLSession object with default configuration, and self as a delegate (so calls delegate method)
let session = NSURLSession(configuration: config, delegate: self, delegateQueue: nil)
// Change from default GET to POST (needed to call URL properly)
request.HTTPMethod = "POST"
// Construct my parameters to send in with the URL
var params = ["a":a, "b":b] as Dictionary<String, String>
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(params, options: nil, error: &err)
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
// Do some other stuff after delegate has returned...
})
task.resume()
return
}
The delegate code:
func URLSession(_:NSURLSession, task:NSURLSessionTask, willPerformHTTPRedirection:NSHTTPURLResponse, newRequest:NSURLRequest, completionHandler:(NSURLRequest!) -> Void ) {
// Check Cookies
let url = NSURL(string: "https://mycorrecturl.com")
var all = NSHTTPCookie.cookiesWithResponseHeaderFields(willPerformHTTPRedirection.allHeaderFields, forURL: url!)
// Get the correct cookie
for cookie:NSHTTPCookie in all as [NSHTTPCookie] {
if cookie.name as String == "important_cookie" {
NSHTTPCookieStorage.sharedHTTPCookieStorage().setCookie(cookie)
}
}
task.cancel()
}
It used to return to the calling function without calling task.cancel(). Is there anything that looks wrong with the code that would cause it to just hang in the delegate function if task.cancel() isn't called?
Edit: What code would I add to fix this.
If you are not canceling the request, your willPerformHTTPRedirection should call the completionHandler. As the documentation says, this completionHandler parameter is:
A block that your handler should call with either the value of the request parameter, a modified URL request object, or NULL to refuse the redirect and return the body of the redirect response.

Resources