URLSession delegate methods not called [duplicate] - ios

This question already has an answer here:
URLSessionDelegate Function Not Being Called
(1 answer)
Closed 4 years ago.
I download the song and then play it on the button. Everything in this part working. But I want to monitor the download indicator and pause and resume downloading. But delegate methods not called.
class ViewController: UIViewController, URLSessionDownloadDelegate, URLSessionTaskDelegate,URLSessionDelegate {
#IBOutlet weak var progressBar: UIProgressView!
let url = URL(string: "https://www.dropbox.com/s/s7zkka3at171wf8/Track01.mp3?raw=1")!
var audioPlayer : AVAudioPlayer!
var task: URLSessionDownloadTask!
let configuration1 = URLSessionConfiguration.default
var urlSession: URLSession!
let operationQueue1 = OperationQueue()
var url1 : URL!
// MARK: - Life CYcle
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - IBActions
#IBAction func playAudio(_ sender: Any) {
do {
audioPlayer = try AVAudioPlayer(contentsOf: self.url1)
audioPlayer.play()
}
catch {
print("error")
}
}
#IBAction func pauseAudio(_ sender: Any) {}
#IBAction func downloadButton(_ sender: Any) {
guard let audioUrl = URL(string: "https://www.dropbox.com/s/s7zkka3at171wf8/Track01.mp3?raw=1") else {
return
}
// 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")
self.url1 = destinationUrl
// if the file doesn't exist
}
else {
print("0")
urlSession = URLSession(configuration: configuration1, delegate: self, delegateQueue: operationQueue1)
task = urlSession.downloadTask(with: audioUrl) { (location, response, error) in
guard
let location = location else {
return
}
do {
try FileManager.default.moveItem(at: location ,to : destinationUrl)
print("File moved to documents folder")
self.url1 = destinationUrl
}
catch let error as NSError{
print(error.localizedDescription)
}
print("1")
}
task.resume()
print("2")
}
print("3")
}
// MARK: -
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("fineshed download file")
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
print(totalBytesWritten)
print(totalBytesExpectedToWrite)
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
print("All success")
}
}

Try this
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)

Related

How to show Progress in swift - iOS [duplicate]

Is there any way to get progress from dataTaskWithURL in swift while the data is downloading?
NSURLSession.sharedSession().dataTaskWithURL(...)
I need to show progress bar while the data is downloading.
You can simply observe progress property of the URLSessionDataTask object.
Example:
import UIKit
class SomeViewController: UIViewController {
private var observation: NSKeyValueObservation?
deinit {
observation?.invalidate()
}
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://source.unsplash.com/random/4000x4000")!
let task = URLSession.shared.dataTask(with: url)
observation = task.progress.observe(\.fractionCompleted) { progress, _ in
print("progress: ", progress.fractionCompleted)
}
task.resume()
}
}
Playground example:
import Foundation
import PlaygroundSupport
let page = PlaygroundPage.current
page.needsIndefiniteExecution = true
let url = URL(string: "https://source.unsplash.com/random/4000x4000")!
let task = URLSession.shared.dataTask(with: url) { _, _, _ in
page.finishExecution()
}
// Don't forget to invalidate the observation when you don't need it anymore.
let observation = task.progress.observe(\.fractionCompleted) { progress, _ in
print(progress.fractionCompleted)
}
task.resume()
you can use this code for showing download process with progress bar with its delegate functions.
import UIKit
class ViewController: UIViewController,NSURLSessionDelegate,NSURLSessionDataDelegate{
#IBOutlet weak var progress: UIProgressView!
var buffer:NSMutableData = NSMutableData()
var session:NSURLSession?
var dataTask:NSURLSessionDataTask?
let url = NSURL(string:"http://i.stack.imgur.com/b8zkg.png" )!
var expectedContentLength = 0
override func viewDidLoad() {
super.viewDidLoad()
progress.progress = 0.0
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let manqueue = NSOperationQueue.mainQueue()
session = NSURLSession(configuration: configuration, delegate:self, delegateQueue: manqueue)
dataTask = session?.dataTaskWithRequest(NSURLRequest(URL: url))
dataTask?.resume()
// Do any additional setup after loading the view, typically from a nib.
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void) {
//here you can get full lenth of your content
expectedContentLength = Int(response.expectedContentLength)
println(expectedContentLength)
completionHandler(NSURLSessionResponseDisposition.Allow)
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
buffer.appendData(data)
let percentageDownloaded = Float(buffer.length) / Float(expectedContentLength)
progress.progress = percentageDownloaded
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
//use buffer here.Download is done
progress.progress = 1.0 // download 100% complete
}
}
Update for Swift4:
Supports execution of multiple simultaneous operations.
File: DownloadService.swift. Keeps reference to URLSession and tracks executing tasks.
final class DownloadService: NSObject {
private var session: URLSession!
private var downloadTasks = [GenericDownloadTask]()
public static let shared = DownloadService()
private override init() {
super.init()
let configuration = URLSessionConfiguration.default
session = URLSession(configuration: configuration,
delegate: self, delegateQueue: nil)
}
func download(request: URLRequest) -> DownloadTask {
let task = session.dataTask(with: request)
let downloadTask = GenericDownloadTask(task: task)
downloadTasks.append(downloadTask)
return downloadTask
}
}
extension DownloadService: URLSessionDataDelegate {
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse,
completionHandler: #escaping (URLSession.ResponseDisposition) -> Void) {
guard let task = downloadTasks.first(where: { $0.task == dataTask }) else {
completionHandler(.cancel)
return
}
task.expectedContentLength = response.expectedContentLength
completionHandler(.allow)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
guard let task = downloadTasks.first(where: { $0.task == dataTask }) else {
return
}
task.buffer.append(data)
let percentageDownloaded = Double(task.buffer.count) / Double(task.expectedContentLength)
DispatchQueue.main.async {
task.progressHandler?(percentageDownloaded)
}
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
guard let index = downloadTasks.index(where: { $0.task == task }) else {
return
}
let task = downloadTasks.remove(at: index)
DispatchQueue.main.async {
if let e = error {
task.completionHandler?(.failure(e))
} else {
task.completionHandler?(.success(task.buffer))
}
}
}
}
File: DownloadTask.swift. Lightweight interface just to hide concrete implementation.
protocol DownloadTask {
var completionHandler: ResultType<Data>.Completion? { get set }
var progressHandler: ((Double) -> Void)? { get set }
func resume()
func suspend()
func cancel()
}
File: GenericDownloadTask.swift. Concrete implementation of DownloadTask interface.
class GenericDownloadTask {
var completionHandler: ResultType<Data>.Completion?
var progressHandler: ((Double) -> Void)?
private(set) var task: URLSessionDataTask
var expectedContentLength: Int64 = 0
var buffer = Data()
init(task: URLSessionDataTask) {
self.task = task
}
deinit {
print("Deinit: \(task.originalRequest?.url?.absoluteString ?? "")")
}
}
extension GenericDownloadTask: DownloadTask {
func resume() {
task.resume()
}
func suspend() {
task.suspend()
}
func cancel() {
task.cancel()
}
}
File: ResultType.swift. Reusable type to keep result or error.
public enum ResultType<T> {
public typealias Completion = (ResultType<T>) -> Void
case success(T)
case failure(Swift.Error)
}
Usage: Example how to run two download tasks in parallel (macOS App):
class ViewController: NSViewController {
#IBOutlet fileprivate weak var loadImageButton1: NSButton!
#IBOutlet fileprivate weak var loadProgressIndicator1: NSProgressIndicator!
#IBOutlet fileprivate weak var imageView1: NSImageView!
#IBOutlet fileprivate weak var loadImageButton2: NSButton!
#IBOutlet fileprivate weak var loadProgressIndicator2: NSProgressIndicator!
#IBOutlet fileprivate weak var imageView2: NSImageView!
fileprivate var downloadTask1: DownloadTask?
fileprivate var downloadTask2: DownloadTask?
override func viewDidLoad() {
super.viewDidLoad()
loadImageButton1.target = self
loadImageButton1.action = #selector(startDownload1(_:))
loadImageButton2.target = self
loadImageButton2.action = #selector(startDownload2(_:))
}
}
extension ViewController {
#objc fileprivate func startDownload1(_ button: NSButton) {
let url = URL(string: "http://localhost:8001/?imageID=01&tilestamp=\(Date.timeIntervalSinceReferenceDate)")!
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 30)
downloadTask1 = DownloadService.shared.download(request: request)
downloadTask1?.completionHandler = { [weak self] in
switch $0 {
case .failure(let error):
print(error)
case .success(let data):
print("Number of bytes: \(data.count)")
self?.imageView1.image = NSImage(data: data)
}
self?.downloadTask1 = nil
self?.loadImageButton1.isEnabled = true
}
downloadTask1?.progressHandler = { [weak self] in
print("Task1: \($0)")
self?.loadProgressIndicator1.doubleValue = $0
}
loadImageButton1.isEnabled = false
imageView1.image = nil
loadProgressIndicator1.doubleValue = 0
downloadTask1?.resume()
}
#objc fileprivate func startDownload2(_ button: NSButton) {
let url = URL(string: "http://localhost:8002/?imageID=02&tilestamp=\(Date.timeIntervalSinceReferenceDate)")!
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 30)
downloadTask2 = DownloadService.shared.download(request: request)
downloadTask2?.completionHandler = { [weak self] in
switch $0 {
case .failure(let error):
print(error)
case .success(let data):
print("Number of bytes: \(data.count)")
self?.imageView2.image = NSImage(data: data)
}
self?.downloadTask2 = nil
self?.loadImageButton2.isEnabled = true
}
downloadTask2?.progressHandler = { [weak self] in
print("Task2: \($0)")
self?.loadProgressIndicator2.doubleValue = $0
}
loadImageButton2.isEnabled = false
imageView2.image = nil
loadProgressIndicator2.doubleValue = 0
downloadTask2?.resume()
}
}
Bonus 1. File StartPHPWebServer.command. Example script to run 2 Build-in PHP servers to simulate simultaneous downloads.
#!/bin/bash
AWLScriptDirPath=$(cd "$(dirname "$0")"; pwd)
cd "$AWLScriptDirPath"
php -S localhost:8001 &
php -S localhost:8002 &
ps -afx | grep php
echo "Press ENTER to exit."
read
killall php
Bonus 2. File index.php. Example PHP script to implement slow download.
<?php
$imageID = $_REQUEST["imageID"];
$local_file = "Image-$imageID.jpg";
$download_rate = 20.5; // set the download rate limit (=> 20,5 kb/s)
if (file_exists($local_file) && is_file($local_file)) {
header('Cache-control: private');
header('Content-Type: image/jpeg');
header('Content-Length: '.filesize($local_file));
flush();
$file = fopen($local_file, "r");
while(!feof($file)) {
// send the current file part to the browser
print fread($file, round($download_rate * 1024));
flush(); // flush the content to the browser
usleep(0.25 * 1000000);
}
fclose($file);}
else {
die('Error: The file '.$local_file.' does not exist!');
}
?>
Misc: Contents of directory which simulates 2 PHP servers.
Image-01.jpg
Image-02.jpg
StartPHPWebServer.command
index.php
in class declare
class AudioPlayerViewController: UIViewController, URLSessionDelegate, URLSessionDataDelegate, URLSessionDownloadDelegate{
var defaultSession: URLSession!
var downloadTask: URLSessionDownloadTask!
in download method
defaultSession = Foundation.URLSession(configuration: .default, delegate: self, delegateQueue: nil)
downloadProgress.setProgress(0.0, animated: false)
downloadTask = defaultSession.downloadTask(with: audioUrl)
downloadTask.Resume()
And add following delegates:
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
DispatchQueue.main.async {
self.downloadProgressBar.setProgress(Float(totalBytesWritten)/Float(totalBytesExpectedToWrite), animated: true)
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
DispatchQueue.main.async {
//DOWNLOAD SUCCESSFUL AND FILE PATH WILL BE IN URL.
}
}
In reference to Dharmesh's work, there have been quite a number of minor changes with URLSessionDelegate and URLSessionDataDelegate delegate methods. Here come the code for Swift 4.2 compatibility.
import UIKit
class ViewController: UIViewController, URLSessionDelegate, URLSessionDataDelegate {
// MARK: - Variables
// MARK: - IBOutlet
#IBOutlet weak var progress: UIProgressView!
// MARK: - IBAction
#IBAction func goTapped(_ sender: UIButton) {
let url = URL(string: "http://www.example.com/file.zip")!
fetchFile(url: url)
}
// MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
}
func fetchFile(url: URL) {
progress.progress = 0.0
let configuration = URLSessionConfiguration.default
let mainQueue = OperationQueue.main
session = URLSession(configuration: configuration, delegate: self, delegateQueue: mainQueue)
dataTask = session?.dataTask(with: URLRequest(url: url))
dataTask?.resume()
}
var buffer: NSMutableData = NSMutableData()
var session: URLSession?
var dataTask: URLSessionDataTask?
var expectedContentLength = 0
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
buffer.append(data)
let percentageDownloaded = Float(buffer.length) / Float(expectedContentLength)
progress.progress = percentageDownloaded
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: (URLSession.ResponseDisposition) -> Void) {
expectedContentLength = Int(response.expectedContentLength)
completionHandler(URLSession.ResponseDisposition.allow)
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
progress.progress = 1.0
}
}
for the data been download you need to set the NSURLSessionDownloadDelegate and to implement URLSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)
There is a nice tutorial about it here but is in object-c.

how to still show the progress of progressView after reloading the view controller?

this is my first question on swift, i couldn't really find any answer in the internet. i have a download button that download a pdf file from url, and there is a progressView that shows the progress of downloading to the user. everything works fine but when i download the file and reload the view controller the progress of progressView is resets to zero. how can i still show the progress of downloading even after reloading the viewController.
I'm passing the url from another viewController
please help me.
this is my code
class CellDetailsViewController: UIViewController , URLSessionDownloadDelegate{
var download = ""
var downloadTask : URLSessionDownloadTask!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func download(_ sender: Any) {
guard let url = URL(string: download)else {return}
let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue())
downloadTask = urlSession.downloadTask(with: url)
downloadTask.resume()
print("downloading")
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("download Location", location)
let fileManager = FileManager.default
let documentPath = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let PdfName = documentPath.appendingPathComponent("\(name).pdf")
try? fileManager.removeItem(at: PdfName)
do{
try fileManager.moveItem(at: location, to: PdfName)
}catch{
print("copy error\(error.localizedDescription)")
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
if totalBytesExpectedToWrite > 0 {
let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
DispatchQueue.main.async {
self.progressView.progress = progress
print(totalBytesExpectedToWrite)
if progress == 1 {
self.progressView.isHidden = true
}
}
}
}
}
You can store a static variable in a struct somewhere else. I would create a new file that you can store in a "Model" folder and do something like this:
struct staticVariableStorage {
static var fileProgress : Double = 0.0 }
and then you could access it in your code by setting a new value to
staticVariableStorage.fileProgress
This shouldn't be affected by resetting the ViewController seeing as it would be separate from the ViewController in the first place.

Download with URLSession and Operation queue does not start next operation when application is in background

Use case : Download file from a server via one API which will give the download server url(download server url vality is for 10 secs only).
I have enabled the background capabilities.
Then created one Download Manager which holds the OperationQueue for download data task.
In Operation class when main() function calls, I have makes the API call which will return the download server URL and started a download.
Works fine in debug mode but does not work in release mode.
Download Manager :
final class SCDownloadManager: NSObject {
#objc static var shared = SCDownloadManager()
private override init() {
super.init()
NotificationCenter.default.addObserver(self, selector: #selector(SCDownloadManager.networkDidChange(_:)), name: NSNotification.Name.init(NetworkConnectionChanged), object: nil)
}
/// Dictionary of operations, keyed by the `Download URL` of the `URLSessionTask`
fileprivate var operations = [URL: SCDownloadOperation]()
fileprivate var serverURLRequestMap = [URL: SCDownloadOperation]()
/// Serial NSOperationQueue for downloads
private let queue: OperationQueue = {
let _queue = OperationQueue()
_queue.name = "SCDownloadManagerQueue"
_queue.maxConcurrentOperationCount = 3
return _queue
}()
/// Delegate-based NSURLSession for DownloadManager
lazy var downloadSession: URLSession = {
let configuration = URLSessionConfiguration.background(withIdentifier: "com.xyz.background.download")
let sessionQueue = OperationQueue()
return URLSession(configuration: configuration, delegate: self, delegateQueue: sessionQueue)
}()
lazy var dataSession: URLSession = {
let configuration = URLSessionConfiguration.background(withIdentifier: "com.xyz.background.url")
let sessionQueue = OperationQueue()
return URLSession(configuration: configuration, delegate: self, delegateQueue: sessionQueue)
}()
#discardableResult
#objc func addDownload(_ downloadRecord: DownloadRecord) -> SCDownloadOperation {
let operation = SCDownloadOperation(withDownloadRecord: downloadRecord)
operation.delegate = self
operations[downloadRecord.downloadUrl] = operation
queue.addOperation(operation)
return operation
}
/// Cancel all queued operations
#objc func cancelAll() {
queue.cancelAllOperations()
}
#objc func networkDidChange(_ notification: Notification) {
if let reachability = notification.object as? Reachability {
if reachability.currentReachabilityStatus() == NotReachable {
cancelAll()
}
}
}
}
//MARK:- Download operation delegate
extension SCDownloadManager: SCDownloadOperationDelegate {
func getServerDownloadUrl(forRequest request: URLRequest, ofOperation operation: SCDownloadOperation) {
if let url = request.url {
serverURLRequestMap[url] = operation
}
self.dataSession.downloadTask(with: request).resume()
}
}
// MARK: URLSessionDownloadDelegate methods
extension SCDownloadManager: URLSessionDownloadDelegate {
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
guard let sourceURL = downloadTask.originalRequest?.url else { return }
if session == dataSession {
if FileManager.default.isReadableFile(atPath: location.path) {
if let data = FileManager.default.contents(atPath: location.path) {
if let downloadOperation = serverURLRequestMap[sourceURL] {
if let serverUrl = String(data: data, encoding: .utf8), let downloadServerUrl = URL(string: serverUrl) {
self.operations.changeKey(from: downloadOperation.downloadRecord.downloadUrl, to: downloadServerUrl)
downloadOperation.downloadRecord.downloadUrl = downloadServerUrl
if let url = downloadOperation.downloadRecord.downloadUrl {
downloadOperation.downloadRecord.downloadTask = downloadSession.downloadTask(with: url)
downloadOperation.downloadRecord.downloadTask.resume()
}
}
}
do {
try FileManager.default.removeItem(at: location)
} catch {
print("error while removing file from default location")
}
}
}
} else {
operations[sourceURL]?.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location)
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
if session == self.downloadSession {
guard let sourceURL = downloadTask.originalRequest?.url else { return }
operations[sourceURL]?.urlSession(session, downloadTask: downloadTask, didWriteData: bytesWritten, totalBytesWritten: totalBytesWritten, totalBytesExpectedToWrite: totalBytesExpectedToWrite)
}
}
}
// MARK: URLSessionTaskDelegate methods
extension SCDownloadManager: URLSessionTaskDelegate {
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
if downloadSession == session {
DispatchQueue.main.async {
if let completionHandler = Constants.appDelegate.backgroundTransferCompletionHandler {
Constants.appDelegate.backgroundTransferCompletionHandler = nil
completionHandler()
}
}
}
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
guard let sourceURL = task.originalRequest?.url else { return }
if session == dataSession {
if error == nil {
} else {
print("got error while doing API on download task")
}
} else {
operations[sourceURL]?.urlSession(session, task: task, didCompleteWithError: error)
operations.removeValue(forKey: sourceURL)
}
}
}
Operation Class :
class SCDownloadOperation: SCAsynchronousOperation {
let downloadRecord: DownloadRecord
var getServerDownloadUrl: ((URLRequest)->Void)?
weak var delegate: SCDownloadOperationDelegate?
init(withDownloadRecord dr: DownloadRecord) {
downloadRecord = dr
super.init()
}
override func cancel() {
if let task = downloadRecord.downloadTask{
task.cancel()
}
super.cancel()
}
override func main() {
switch downloadRecord.downloadDataType {
case DOWNLOAD_DATA_TYPE_CONTENT:
let configuration = RequestConfiguration(withMethodType: .GET, urlString: downloadRecord.downloadUrl.absoluteString)
if let request = NetworkManager.sharedInstance.urlRequest(forRequestConfiguration: configuration) {
delegate?.getServerDownloadUrl(forRequest: request, ofOperation: self)
}
default: break
}
}
}
// MARK: NSURLSessionDownloadDelegate methods
extension SCDownloadOperation: URLSessionDownloadDelegate {
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
downloadRecord.progress = 1
switch downloadRecord.downloadDataType {
case DOWNLOAD_DATA_TYPE_CONTENT:
processDownloadFinish(atLocation: location)
default: break
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
downloadRecord.isExecuting = true
downloadRecord.isDownloadProgressAvailable = true
let downloadProgress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
downloadRecord.progress = downloadProgress
downloadRecord.percentComplete = ((Float(totalBytesWritten) / Float(totalBytesExpectedToWrite))*100)
downloadRecord.downloadProgress = downloadProgress
switch downloadRecord.downloadDataType {
case DOWNLOAD_DATA_TYPE_CONTENT:
updateWrittenData()
default: break
}
}
}
// MARK: NSURLSessionTaskDelegate methods
extension SCDownloadOperation: URLSessionTaskDelegate {
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
completeOperation()
if error != nil {
print("\(String(describing: error))")
switch downloadRecord.downloadDataType {
case DOWNLOAD_DATA_TYPE_CONTENT:
contentDownloadFail()
default: break
}
}
}
}

UIdocumentInteractionController cant display the PDF file in swift 4

class ViewController: UIViewController,UIDocumentInteractionControllerDelegate
{
let documentInteractionController = UIDocumentInteractionController(url: URL(fileURLWithPath: path))
documentInteractionController.delegate = self
documentInteractionController.presentPreview(animated: true)
}
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController
{
return self
}
above code i try on my apps, when click left menu , not able to show the pdf file. anyone got idea ?
I am showing here an example for download pdf from url link displaying in UIdocumentInteractionController in swift 4 as per your requirement
class ExampleViewController: UIViewController, URLSessionDownloadDelegate, UIDocumentInteractionControllerDelegate {
let urlLink = "sample link";
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnDownArrowOnPressed(_ sender: UIButton) {
if(urlLink != nil){
if(urlLink != nil && urlLink != "Null" && urlLink != ""){
let backgroundSessionConfiguration = URLSessionConfiguration.background(withIdentifier: "backgroundSession")
backgroundSession = Foundation.URLSession(configuration: backgroundSessionConfiguration, delegate: self, delegateQueue: OperationQueue.main)
let url = URL(string: urlLink!
downloadTask = backgroundSession.downloadTask(with: url)
downloadTask.resume()
}else{
self.view.makeToast("You don't have any certificate to download.")
}
}
else{
self.view.makeToast("You don't have any certificate to download.")
}
}
func showFileWithPath(path: String){
let isFileFound:Bool? = FileManager.default.fileExists(atPath: path)
if isFileFound == true{
let viewer = UIDocumentInteractionController(url: URL(fileURLWithPath: path))
viewer.delegate = self
viewer.presentPreview(animated: true)
}
}
func urlSession(_ session: URLSession,
downloadTask: URLSessionDownloadTask,
didFinishDownloadingTo location: URL){
let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let documentDirectoryPath:String = path[0]
let fileManager = FileManager()
let destinationURLForFile = URL(fileURLWithPath: documentDirectoryPath.appendingFormat("/Certificate.pdf"))
if fileManager.fileExists(atPath: destinationURLForFile.path){
showFileWithPath(path: destinationURLForFile.path)
}
else{
do {
try fileManager.moveItem(at: location, to: destinationURLForFile)
// show file
showFileWithPath(path: destinationURLForFile.path)
}catch{
print("An error occurred while moving file to destination url")
}
}
}
// 2
func urlSession(_ session: URLSession,
downloadTask: URLSessionDownloadTask,
didWriteData bytesWritten: Int64,
totalBytesWritten: Int64,
totalBytesExpectedToWrite: Int64){
}
//MARK: URLSessionTaskDelegate
func urlSession(_ session: URLSession,
task: URLSessionTask,
didCompleteWithError error: Error?){
downloadTask = nil
if (error != nil) {
print(error!.localizedDescription)
}else{
self.view.hideToastActivity()
print("The task finished transferring data successfully")
}
}
//MARK: UIDocumentInteractionControllerDelegate
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController
{
UINavigationBar.appearance().tintColor = UIColor.white
return self
}
}
Use URL(string: path) instead of URL(fileURLWithPath: path)

get progress from dataTaskWithURL in swift

Is there any way to get progress from dataTaskWithURL in swift while the data is downloading?
NSURLSession.sharedSession().dataTaskWithURL(...)
I need to show progress bar while the data is downloading.
You can simply observe progress property of the URLSessionDataTask object.
Example:
import UIKit
class SomeViewController: UIViewController {
private var observation: NSKeyValueObservation?
deinit {
observation?.invalidate()
}
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://source.unsplash.com/random/4000x4000")!
let task = URLSession.shared.dataTask(with: url)
observation = task.progress.observe(\.fractionCompleted) { progress, _ in
print("progress: ", progress.fractionCompleted)
}
task.resume()
}
}
Playground example:
import Foundation
import PlaygroundSupport
let page = PlaygroundPage.current
page.needsIndefiniteExecution = true
let url = URL(string: "https://source.unsplash.com/random/4000x4000")!
let task = URLSession.shared.dataTask(with: url) { _, _, _ in
page.finishExecution()
}
// Don't forget to invalidate the observation when you don't need it anymore.
let observation = task.progress.observe(\.fractionCompleted) { progress, _ in
print(progress.fractionCompleted)
}
task.resume()
you can use this code for showing download process with progress bar with its delegate functions.
import UIKit
class ViewController: UIViewController,NSURLSessionDelegate,NSURLSessionDataDelegate{
#IBOutlet weak var progress: UIProgressView!
var buffer:NSMutableData = NSMutableData()
var session:NSURLSession?
var dataTask:NSURLSessionDataTask?
let url = NSURL(string:"http://i.stack.imgur.com/b8zkg.png" )!
var expectedContentLength = 0
override func viewDidLoad() {
super.viewDidLoad()
progress.progress = 0.0
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let manqueue = NSOperationQueue.mainQueue()
session = NSURLSession(configuration: configuration, delegate:self, delegateQueue: manqueue)
dataTask = session?.dataTaskWithRequest(NSURLRequest(URL: url))
dataTask?.resume()
// Do any additional setup after loading the view, typically from a nib.
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void) {
//here you can get full lenth of your content
expectedContentLength = Int(response.expectedContentLength)
println(expectedContentLength)
completionHandler(NSURLSessionResponseDisposition.Allow)
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
buffer.appendData(data)
let percentageDownloaded = Float(buffer.length) / Float(expectedContentLength)
progress.progress = percentageDownloaded
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
//use buffer here.Download is done
progress.progress = 1.0 // download 100% complete
}
}
Update for Swift4:
Supports execution of multiple simultaneous operations.
File: DownloadService.swift. Keeps reference to URLSession and tracks executing tasks.
final class DownloadService: NSObject {
private var session: URLSession!
private var downloadTasks = [GenericDownloadTask]()
public static let shared = DownloadService()
private override init() {
super.init()
let configuration = URLSessionConfiguration.default
session = URLSession(configuration: configuration,
delegate: self, delegateQueue: nil)
}
func download(request: URLRequest) -> DownloadTask {
let task = session.dataTask(with: request)
let downloadTask = GenericDownloadTask(task: task)
downloadTasks.append(downloadTask)
return downloadTask
}
}
extension DownloadService: URLSessionDataDelegate {
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse,
completionHandler: #escaping (URLSession.ResponseDisposition) -> Void) {
guard let task = downloadTasks.first(where: { $0.task == dataTask }) else {
completionHandler(.cancel)
return
}
task.expectedContentLength = response.expectedContentLength
completionHandler(.allow)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
guard let task = downloadTasks.first(where: { $0.task == dataTask }) else {
return
}
task.buffer.append(data)
let percentageDownloaded = Double(task.buffer.count) / Double(task.expectedContentLength)
DispatchQueue.main.async {
task.progressHandler?(percentageDownloaded)
}
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
guard let index = downloadTasks.index(where: { $0.task == task }) else {
return
}
let task = downloadTasks.remove(at: index)
DispatchQueue.main.async {
if let e = error {
task.completionHandler?(.failure(e))
} else {
task.completionHandler?(.success(task.buffer))
}
}
}
}
File: DownloadTask.swift. Lightweight interface just to hide concrete implementation.
protocol DownloadTask {
var completionHandler: ResultType<Data>.Completion? { get set }
var progressHandler: ((Double) -> Void)? { get set }
func resume()
func suspend()
func cancel()
}
File: GenericDownloadTask.swift. Concrete implementation of DownloadTask interface.
class GenericDownloadTask {
var completionHandler: ResultType<Data>.Completion?
var progressHandler: ((Double) -> Void)?
private(set) var task: URLSessionDataTask
var expectedContentLength: Int64 = 0
var buffer = Data()
init(task: URLSessionDataTask) {
self.task = task
}
deinit {
print("Deinit: \(task.originalRequest?.url?.absoluteString ?? "")")
}
}
extension GenericDownloadTask: DownloadTask {
func resume() {
task.resume()
}
func suspend() {
task.suspend()
}
func cancel() {
task.cancel()
}
}
File: ResultType.swift. Reusable type to keep result or error.
public enum ResultType<T> {
public typealias Completion = (ResultType<T>) -> Void
case success(T)
case failure(Swift.Error)
}
Usage: Example how to run two download tasks in parallel (macOS App):
class ViewController: NSViewController {
#IBOutlet fileprivate weak var loadImageButton1: NSButton!
#IBOutlet fileprivate weak var loadProgressIndicator1: NSProgressIndicator!
#IBOutlet fileprivate weak var imageView1: NSImageView!
#IBOutlet fileprivate weak var loadImageButton2: NSButton!
#IBOutlet fileprivate weak var loadProgressIndicator2: NSProgressIndicator!
#IBOutlet fileprivate weak var imageView2: NSImageView!
fileprivate var downloadTask1: DownloadTask?
fileprivate var downloadTask2: DownloadTask?
override func viewDidLoad() {
super.viewDidLoad()
loadImageButton1.target = self
loadImageButton1.action = #selector(startDownload1(_:))
loadImageButton2.target = self
loadImageButton2.action = #selector(startDownload2(_:))
}
}
extension ViewController {
#objc fileprivate func startDownload1(_ button: NSButton) {
let url = URL(string: "http://localhost:8001/?imageID=01&tilestamp=\(Date.timeIntervalSinceReferenceDate)")!
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 30)
downloadTask1 = DownloadService.shared.download(request: request)
downloadTask1?.completionHandler = { [weak self] in
switch $0 {
case .failure(let error):
print(error)
case .success(let data):
print("Number of bytes: \(data.count)")
self?.imageView1.image = NSImage(data: data)
}
self?.downloadTask1 = nil
self?.loadImageButton1.isEnabled = true
}
downloadTask1?.progressHandler = { [weak self] in
print("Task1: \($0)")
self?.loadProgressIndicator1.doubleValue = $0
}
loadImageButton1.isEnabled = false
imageView1.image = nil
loadProgressIndicator1.doubleValue = 0
downloadTask1?.resume()
}
#objc fileprivate func startDownload2(_ button: NSButton) {
let url = URL(string: "http://localhost:8002/?imageID=02&tilestamp=\(Date.timeIntervalSinceReferenceDate)")!
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 30)
downloadTask2 = DownloadService.shared.download(request: request)
downloadTask2?.completionHandler = { [weak self] in
switch $0 {
case .failure(let error):
print(error)
case .success(let data):
print("Number of bytes: \(data.count)")
self?.imageView2.image = NSImage(data: data)
}
self?.downloadTask2 = nil
self?.loadImageButton2.isEnabled = true
}
downloadTask2?.progressHandler = { [weak self] in
print("Task2: \($0)")
self?.loadProgressIndicator2.doubleValue = $0
}
loadImageButton2.isEnabled = false
imageView2.image = nil
loadProgressIndicator2.doubleValue = 0
downloadTask2?.resume()
}
}
Bonus 1. File StartPHPWebServer.command. Example script to run 2 Build-in PHP servers to simulate simultaneous downloads.
#!/bin/bash
AWLScriptDirPath=$(cd "$(dirname "$0")"; pwd)
cd "$AWLScriptDirPath"
php -S localhost:8001 &
php -S localhost:8002 &
ps -afx | grep php
echo "Press ENTER to exit."
read
killall php
Bonus 2. File index.php. Example PHP script to implement slow download.
<?php
$imageID = $_REQUEST["imageID"];
$local_file = "Image-$imageID.jpg";
$download_rate = 20.5; // set the download rate limit (=> 20,5 kb/s)
if (file_exists($local_file) && is_file($local_file)) {
header('Cache-control: private');
header('Content-Type: image/jpeg');
header('Content-Length: '.filesize($local_file));
flush();
$file = fopen($local_file, "r");
while(!feof($file)) {
// send the current file part to the browser
print fread($file, round($download_rate * 1024));
flush(); // flush the content to the browser
usleep(0.25 * 1000000);
}
fclose($file);}
else {
die('Error: The file '.$local_file.' does not exist!');
}
?>
Misc: Contents of directory which simulates 2 PHP servers.
Image-01.jpg
Image-02.jpg
StartPHPWebServer.command
index.php
in class declare
class AudioPlayerViewController: UIViewController, URLSessionDelegate, URLSessionDataDelegate, URLSessionDownloadDelegate{
var defaultSession: URLSession!
var downloadTask: URLSessionDownloadTask!
in download method
defaultSession = Foundation.URLSession(configuration: .default, delegate: self, delegateQueue: nil)
downloadProgress.setProgress(0.0, animated: false)
downloadTask = defaultSession.downloadTask(with: audioUrl)
downloadTask.Resume()
And add following delegates:
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
DispatchQueue.main.async {
self.downloadProgressBar.setProgress(Float(totalBytesWritten)/Float(totalBytesExpectedToWrite), animated: true)
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
DispatchQueue.main.async {
//DOWNLOAD SUCCESSFUL AND FILE PATH WILL BE IN URL.
}
}
In reference to Dharmesh's work, there have been quite a number of minor changes with URLSessionDelegate and URLSessionDataDelegate delegate methods. Here come the code for Swift 4.2 compatibility.
import UIKit
class ViewController: UIViewController, URLSessionDelegate, URLSessionDataDelegate {
// MARK: - Variables
// MARK: - IBOutlet
#IBOutlet weak var progress: UIProgressView!
// MARK: - IBAction
#IBAction func goTapped(_ sender: UIButton) {
let url = URL(string: "http://www.example.com/file.zip")!
fetchFile(url: url)
}
// MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
}
func fetchFile(url: URL) {
progress.progress = 0.0
let configuration = URLSessionConfiguration.default
let mainQueue = OperationQueue.main
session = URLSession(configuration: configuration, delegate: self, delegateQueue: mainQueue)
dataTask = session?.dataTask(with: URLRequest(url: url))
dataTask?.resume()
}
var buffer: NSMutableData = NSMutableData()
var session: URLSession?
var dataTask: URLSessionDataTask?
var expectedContentLength = 0
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
buffer.append(data)
let percentageDownloaded = Float(buffer.length) / Float(expectedContentLength)
progress.progress = percentageDownloaded
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: (URLSession.ResponseDisposition) -> Void) {
expectedContentLength = Int(response.expectedContentLength)
completionHandler(URLSession.ResponseDisposition.allow)
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
progress.progress = 1.0
}
}
for the data been download you need to set the NSURLSessionDownloadDelegate and to implement URLSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)
There is a nice tutorial about it here but is in object-c.

Resources