This question already has answers here:
Loading/Downloading image from URL on Swift
(39 answers)
Closed 7 years ago.
I am trying to downlaod an image.
This is the link of the image
https://www.google.it/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png
what I did is:
let url = NSURL(string: "https://www.google.it/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png")
let session = NSURLSession.sharedSession()
let task = session.downloadTaskWithURL(url!, completionHandler: {(url, response, error) in
if let error = error {
print("error = \(error)")
}
if let response = response {
print("response = \(response)")
}
})
task.resume()
really i have a response, and i can see it in the log as the following:
response = <NSHTTPURLResponse: 0x7fc1a1427060> { URL: https://www.google.it/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png } { status code: 200, headers {
"Cache-Control" = "private, max-age=31536000";
"Content-Length" = 13504;
"Content-Type" = "image/png";
Date = "Mon, 23 Nov 2015 23:57:55 GMT";
Expires = "Mon, 23 Nov 2015 23:57:55 GMT";
"Last-Modified" = "Fri, 04 Sep 2015 22:33:08 GMT";
Server = sffe;
"alt-svc" = "quic=\"www.google.com:443\"; p=\"1\"; ma=600,quic=\":443\"; p=\"1\"; ma=600";
"alternate-protocol" = "443:quic,p=1";
"x-content-type-options" = nosniff;
"x-xss-protection" = "1; mode=block";
} }
my problem is that there is no data in the response to get the actual image. I used to call the dataTaskWithRequest and the clouser for it have a data like this:
session.dataTaskWithRequest(request, completionHandler: {(data, response, error)
but here i didn't find the data, what is my wrong please?
When using a "download" task, you need to implement a session delegate method to get the data. Download tasks put the data into a temporary file, then call the delegate to tell it where that file is located.
If you implement URLSession:downloadTask:didFinishDownloadingToURL: in your download delegate, that method will be called with a file URL pointing to the temporary file.
If the images aren't too big, you can use data-style tasks instead. They download the data to memory, then give you an NSData object in the completion handler.
Related
I'm using Alamofire 3 and the end point I'm calling (I don't own the server) accepts a POST request and returns HTML as a response.
I am able to get the HTML response when using curl in the command line, however Alamofire doesn't return the response body, but only the header.
This is my code:
let headers = [
"Referer": "SOMEURL"
]
Alamofire.request(.POST, url, headers: headers)
.validate()
.response { request, response, data, error in
// do something with response
}
response is:
Optional(<NSHTTPURLResponse: 0x7f9ba3759760> { URL: SOMEURL } { status code: 200, headers {
Connection = "keep-alive";
"Content-Encoding" = gzip;
"Content-Type" = "text/html";
Date = "Thu, 22 Sep 2016 15:19:20 GMT";
Server = nginx;
"Transfer-Encoding" = Identity;
Vary = "Accept-Encoding";
} })
and data is:
Optional<NSData>
- Some : <>
Any thoughts?
I am working on get data from client server(ratin24 API). The API basically work after Authentication means I have one certificate and I was authenticate it with NSURLSession "didReceiveChallenge" delegate method. Everything is working fine but now issue is that I Got only header parts as a response not BOTY. so how to get actual data from there. I Pass XML Parameter in request body and the response should be XML but Got only header so please help me how to get BODY data in this situation.
let xmlString = "<?xml version='1.0' encoding='ISO-8859-1'?><TICKETANYWHERE><COUPON VER='1.0'><TEMPLATELIST /></COUPON></TICKETANYWHERE>"
let xmlData = xmlString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
let request = NSMutableURLRequest(URL: NSURL(string: "My URL")!)
request.HTTPMethod = "POST"
request.HTTPBody = xmlData
request.addValue("text/html; charset=ISO-8859-1", forHTTPHeaderField: "Content-Type")
struct SessionProperties {
static let identifier : String! = "url_session_background_download"
}
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let backgroundSession = NSURLSession(configuration: configuration, delegate:self as? NSURLSessionDelegate, delegateQueue: NSOperationQueue.mainQueue())
let downloadTask = backgroundSession.downloadTaskWithRequest(request){ (data, response, error) -> Void in
let httpResponse = response as! NSHTTPURLResponse
let statusCode = httpResponse.statusCode
if (statusCode == 200) {
print("Everyone is fine, file downloaded successfully.")
}
}
downloadTask.resume()
Response Data (only Header) body ? :
status code: 200, headers {
Connection = close;
"Content-Length" = 23113;
"Content-Type" = "text/html; charset=ISO-8859-1";
Date = "Mon, 27 Jun 2016 11:36:12 GMT";
Server = "Apache/2.2.15 (CentOS)";
}
The response object isn't supposed to contain the body data. An NSURLResponse object contains only metadata, such as the status code and headers. The actual body data should be in the NSData object for a data task, or in the provided file for a download task.
Note that for a download task the first parameter is an NSURL, not an NSData object. That NSURL contains the location of a file on disk from which you must immediately read the response data in your completion handler or move the file to a permanent location.
I have a file in my webserver and I am downloading that to my app everytime I access it because its possible that file content might be changed But If it is changed I would like to download that time only so bandwidth can be saved and fortunately that's what this ETag and If-None-Match header fields are for.
When I make a request first time ,I retrieve the ETag from the HTTP response headers
In the subsequent requests to download that file I'd attach the Etag value for If-None-Match headerfield so that if there is no change then I'd get HTTP response status code 304 or else I'd get 200 if there is a change in the file.
Note:
When I try the above steps in Advanced REST Client Application in chrome it works fine as it is supposed to be but when I try that in iOS I always get the response code 200 but it should have given me 304 for the subsequent requests.
Here is the sample code I use
var request1 = NSMutableURLRequest(URL:NSURL(string: "http://10.12.1.101/Etag/ringtone1.mp3")!)
let Etagvalue="\"36170-52c1cc36d9b40\""
var session1 = NSURLSession.sharedSession()
request1.HTTPMethod = "GET"
var err: NSError?
request1.addValue(Etagvalue, forHTTPHeaderField: "If-None-Match")
var task = session1.dataTaskWithRequest(request1, completionHandler: {data, response, error -> Void in
print("response: \(response)")
})
Here is the response
response: Optional( { URL:
http://10.12.1.101/Etag/ringtone1.mp3 } { status code: 200, headers {
"Accept-Ranges" = bytes;
Connection = "Keep-Alive";
"Content-Length" = 221552;
"Content-Type" = "audio/mpeg";
Date = "Wed, 24 Feb 2016 14:57:53 GMT";
Etag = "\"36170-52c1cc36d9b40\"";
"Keep-Alive" = "timeout=5, max=100";
"Last-Modified" = "Fri, 19 Feb 2016 10:15:33 GMT";
Server = "Apache/2.4.16 (Unix) PHP/5.5.29"; } })
What am I doing wrong here ?
I've encountered the same problem. I've discovered that it's because of cachePolicy. You need to set it as follows:
request.cachePolicy = .ReloadIgnoringLocalAndRemoteCacheData
And you will be OK
CachePolicy naming convention is confusing and better yet it actually does not implement some of them...
This article explains them well. http://nshipster.com/nsurlcache/
Also, if you let the Cache Policy to use UseProtocolCachePolicy then your NSURLSession will receive Status code 200 with response generated from Cache.
This issue is due to the cache policy 'useProtocolCachePolicy', 'returnCacheDataElseLoad' or 'returnCacheDataDontLoad'.
You can use any other policy then above one. Preferably 'reloadIgnoringLocalAndRemoteCacheData'.
.reloadRevalidatingCacheData was introduced recently which sounds a bit more efficient than the reloadIgnoringLocalAndRemoteCacheData way of the yore
I'm using NSURLSession to request a JSON resource from an HTTP server. The server uses Cache-Control to limit the time the resource is cached on clients.
This works great, but I'd also like to cache a deserialized JSON object in memory as it is accessed quite often, while continuing to leverage the HTTP caching mechanisms built into NSURLSession.
I'm thinking I can save a few HTTP response headers: Content-MD5, Etag, and Last-Modified along with the deserialized JSON object (I'm using those 3 fields since I've noticed not all HTTP servers return Content-MD5, otherwise that'd be sufficient by itself). The next time I receive a response for the JSON object, if those 3 fields are the same then I can reuse the previously deserialized JSON object.
Is this a robust way to determine the deserizlied JSON is still valid. If not, how do I determine if the deserialized object is up to date?
I created a HTTPEntityFingerprint structure which stores some of the entity headers: Content-MD5, Etag, and Last-Modified.
import Foundation
struct HTTPEntityFingerprint {
let contentMD5 : String?
let etag : String?
let lastModified : String?
}
extension HTTPEntityFingerprint {
init?(response : NSURLResponse) {
if let httpResponse = response as? NSHTTPURLResponse {
let h = httpResponse.allHeaderFields
contentMD5 = h["Content-MD5"] as? String
etag = h["Etag"] as? String
lastModified = h["Last-Modified"] as? String
if contentMD5 == nil && etag == nil && lastModified == nil {
return nil
}
} else {
return nil
}
}
static func match(first : HTTPEntityFingerprint?, second : HTTPEntityFingerprint?) -> Bool {
if let a = first, b = second {
if let md5A = a.contentMD5, md5B = b.contentMD5 {
return md5A == md5B
}
if let etagA = a.etag, etagB = b.etag {
return etagA == etagB
}
if let lastA = a.lastModified, lastB = b.lastModified {
return lastA == lastB
}
}
return false
}
}
When I get an NSHTTPURLResponse from an NSURLSession, I create an HTTPEntityFingerprint from it and compare it against a previously stored fingerprint using HTTPEntityFingerprint.match. If the fingerprints match, then the HTTP resource hasn't changed and thus I do not need to deserialized the JSON response again; however, if the fingerprints do not match, then I deserialize the JSON response and save the new fingerprint.
This mechanism only works if your server returns at least one of the 3 entity headers: Content-MD5, Etag, or Last-Modified.
More Details on NSURLSession and NSURLCache Behavior
The caching provided by NSURLSession via NSURLCache is transparent, meaning when you request a previously cached resource NSURLSession will call the completion handlers/delegates as if a 200 response occurred.
If the cached response has expired then NSURLSession will send a new request to the origin server, but will include the If-Modified-Since and If-None-Match headers using the Last-Modified and Etag entity headers in the cached (though expired) result; this behavior is built in, you don't have to do anything besides enable caching. If the origin server returns a 304 (Not Modified), then NSURLSession will transform this to a 200 response the application (making it look like you fetched a new copy of the resource, even though it was still served from the cache).
This could be done with simple HTTP standard response.
Assume previous response is something like below:
{ status code: 200, headers {
"Accept-Ranges" = bytes;
Connection = "Keep-Alive";
"Content-Length" = 47616;
Date = "Thu, 23 Jul 2015 10:47:56 GMT";
"Keep-Alive" = "timeout=5, max=100";
"Last-Modified" = "Tue, 07 Jul 2015 11:28:46 GMT";
Server = Apache;
} }
Now use below to tell server not to send date if it is not modified since.
NSURLSession is a configurable container, you would probably need to use http option "IF-Modified-Since"
Use below configuration kind before downloading the resource,
NSURLSessionConfiguration *backgroundConfigurationObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"myBackgroundSessionIdentifier"];
[backgroundConfigurationObject setHTTPAdditionalHeaders:
#{#"If-Modified-Since": #"Tue, 07 Jul 2015 11:28:46 GMT"}];
if resource for example doesn't change from above set date then below delegate will be called
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) downloadTask.response;
if([httpResponse statusCode] == 304)
//resource is not modified since last download date
}
Check the downloadTask.response status code is 304 .. then resource is not modified and the resource is not downloaded.
Note save the previous success full download date in some NSUserDefaults to set it in if-modifed-since
When trying to log in to my app with iOS7 I'm getting a failure response with some description. This is NSLog of NSURLAuthenticationChallenge failureResponse:
<NSHTTPURLResponse: 0xa383570> { URL: <URL> } { status code: 401,headers {
"Content-Length" = 1385;"Content-Type" = "text/html;charset=utf-8";
Date = "Fri, 13 Jun 2014 12:14:24 GMT";
Server = "Apache-Coyote/1.1";
"Www-Authenticate" = "Basic realm=\"booo\"";
"x-errorCode" = 02;
"x-errorDescription" = "com.idealination.core.server.client.http.ServerException:
Request failed: <URL> Status: CLIENT_ERROR_UNAUTHORIZED
Error: #Unauthorized :: Invalid userid or password.";
} }
and I need that last line to know what error do I get. But when I use iOS6 and iPhone 3gs I get only:
<NSHTTPURLResponse: 0x1c507ae0>
What should I do to get a response like using iOS7? And why I'm getting a different response?
You should not be looking for the last line, you should be looking for the HTTP Status Code, in this case 401.
if(urlResponse.statusCode == 401) { }
If you need to convert that into what the status code means as a string use
NSString *status = [NSHTTPURLResponse localizedStringForStatusCode:urlResponse.statusCode];