iOS get file size before downloading - ios

I couldn't find a solution that worked for me. But I need to get the file size of a video I am downloading so that I can make sure the user has enough space on his phone for it.
My thoughts are to check the size of the video, then if the user has space for it, I would download it. Any recommendations?
NSURL *url = [NSURL URLWithString:stringURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {...}];

Here's a variation of the other answers that uses a function (in Swift 4) to call a closure when the size is retrieved:
func getDownloadSize(url: URL, completion: #escaping (Int64, Error?) -> Void) {
let timeoutInterval = 5.0
var request = URLRequest(url: url,
cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
timeoutInterval: timeoutInterval)
request.httpMethod = "HEAD"
URLSession.shared.dataTask(with: request) { (data, response, error) in
let contentLength = response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown
completion(contentLength, error)
}.resume()
}
Here's how this function could be used:
let url = URL(string: "https://upload.wikimedia.org/wikipedia/commons/7/70/Example.png")!
getDownloadSize(url: url, completion: { (size, error) in
if error != nil {
print("An error occurred when retrieving the download size: \(error.localizedDescription)")
} else {
print("The download size is \(size).")
}
})

Swift 3:
Since you are calling dataTask you can't use the value outside of the block so use it this way.
var contentLength: Int64 = NSURLSessionTransferSizeUnknown
let request = NSMutableURLRequest(url: url as URL, cachePolicy: NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 30.0);
request.httpMethod = "HEAD";
request.timeoutInterval = 5;
let group = DispatchGroup()
group.enter()
URLSession.shared.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
contentLength = response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown
//Here you should use the value
print("contentLength",contentLength)
group.leave()
}).resume()

Use this function to get remote size of URL. Please note this function is synchronous and will block thread, so call it from a thread different from main thread:
extension NSURL {
var remoteSize: Int64 {
var contentLength: Int64 = NSURLSessionTransferSizeUnknown
let request = NSMutableURLRequest(URL: self, cachePolicy: NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 30.0);
request.HTTPMethod = "HEAD";
request.timeoutInterval = 5;
let group = dispatch_group_create()
dispatch_group_enter(group)
NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
contentLength = response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown
dispatch_group_leave(group)
}).resume()
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, Int64(5 * NSEC_PER_SEC)))
return contentLength
}
}
then call remoteSize variable everywhere you need, on a thread different from main thread:
let size = url.remoteSize

SWIFT 3:
extension NSURL {
var remoteSize: Int64 {
var contentLength: Int64 = NSURLSessionTransferSizeUnknown
let request = NSMutableURLRequest(url: self as URL, cachePolicy: NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 30.0);
request.httpMethod = "HEAD";
request.timeoutInterval = 5;
let group = DispatchGroup()
group.enter()
URLSession.shared.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
contentLength = response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown
group.leave()
}).resume()
return contentLength
}
}

Related

How to get timeout info from dataTask

I have the following code for requesting data from an external API:
var request = URLRequest(url: myURL!, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
if error != nil {
print(error)
} else {
let httpResponse = response as? HTTPURLResponse
}
})
dataTask.resume()
The timeoutInterval is set to 10.0. But how can I get back info on if the request/session timed out, or how long the request/session took the complete? I will then use that info to determine which function I should call.
Any help is much appreciated!
If the error is not nil then cast error as URLError and check the code is .timeout or not. Here is the code.
var request = URLRequest(url: myURL!, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
if let error = error as? URLError {
if error.code == .timedOut {
print("Timeout error")
}
}
print(error)
} else {
let httpResponse = response as? HTTPURLResponse
}
})
dataTask.resume()

how to check if rtmp or hls urls are exist or they'll give 404 error in swift

I need to parse some data from rss and open related links from parsed rss in swift 2,
for example i want to check this link is valid or not:
rtmp://185.23.131.187:1935/live/jomhori1
or this one :
http://185.23.131.25/hls-live/livepkgr/_defint_/liveevent/livestream.m3u8
My code to check the validation of the url :
let urlPath: String = "http://185.23.131.25/hls-live/livepkgr/_defint_/liveevent/livestream.m3u8"
let url: NSURL = NSURL(string: urlPath)!
let request: NSURLRequest = NSURLRequest(URL: url)
let response: AutoreleasingUnsafeMutablePointer<NSURLResponse?>=nil
var valid : Bool!
do {
_ = try NSURLConnection.sendSynchronousRequest(request, returningResponse: response)
} catch {
print("404")
valid = false
}
I've searched the web but all the method I found wasn't helpful for my issue.
The answer by #sschale is nice, but NSURLConnection is deprecated, it's better to use NSURLSession now.
Here's my version of an URL testing class:
class URLTester {
class func verifyURL(urlPath: String, completion: (isOK: Bool)->()) {
if let url = NSURL(string: urlPath) {
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "HEAD"
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { (_, response, error) in
if let httpResponse = response as? NSHTTPURLResponse where error == nil {
completion(isOK: httpResponse.statusCode == 200)
} else {
completion(isOK: false)
}
}
task.resume()
} else {
completion(isOK: false)
}
}
}
And you use it by calling the class method with a trailing closure:
URLTester.verifyURL("http://google.com") { (isOK) in
if isOK {
print("This URL is ok")
} else {
print("This URL is NOT ok")
}
}
Swift 3.0 with URLSession
class URLTester {
class func verifyURL(urlPath: String, completion: #escaping (_ isOK: Bool)->()) {
if let url = URL(string: urlPath) {
var request = URLRequest(url: url)
request.httpMethod = "HEAD"
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
if let httpResponse = response as? HTTPURLResponse, error == nil {
completion(httpResponse.statusCode == 200)
} else {
completion(false)
}
})
task.resume()
} else {
completion(false)
}
}
}
This is better than your answer because it only downloads the response headers instead of the whole page (also, it's better because asynchronous).
I found a solution here in Objective C, so I ported the code to Swift (though you'll need to test it):
class testHandler: NSObject, NSURLConnectionDelegate{
func testURL(urlPath: String){
let url: NSURL = NSURL(string: urlPath)!
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "HEAD"
let connection = NSURLConnection(request: request, delegate: self)
}
func connection(connection: NSURLConnection,
didReceiveResponse response: NSURLResponse){
if response is NSHTTPURLResponse{
if (response as! NSHTTPURLResponse).statusCode==200{
//url exists
}
}
}
}
An Obj-C variation for answer provided by #Moritz:
Note: I was preferring a function instead of a class, but the behavior is the same:
+(void)verifyURL:(NSString*)urlPath withCompletion:(void (^_Nonnull)(BOOL isOK))completionBlock
{
NSURL *url = [NSURL URLWithString:urlPath];
if (url) {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = #"HEAD";
//optional: request.timeoutInterval = 3;
NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
BOOL isOK = NO;
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
int code = (int)((NSHTTPURLResponse*)response).statusCode;
//note: you may want to allow other http codes as well
isOK = !error && (code == 200);
}
completionBlock(isOK);
}];
[dataTask resume];
} else {
completionBlock(NO);
}
}
and here is a call with timestamps:
NSDate *date1 = [NSDate date];
[AppDelegate verifyURL:#"http://bing.com" withCompletion:^(BOOL isOK) {
NSDate *date2 = [NSDate date];
if (isOK) {
NSLog(#"url is ok");
} else {
NSLog(#"url is currently not ok");
}
NSTimeInterval diff = [date2 timeIntervalSinceDate:date1];
NSLog(#"time to return: %.3f", diff);
}];
For easy use I wrote below code and It's working perfectly.
var video_Url = ""
if let url = NSURL(string: Response),
data = NSData(contentsOfURL: url)
{
video_Url = Response
}
else
{
video_Url = ""
}

How do you add headers to dataTaskWithUrl?

I have a dataTaskWithUrl:
var headers: NSDictionary = ["X-Mashape-Key": "my-secret-key" , "Accept" : "application/json"]
var stringUrl = "https://restcountries-v1.p.mashape.com/all"
stringUrl = stringUrl.stringByReplacingOccurrencesOfString(" ", withString: "+")
let url = NSURL(string: stringUrl)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url!, completionHandler: { (data, response, error) -> Void in
if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as? NSDictionary{
println(jsonResult)
}else{
println("error")
}
})
task.resume()
I want to add headers to my task.
In other words, I would like to convert this code to swift:
NSDictionary *headers = #{#"X-Mashape-Key": #"my-secret-key", #"Accept": #"application/json"};
UNIUrlConnection *asyncConnection = [[UNIRest get:^(UNISimpleRequest *request) {
[request setUrl:#"https://restcountries-v1.p.mashape.com/all"];
[request setHeaders:headers];
}] asJsonAsync:^(UNIHTTPJsonResponse *response, NSError *error) {
NSInteger code = response.code;
NSDictionary *responseHeaders = response.headers;
UNIJsonNode *body = response.body;
NSData *rawBody = response.rawBody;
}];
I am new to dataRequests. I do not understand Objective C code but I made a guess when I looked at that code. I need to use headers because I if I just try going to
https://restcountries-v1.p.mashape.com/all directly, I get an error. I had received that Objective C code from this website: https://www.mashape.com/fayder/rest-countries-v1. Any help in the right direction would be very much appreciated.
Thanks
Update for Swift 4+:
let httpUrl = "http://...."
guard let url = URL(string: httpUrl) else {
return
}
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("my-secret-key", forHTTPHeaderField: "X-Mashape-Key")
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
}
task.resume()
Old Post:
If you want to use dataTask
var stringUrl = "https://restcountries-v1.p.mashape.com/all"
stringUrl = stringUrl.stringByReplacingOccurrencesOfString(" ", withString: "+")
let url = NSURL(string: stringUrl)
let session = NSURLSession.sharedSession()
var muableRequest = NSMutableURLRequest(URL: url!)
muableRequest.setValue("application/json", forHTTPHeaderField: "Accept")
muableRequest.setValue("my-secret-key", forHTTPHeaderField: "X-Mashape-Key")
let task = session.dataTaskWithRequest(muableRequest, completionHandler: { (data, response, error) -> Void in
if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil){
println(jsonResult)
}
})
task.resume()
It's the same answer as #Leo's answer but the syntax for Swift changed a little which is why I think it's good to "update the answer a little". So this should work with Swift 3.
func get(_ url: String) {
if let url = URL(string: url) {
var request = URLRequest(url: url)
// Set headers
request.setValue("headerValue", forHTTPHeaderField: "headerField")
request.setValue("anotherHeaderValue", forHTTPHeaderField: "anotherHeaderField")
let completionHandler = {(data: Data?, response: URLResponse?, error: Error?) -> Void in
// Do something
}
URLSession.shared.dataTask(with: request, completionHandler: completionHandler).resume()
} else {
// Something went wrong
}

sendAsynchronousRequest was deprecated in iOS 9, How to alter code to fix

Below is my code I am getting the issue with:
func parseFeedForRequest(request: NSURLRequest, callback: (feed: RSSFeed?, error: NSError?) -> Void)
{
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) { (response, data, error) -> Void in
if ((error) != nil)
{
callback(feed: nil, error: error)
}
else
{
self.callbackClosure = callback
let parser : NSXMLParser = NSXMLParser(data: data!)
parser.delegate = self
parser.shouldResolveExternalEntities = false
parser.parse()
}
}
}
This is now deprecated as of iOS 9, and is telling me to use dataTaskWithRequest instead. Can someone help me change sendAsync with dataTask, I don't know how to.
Use NSURLSession instead like below,
For Objective-C
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:"YOUR URL"]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
// handle response
}] resume];
For Swift,
var request = NSMutableURLRequest(URL: NSURL(string: "YOUR URL")!)
var session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
var params = ["username":"username", "password":"password"] as Dictionary<String, String>
request.HTTPBody = try? NSJSONSerialization.dataWithJSONObject(params, options: [])
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
print("Response: \(response)")})
task.resume()
For asynchronously query, from Apple docs
Like most networking APIs, the NSURLSession API is highly
asynchronous. It returns data in one of two ways, depending on the
methods you call:
To a completion handler block that returns data to your app when a
transfer finishes successfully or with an error.
By calling methods on your custom delegate as the data is received.
By calling methods on your custom delegate when download to a file is
complete.
Swift implementation
let session = NSURLSession.sharedSession()
session.dataTaskWithRequest(request) { (data, response, error) -> Void in
}
Swift 3.0
var request = URLRequest(url: URL(string: "http://example.com")!)
request.httpMethod = "POST"
let session = URLSession.shared
session.dataTask(with: request) {data, response, err in
print("Entered the completionHandler")
}.resume()
This is the swift 2.1 version:
let request = NSMutableURLRequest(URL: NSURL(string: "YOUR URL")!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
let params = ["username":"username", "password":"password"] as Dictionary<String, String>
request.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(params, options: [])
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
print("Response: \(response)")})
task.resume()
Swift 2.0:
Old (replace with New below):
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue()) { (response, data, error) -> Void in
// Code
}
New:
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){ data, response, error in
// Code
}
task.resume()
Swift 4
let params = ["email":"email#email.com", "password":"123456"] as Dictionary<String, String>
var request = URLRequest(url: URL(string: "http://localhost:8080/api/1/login")!)
request.httpMethod = "POST"
request.httpBody = try? JSONSerialization.data(withJSONObject: params, options: [])
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let session = URLSession.shared
let task = session.dataTask(with: request, completionHandler: { data, response, error -> Void in
do {
let json = try JSONSerialization.jsonObject(with: data!) as! Dictionary<String, AnyObject>
print(json)
} catch {
print("error")
}
})
task.resume()
with swift 3.1
let request = NSMutableURLRequest(url: NSURL(string: image_url_string)! as URL)
let session = URLSession.shared
request.httpMethod = "POST"
let params = ["username":"username", "password":"password"] as Dictionary<String, String>
request.httpBody = try? JSONSerialization.data(withJSONObject: params, options: [])
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTask(with: request as URLRequest, completionHandler: {data, response, error -> Void in
print("Response: \(String(describing: response))")})
task.resume()
Illustrating with an example, the alternative code to the deprecation of:
sendAsynchronousRequest(_:queue:completionHandler:)' was deprecated in iOS 9.0: Use [NSURLSession dataTaskWithRequest:completionHandler:]
Tested and works in Swift 2.1 onwards.
import UIKit
class ViewController: UIViewController {
#IBOutlet var theImage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string: "https://upload.wikimedia.org/wikipedia/commons/6/6a/Johann_Sebastian_Bach.jpg")
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) { (data, response, error) -> Void in
if error != nil {
print("thers an error in the log")
} else {
dispatch_async(dispatch_get_main_queue()) {
let image = UIImage(data: data!)
self.theImage.image = image
}
}
}
task.resume()
}
}
//Displays an image on the ViewControllers ImageView. Connect an outlet of the ImageView
Here is the SWIFT3.0 Version of Nilesh Patel's Answer with JSONSerialised data
let url = URL(string: "<HERE GOES SERVER API>")!
var request = URLRequest(url: url)
request.httpMethod = "POST" //GET OR DELETE etc....
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("<ValueforAuthorization>", forHTTPHeaderField: "Authorization")
let parameter = [String:Any]() //This is your parameters [String:Any]
do {
let jsonData = try JSONSerialization.data(withJSONObject: parameter, options: .prettyPrinted)
// here "jsonData" is the dictionary encoded in JSON data
request.httpBody = jsonData
let session = URLSession(configuration: .default)
let task = session.dataTask(with: request, completionHandler: { (incomingData, response, error) in
if let error = error {
print(error.localizedDescription)
print(request)
}else if let response = response {
print(response)
}else if let incomingData = incomingData {
print(incomingData)
}
})
task.resume()
} catch {
print(error.localizedDescription)
}
Swift 4.2
This worked for me:
func loadImageFromURL(URL: NSURL) {
let request = URLRequest(url: URL as URL)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let imageData = data {
DispatchQueue.main.async {
self.imageView.image = UIImage(data: imageData)
}
}
}
task.resume()
}
I had to add "DispatchQueue.main.async { }" because I had a runtime warning, since only the main thread is supposed to modify UI elements.

URL File Size With NSURLConnection - Swift

i am trying to get a file size from url before downloading
here is the obj-c code
NSURL *URL = [NSURL URLWithString:"ExampleURL"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
[request setHTTPMethod:#"HEAD"];
NSHTTPURLResponse *response;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error: nil];
long long size = [response expectedContentLength];
and here is Swift Code
var url:NSURL = NSURL(string: "ExmapleURL")
var request:NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.HTTPMethod = "HEAD"
var response = NSHTTPURLResponse()
NSURLConnection.sendSynchronousRequest(request, returningResponse: &response , error: nil)
but i have error here
NSURLConnection.sendSynchronousRequest(request, returningResponse: &response , error: nil)
'NSHTTPURLResponse' is not identical to 'NSURLResponse?'
did i miss something in swift here ?
The response parameter has the type
AutoreleasingUnsafeMutablePointer<NSURLResponse?>
which means that you can pass the address of an optional NSURLResponse as argument:
var response : NSURLResponse?
NSURLConnection.sendSynchronousRequest(request, returningResponse: &response , error: nil)
You can then conditionally cast the returned response to a NSHTTPURLResponse:
if let httpResponse = response as? NSHTTPURLResponse {
println(httpResponse.expectedContentLength)
}
Note that you should check the return value of sendSynchronousRequest(), which
is nil if no connection could be made.
It is also recommended to call this
method only from a separate thread (or use sendAsynchronousRequest() instead)
because it can take a while to make a connection
– in particular when using a cellular network – and the main thread would be
blocked otherwise.
Swift 4 solution:
func fetchContentLength(for url: URL, completionHandler: #escaping (_ contentLength: Int64?) -> ()) {
var request = URLRequest(url: url)
request.httpMethod = "HEAD"
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard error == nil, let response = response as? HTTPURLResponse, let contentLength = response.allHeaderFields["Content-Length"] as? String else {
completionHandler(nil)
return
}
completionHandler(Int64(contentLength))
}
task.resume()
}
// Usage:
let url = URL(string: "https://s3.amazonaws.com/x265.org/video/Tears_400_x265.mp4")!
fetchContentLength(for: url, completionHandler: { contentLength in
print(contentLength ?? 0)
})

Resources