Reading data from an URL with URLSession - swift

I'm using URLSession to retrieve data from an url
func scheduleURLSession() {
let backgroundConfigObject = URLSessionConfiguration.background(withIdentifier: "myIdentifier...")
let backgroundSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil)
let retrieveTask = backgroundSession.downloadTask(with: URL(string: "https://api.wedsblog.net/v2/index.php?bitcoin&simple")!)
retrieveTask.resume()
}
When download finished
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { }
Will be called. How can I read the content of downloaded file? Is ".downloadTask" the right function or shall I use another one?

Found following solution:
Implemented URLSessionDataDelegate
func scheduleURLSession() {
let backgroundConfigObject = URLSessionConfiguration.background(withIdentifier: "myIdentifier")
let backgroundSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil)
let retrieveTask = backgroundSession.dataTask(with: URL(string: "https://api.wedsblog.net/v2/index.php?bitcoin&simple")!)
retrieveTask.resume()
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
print(String(data: data, encoding: String.Encoding.utf8)!)
}

let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url) {(data, response, error) -> Void in { // First check if error is not nil
// Second check what is status code of your http response
// Third do with data what you want(parse to objects)
}
I think it helpful.

Related

How to put the file download progress on the label on the MacOS App interface using Swift?

I write a download button that works fine, but can't show the percentage progress on the label.
I tried putting the label in the FileDownloader class, but it's not updating and showing on the interface.
0.041681744
0.045944795
0.11841663
0.12694274
0.13546883
.
.
.
99.66489
99.84393
99.84819
100.0
this is my code:
class ramdisk: NSViewController {
...
class FileDownloader : NSObject, URLSessionDownloadDelegate {
var url : URL?
// will be used to do whatever is needed once download is complete
var yourOwnObject : NSObject?
init(_ yourOwnObject : NSObject)
{
self.yourOwnObject = yourOwnObject
}
//is called once the download is complete
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL)
{
//copy downloaded data to your documents directory with same names as source file
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
//FileManager.default.
let destinationUrl = documentsUrl!.appendingPathComponent(url!.lastPathComponent)
let dataFromURL = NSData(contentsOf: location)
dataFromURL?.write(to: destinationUrl, atomically: true)
//now it is time to do what is needed to be done after the download
//yourOwnObject!.callWhatIsNeeded()
}
//this is to track progress
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
let progressPercentage = progress * 100
print(progressPercentage)
}
// if there is an error during download this will be called
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)
{
if (error != nil)
{
//handle the error
print("Download completed with error: \(error!.localizedDescription)");
}
}
//method to be called to download
func download(url: URL)
{
self.url = url
//download identifier can be customized. I used the "ulr.absoluteString"
let sessionConfig = URLSessionConfiguration.background(withIdentifier: url.absoluteString)
let session = Foundation.URLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil)
let task = session.downloadTask(with: url)
task.resume()
}
}
#IBAction func downloads(_ sender: Any) {
let strURL = "http://downloads.xyz/test.zip"
let url = URL(string: strURL)
FileDownloader(url! as NSObject).download(url: url!)
}
...
}
I write a download button that works fine, but can't show the percentage progress on the label.
I tried putting the label in the FileDownloader class, but it's not updating and showing on the interface.

Swift URL Session: Progress bar increments even with Server Error [404, 500]

I have implemented a background upload task using URLSession as follow:
let boundary = UUID().uuidString
let configuration = URLSessionConfiguration.background(withIdentifier: "com.example.upload")
let session = URLSession(configuration: configuration, delegate: self, delegateQueue: .main)
var urlRequest = URLRequest(url: URL(string: "https://server/file.php")!)
urlRequest.httpMethod = "POST"
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
var data = Data()
data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
data.append("Content-Disposition: form-data; name=\"video\"; filename=\"rawvideo.mp4\"\r\n".data(using: .utf8)!)
data.append("Content-Type: video/mp4\r\n\r\n".data(using: .utf8)!)
data.append(videoData!)
data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)
let tempDir = FileManager.default.temporaryDirectory
let localURL = tempDir.appendingPathComponent("upload")
try? data.write(to: localURL)
session.uploadTask(with: urlRequest, fromFile: localURL).resume()
To handle the server response, I have used the following code [But I am not getting any print output. Seems this method is never called]
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: #escaping (URLSession.ResponseDisposition) -> Void) {
print("anoop")
guard let response = response as? HTTPURLResponse, (200...209).contains(response.statusCode) else {
DispatchQueue.main.async {
self.uploadComplete("Error", "Upload Server Down!")
}
print("error")
completionHandler(.cancel)
return
}
completionHandler(.allow)
}
To handle the server Data, I am using following code:
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
if let toDictionary = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? NSDictionary {
print(toDictionary)
}
}
To handle the completion, I am using following code:
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let error = error {
print(error.localizedDescription)
}
}
Additionally, I have a progress bar which I am updating as follows:
func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
let uploadProgress:Float = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
let percent = Int(uploadProgress * 100)
DispatchQueue.main.async {
self.progressLabel.text = "\(percent)% completed"
self.progressView.setProgress(Float(uploadProgress), animated: true)
}
}
This code works well if the server is working fine [Response: 200].
But if I change the request URL to a dummy url like https://server/dummyfile.php, the progress bar still increments and I don't get any error since
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: #escaping (URLSession.ResponseDisposition) -> Void)
doesn't work.
Is it possible that in case of server errors [404, 500] the progress bar doesn't increment?

Why URLSession doesn't go in URLSessionDownloadDelegate?

i try to print progress of downloading using URLSessionDownloadDelegate, but delegate's methods don't work
Although image is download, progress don't print
I have button
#IBAction func downloadTapped(_ sender: UIButton) {
let image = "https://neilpatel-qvjnwj7eutn3.netdna-ssl.com/wp-content/uploads/2016/02/applelogo.jpg"
guard let url = URL(string: image) else {return}
let operationQueue = OperationQueue()
let session = URLSession(configuration: .default, delegate: self, delegateQueue: operationQueue)
session.downloadTask(with: url) { (data, response, error) in
guard let url = data else {return}
do {
let data = try Data(contentsOf: url)
OperationQueue.main.addOperation {
self.imageView.image = UIImage(data: data)
}
} catch {
}
}.resume()
}
And extension
extension DownloadingViewController: URLSessionDownloadDelegate {
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("=====FINISH=====")
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
let progress = Float(bytesWritten) / Float(totalBytesWritten)
print(progress)
}
}
nothing at all
You are calling
session.downloadTask(with: url) { (data, response, error) in
This means that the URLSession's delegate is ignored, because the download task has a completion handler which is used instead. So what you are seeing is the expected behavior.
If you wish to use a delegate, call
session.downloadTask(with: url)
and do everything in the delegate, including receiving the downloaded file.
On the other hand, if your goal is merely to display progress, there is no need for the delegate. The download task vends a progress object for this purpose. Example:
let task = session.downloadTask(with:url) { fileURL, resp, err in
// whatever
}
// self.prog is a UIProgressView
self.prog.observedProgress = task.progress
task.resume()

Swift: Cant get progress of URLSessionDownload in delegate?

Ok, I have never saved files before (m4as in this case) and have no idea if I am doing this correctly and moreover cannot get the download progress of the URL, despite including the required URLSessionDownloadDelegate funcs here:
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
if totalBytesExpectedToWrite > 0 {
let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
print("Progress \(downloadTask) \(progress)")
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("Download finished: \(location)")
try? FileManager.default.removeItem(at: location)
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
print("Task completed: \(task), error: \(error)")
}
I got these off a tutorial however nothing is printed to console ever. I tried calling these functions however I do not know where to get the downloadTask var or the session.
This is how I download my file, this works:
func goDownload()
{
if let audioUrl = testUrl { //set at beginning
// then lets create your document folder url
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
// lets create your destination file url
let destinationUrl = documentsDirectoryURL.appendingPathComponent(audioUrl.lastPathComponent)
print(destinationUrl)
// to check if it exists before downloading it
if FileManager.default.fileExists(atPath: destinationUrl.path) {
print("********** The file already exists at path")
// if the file doesn't exist
} else {
// you can use NSURLSession.sharedSession to download the data asynchronously
URLSession.shared.downloadTask(with: audioUrl, completionHandler: { (location, response, error) -> Void in
guard let location = location, error == nil else { return }
do {
// after downloading your file you need to move it to your destination url
try FileManager.default.moveItem(at: location, to: destinationUrl)
//success
print("************** SUCCESS File moved to documents folder", audioUrl)
self.playModeStreaming = false
self.pausePlay()
AudioPlayerManager.shared.play(url: audioUrl)
} catch let error as NSError {
print(error.localizedDescription)
}
}).resume()
}
}
}
However even if I try to get the downloadTask from here I have type issues. How can I get the download progress (bytes received, etc) using this download func?
EDIT: This is what I have:
var session = URLSession()
let sessDelegate = CustomDelegate()
self.session = URLSession(configuration: .default, delegate: sessDelegate, delegateQueue: nil)
then replaced URLSession.shared with session in the above func.
My custom delegate class separately:
class CustomDelegate : NSObject, URLSessionDataDelegate, URLSessionTaskDelegate {
//define all `NSURLSessionDataDelegate` and `NSURLSessionTaskDelegate` methods here
//URLSessionDelegate methods
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
if totalBytesExpectedToWrite > 0 {
let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
print("Progress \(downloadTask) \(progress)")
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("Download finished: \(location)")
try? FileManager.default.removeItem(at: location)
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
print("Task completed: \(task), error: \(error)")
}
}
You can add a custom delegate class, and implement NSURLSessionDataDelegate and NSURLSessionTaskDelegate protocols.
class CustomDelegate : NSObject, NSURLSessionDataDelegate, NSURLSessionTaskDelegate {
//define all `NSURLSessionDataDelegate` and `NSURLSessionTaskDelegate` methods here
}
Now create an instance of above class like so :
let myCustomDelegate = CustomDelegate();
You will then need to set the delegate property of your NSURLSession instance like so :
OBJECTIVE C
NSURLSession *session=[NSURLSession sessionWithConfiguration:sessionConfig delegate:myCustomDelegate delegateQueue:nil];
SWIFT 3
let session = URLSession(configuration: .default, delegate: myCustomDelegate, delegateQueue: nil)
Ref Links:
NSURLSession Class Reference
From NSURLConnection to NSURLSession

Where is dataTaskWithRequest method used?

public func dataTaskWithRequest(request: NSURLRequest, completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionDataTask
I know the usage method of above.
public func dataTaskWithRequest(request: NSURLRequest) -> NSURLSessionDataTask
But what is the usage of this method? I'm confused about how it is used.All examples I see are using the first method. The method returns no data, no error to handle, nor any response? Or is that you somehow envelope this as a task and later run in a queue?
You use this latter method if you've specified a delegate for a custom NSURLSession. The data is not returned to the closure, but rather the session calls your delegate's didReceiveData, which you have to implement separately.
It takes more work to implement the delegate methods, though, so you generally only do that where you absolutely have to (e.g. you want to process data as it comes in rather than waiting for all of the data to come in; you need delegate methods for custom handling of redirects and challenges; you are doing background NSURLSession with download or upload tasks rather than data tasks; etc.).
For example, to issue a simple GET request, expecting JSON response, you might define your class to conform to URLSessionDelegate, URLSessionDataDelegate, and, optionally, URLSessionTaskDelegate (called NSURLSessionDelegate, NSURLSessionDataDelegate and NSURLSessionTaskDelegate, respectively, in Objective-C and Swift 2), and then do something like the following in Swift 3:
var responseData: Data?
func performRequest() {
let url = URL(string: "http://ip.jsontest.com/")!
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Accept")
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
responseData = Data()
let task = session.dataTask(with: request)
task.resume()
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
responseData!.append(data)
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
guard error == nil else {
print(error!)
return
}
do {
guard let jsonObject = try JSONSerialization.jsonObject(with: responseData!) as? [String: AnyObject] else {
print("response was not JSON dictionary")
print("responseString: \(String(data: responseData!, encoding: .utf8))")
return
}
print(jsonObject)
} catch let parseError {
print("JSON Error: \(parseError)")
}
}
Or, in Swift 2:
var responseData: NSMutableData?
func performRequest() {
let url = NSURL(string: "http://ip.jsontest.com/")!
let request = NSMutableURLRequest(URL: url)
request.setValue("application/json", forHTTPHeaderField: "Accept")
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)
responseData = NSMutableData()
let task = session.dataTaskWithRequest(request)
task.resume()
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
responseData!.appendData(data)
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
guard error == nil else {
print(error!)
return
}
do {
guard let jsonObject = try NSJSONSerialization.JSONObjectWithData(responseData!, options: []) as? [String: AnyObject] else {
print("response was not JSON dictionary")
print("responseString: \(String(data: responseData!, encoding: NSUTF8StringEncoding))")
return
}
print(jsonObject)
} catch let parseError {
print("JSON Error: \(parseError)")
}
}
Clearly, you wouldn't do this unless you needed some of the more complicated delegate methods, too, but I wanted to show a minimalist implementation (which helps us appreciate the completion handler rendition).
public func dataTaskWithRequest(request: NSURLRequest, completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionDataTask
returns data, response and error directly in a completion handler.
public func dataTaskWithRequest(request: NSURLRequest) -> NSURLSessionDataTask
is used in conjunction with the URLSessionDataDelegate protocol. A couple of methods are required to be implemented. During the several stages the delegate methods are called to process the data.
The protocol method provides finer control of the loading process, for example to handle credential requests and redirection control.