NSURLSessionDownloadTask API call clarification on syntax in Swift - ios

I'm learning to do a basic networking call, and following a tutorial. The tutorial goes through concurrency, and downloading the JSON data in the background thread. I was wondering why in the sharedSession.downloadTaskWithURL method the queryURL would be passed as the NSURL object as opposed to the baseURL. I feel like I'm missing something pretty obvious!
func searchRecipeData() {
let baseURL = NSURL(string: "http://api.recipes.com/v1/api/recipes?_app_id=\(apiID)&_app_key=\(apiKey)")
let queryURL = NSURL(string: "&q=onion+soup", relativeToURL: baseURL)!
let sharedSession = NSURLSession.sharedSession()
let downloadData: NSURLSessionDownloadTask = sharedSession.downloadTaskWithURL(queryURL, completionHandler: { (location: NSURL!, response: NSURLResponse!, error: NSError!) -> Void in
if (error == nil) {
let data = NSData(contentsOfURL: baseURL!)
println(data)
}
})
// Resumes it even though it hasn't started yet
downloadData.resume()
}

Take a look at the second parameter where you create queryURL. You are passing the baseURL constant. What happens is that the '&q=onion+soup' query parameter is told to be relative to the baseURL. The queryURL constant is the full URL, and it is then passed to downloadTaskWithURL.

Related

what am I doing wrong Swift 3 http request and response

I'm having big problems in a project I'm currently working with.
I've been reading about URLSession various places but all of them seem to be outdated and refers to NSURLSession I thought that they would be fairly similar and they probably are but for a newbie like me I'm lost. what I do is not working and I do not like solutions I find because they all do their work in a controller..
http://swiftdeveloperblog.com/image-upload-with-progress-bar-example-in-swift/
this one for instance. I'm using the PHP script but wanted to make a networking layer I could invoke and use at will. but I'm lacking a good resource from where I could learn about how to use this api.
every place I find is similar to the link above or older. the few newer seem to also follow the pattern without really explaining how to use this api.
at the same time I'm new to the delegate pattern in fact I only know that it is something that is heavily used in this Api but I have no IDEA how or why.
Basically I need help finding my way to solve this problem here:
I've tried to do something like this:
public class NetworkPostRequestor:NSObject,NetworkPostRequestingProtocol,URLSessionTaskDelegate,URLSessionDataDelegate
{
public var _response:HTTPURLResponse
public override init()
{
_response = HTTPURLResponse()
}
public func post(data: Data, url: URL)
{
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Keep-Alive", forHTTPHeaderField: "Connection")
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration,delegate: self, delegateQueue: OperationQueue.main)
let task = session.uploadTask(with: request, from: data)
task.resume()
}
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: #escaping (URLSession.ResponseDisposition) -> Void)
{
_response = response as! HTTPURLResponse
}
}
however I never even hit the PHPserver. the server when hit will say something like this in the terminal:
[Tue Mar 7 11:43:20 2017] 192.168.250.100:64265 [200]: /
[Tue Mar 7 11:43:20 2017] 192.168.250.100:64266 [404]: /favicon.ico - No such file or directory
Well that is when I hit it with my browser and there is no image with it. but alt least I know that it will write something with the terminal if it hits it. Nothing happens And without a resource to teach me this api I'm afraid I will never learn how to fix this or even if I'm doing something completely wrong.
I'm using Swift 3 and Xcode 8.2.1
Edit:
I've added this method to the class and found that I hit it every single time.
public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)
{
_error = error.debugDescription
}
the debug description have this string "some"
I never used this exact procedure with tasks but rather use the methods with callback. I am not sure if in the background there should be much of a difference though.
So to generate the session (seems pretty close to your):
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
Then I generate the request which stupidly enough needs an URL in the constructor:
var request = URLRequest(url: URL(string: "www.nil.com")!) // can't initialize without url
request.url = nil
Adding url with query parameters (you can just set the URL in your case, I have a tool to handle a few cases):
fileprivate func injectQueryParameters(request: inout URLRequest) {
if let query = queryParameters.urlEncodedString {
let toReturn = endpoint.url + "?" + query
if let url = URL(string: toReturn) {
request.url = url
} else {
print("Cannot prepare url: \(toReturn)")
}
} else {
let toReturn = endpoint.url
if let url = URL(string: toReturn) {
request.url = url
} else {
print("Cannot prepare url: \(toReturn)")
}
}
}
Then the form parameters. We mostly use JSON but anything goes here:
fileprivate func injectFormParameters( request: inout URLRequest) {
if let data = rawFormData {
request.httpBody = data
} else if let data = formParameters.urlEncodedString?.data(using: .utf8) {
request.httpBody = data
}
}
And the headers:
fileprivate func injectHeaders(request: inout URLRequest) {
headers._parameters.forEach { (key, value) in
if let stringValue = value as? String {
request.setValue(stringValue, forHTTPHeaderField: key)
}
}
}
So in the end the whole call looks something like:
class func performRequest(request: URLRequest, callback: (([String: Any]?, NSError?) -> Void)?) {
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) { data, response, error in
// Response is sent here
if let data = data {
callback?((try? JSONSerialization.jsonObject(with: data, options: .allowFragments)) as [String: Any]?, error)
} else {
callback?(nil, error)
}
}
task.resume()
}
I hope this puts you on the right track. In general you do have a few open source libraries you might be interested in. Alamofire is probably still used in most cases.

Why does NSHTTPURLResponse return nil? NSURLSession, Swift 2.2

I'm trying to write a function fetches JSON from a service on the web but one of the lines (commented) keeps returning nil and I have no idea why. Any help is appreciated! :) Thanks!!
func parseJSON(long: CLLocationDegrees, lat: CLLocationDegrees) {
var longitude : String = "\(long)"
var latitude : String = "\(lat)"
longitude = longitude.substringToIndex(longitude.characters.indexOf(".")!)
latitude = latitude.substringToIndex(latitude.characters.indexOf(".")!)
print("\(latitude),\(longitude)")
let appId = "xyz" //Insert API Key
let urlString = "https://api.openweathermap.org/data/2.5/weather?lat=\(latitude)&lon=\(longitude)&units=metric&appid=\(appId)"
let requestURL: NSURL = NSURL(string: urlString)!
let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(urlRequest) {
(data, response, error) -> Void in
//Keeps returning nil. No idea why.
if let httpResponse = response as? NSHTTPURLResponse {
print("Success!")
let statusCode = httpResponse.statusCode
if (statusCode == 200) {
print("Everyone is fine, file downloaded successfully.")
} else {
print("STATUSCODE=\(statusCode)")
}
}
}
task.resume()
}
func dataTaskWithRequest(_ request: NSURLRequest,
completionHandler completionHandler: (NSData?,
NSURLResponse?,
NSError?) -> Void) -> NSURLSessionDataTask
The initial method takes an NSURLResponse. This is the response of the requested URL. NSURLResponse can be of any kind of response.
NSHTTPURLResponse is a subclass of NSURLResponse, you can cast your response to NSHTTPURLResponse only if you are sure that your webservice encode responses using the HTTP protocol. Otherwise, the cast will always return nil.
Also, I'm seeing that the Webservice that you are using has some restrictions of usage :
How to get accurate API response
1 Do not send requests more then 1 time per 10 minutes from one device/one API key. Normally the weather is not changing so
frequently.
2 Use the name of the server as api.openweathermap.org. Please never
use the IP address of the server.
3 Call API by city ID instead of city name, city coordinates or zip
code. In this case you get precise respond exactly for your city.
4 Free account has limitation of capacity and data availability. If
you do not get respond from server do not try to repeat your
request immediately, but only after 10 min. Also we recommend to store
your previous request data.
Now I'm thinking that your problem could come from there.

How to pause NSURLSessionDownloadTask?

This is how I use NSURLSessionDownloadTask:
let sessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
let sessionManager = AFURLSessionManager(sessionConfiguration: sessionConfiguration)
if let url = NSURL(string: URLString) {
let request = NSURLRequest(URL: url)
let sessionDownloadTask = sessionManager.downloadTaskWithRequest(request, progress: nil, destination: { (url, response) -> NSURL in
self.createDirectoryAtPath(destinationPath)
return destinationPath.URLByAppendingPathComponent(fileName)
}, completionHandler: { response, url, error in
completionBlock(error)
})
sessionDownloadTask.resume()
}
I know that I can call sessionDownloadTask.cancel() and sessionDownloadTask.resume(), but I need to call there .pause(), and then .resume() to continue download. Is it possible?
This method of NSURLSessionTask
.suspend()
Doc
A task, while suspended, produces no network traffic and is not subject to timeouts. A download task can continue transferring data at a later time. All other tasks must start over when resumed.
Don't call the sessionDownloadTask.cancel() instead use the call sessionDownloadTask.cancelByProducingResumeData and whenever you want to resume downloading use the method of NSURLSession
sessionManager.downloadTaskWithResumeData:resumeDataObject

NSURLSession task not running

I'm trying to use NSURLSession to get ID's from a url, but the code between let task = NSURLSession.... and task.resume is never executed (I placed a breakpoint inside to check, as you can see in the attached image).
What am I missing?
func getPlayingSongData() {
while loopItem < IDsForSongsToPlay.count {
self.getPlayingSongDataLoop()
loopItem++
}
self.stream()
}
func getPlayingSongDataLoop() {
playerStatus = "Paused"
let songIDToGet = IDsForSongsToPlay[loopItem]
let url:NSURL = NSURL(string: "http://url.com/ajax.php?call=song&id=\(songIDToGet)")!
print(url)
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
// Convert Json data into an Array
let songID:[String:String] = (try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! [String:String]
print(songID["id"]!)
self.StreamIDsForSongs.append((songID["id"]!))
songPlaying = songID["title"]!
}
task.resume()
}
UPDATE:
I changed
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
to
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
and then it worked for me.
My code with breakpoints
According to Apple the function dataTaskWithURL(_:completionHandler:):
Creates a task that retrieves the contents of the specified URL, then calls a handler upon completion.
If the request completes successfully, the data parameter of the completion handler block contains the resource data, and the error parameter is nil. If the request fails, the data parameter is nil and the error parameter contain information about the failure.
Then you should see the values for each parameter regarding your request.
I hope this help you.

ios sharedsession error on completion

I am blocked by this nasty error while learning iOS sharedSession singleton and async calls
here is my code
let baseUrl = NSURL(string: "https://api.forecast.io/forecast/\(apiKey)/")
let forecast = NSURL(string: "47.856223,-122.272590", relativeToURL: baseUrl)
let sharedSession = NSURLSession.sharedSession()
let downloadTask: NSURLSessionDownloadTask =
sharedSession.downloadTaskWithURL( forecast, completionHandler:
{ (loction: <#NSURL!#>, response: <#NSURLResponse!#>, error: <#NSError!#>) -> Void in
println(response);
})
}
here is where error happens
{ (loction: <#NSURL!#>, response: <#NSURLResponse!#>, error: <#NSError!#>) -> Void in
println(response);
})
here is error
1-expected an identifier to name generic parameter
2- expect parameter type following :
You need to add placeholders with appropriate variables.Replace your code with following
sharedSession.downloadTaskWithURL(forecast , completionHandler:{(location:NSURL!, response:NSURLResponse!, error:NSError!) -> Void in
println(response);
})

Resources