0

I'm using URLSessionDataTask to show an image, and using URLSessionDownloadTask to download the image on my app. Also I needed to observe download progress, so I use KVO at Progress's fractionCompleted.

The thing is fractionCompleted value just has 0.05 and 1.0. I think this issue happened for using same remote url. Because when I don't use URLSessionDataTask(I can't show the image though), I get right progress update.

Why is it happened? Is there any reference?

Edited

First, I don't want to use URLSession delegate to get progress. I'm using two VCs(first one is RemoteGridViewController using URLSessionDataTask to show image and second one is MediaViewerController). MediaViewerController delegate to first VC for downloading the image on camera roll. I know I can use URLSession delegate method to notify progress by using NotificationCenter or something like that, but I don't want. This way makes strong coupling(?) between the VCs(I guess...)

Second, RemoteGridViewController use URLSession to show an image by URLSessionDataTask and cache it.

guard let url = URL(string: url) else {
    completionHandler(.failure(NetworkManagerError.urlError))
    return
}

var req = URLRequest(url: url)
req.cachePolicy = .returnCacheDataElseLoad

if let cachedResponse = sharedCache?.cachedResponse(for: req) {
    completionHandler(.success(cachedResponse.data))
} else {
    sessionDataTask = session.dataTask(with: req) { [weak self] (data, response, error) in
        if let error = error {
            completionHandler(.failure(error))
            print(error.localizedDescription)
            return
        }
        
        guard let response = response as? HTTPURLResponse, let data = data else {
            completionHandler(.failure(NetworkManagerError.dataError))
            return
        }
        self?.sharedCache?.storeCachedResponse(CachedURLResponse(response: response, data: data), for: req)
        completionHandler(.success(data))
    }
    
    sessionDataTask?.resume()
}

Third, MediaViewerController call MediaViewerDelegate method, which is downloadImage(itemAt:for:) to download an image.

extension RemoteGridViewController: MediaViewerDelegate {
    func downloadImage(itemAt indexPath: IndexPath, for mediaViewer: MediaViewerController) {
        let data = unsplashData[indexPath.item]
        let urlString = data.regular
        
        guard let url = URL(string: urlString) else { return }
        
        mediaViewer.presentProgressView()

        networkManager.downloadTaskForImage(with: url, progressHandler: mediaViewer.observe(_:)) { result in
            switch result {
            case .success(let image):
                DispatchQueue.main.async {
                    mediaViewer.toastView(with: true)
                }
                UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
            case .failure(let error):
                DispatchQueue.main.async {
                    mediaViewer.toastView(with: false)
                }
                print(error.localizedDescription)
            }
        }
    }
}

class NetworkManager {
    private let session: URLSession!
    private var sessionDownloadTask: URLSessionDownloadTask?
    
    func downloadTaskForImage(with url: URL, progressHandler: (Progress?) -> (), completionHandler: @escaping (Result<UIImage, Error>) -> ()) {
        sessionDownloadTask = session.downloadTask(with: url) { (location, response, error) in
            if let error = error {
                completionHandler(.failure(error))
                return
            }
            
            guard let response = response as? HTTPURLResponse, (200...299).contains(response.statusCode) else {
                completionHandler(.failure(NetworkManagerError.responseError))
                return
            }
            
            guard let location = location else {
                completionHandler(.failure(NetworkManagerError.urlError))
                return
            }
            
            do {
                let data = try Data(contentsOf: location)
                if let image = UIImage(data: data) {
                    completionHandler(.success(image))
                } else {
                    completionHandler(.failure(NetworkManagerError.dataError))
                }
            } catch let error {
                completionHandler(.failure(error))
            }
        }
        
        progressHandler(sessionDownloadTask?.progress)
        sessionDownloadTask?.resume()
    }
}

InTaek Cho
  • 41
  • 5
  • @Rob Oh, my mistake. I edited the post. – InTaek Cho Jan 19 '21 at 09:26
  • Still more clarity is needed to understand your question. At what place you need to measure the progress of downloading the image? – Nirmit Dagly Jan 19 '21 at 10:30
  • @NirmitDagly MediaViewerController present progressView, so this progressView need to measure the progress. That's why I use `mediaViewer.observe(_:)` on `downloadTaskForImage(with:progressHandler:completionHandler:)`. To be clear, feel free to ask. Thanks. – InTaek Cho Jan 19 '21 at 10:47

1 Answers1

0

You can track for the downloaded bytes to total expected bytes to track the progress of downloading image.

Use following delegate method:

URLSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)

and following snippet:

func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
        // println("download task did write data")

        let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
        
        //Your code to download and save image to camera role
    }
}

I hope this may have resolved your query to check for exact progress with downloading.

Nirmit Dagly
  • 1,272
  • 1
  • 12
  • 25
  • Thanks for reply, but **I said** I don't want to use `URLSession delegate`. I used it before actually, but I thought there is no way to notify progress to `DownloadAlertController` other than `NotificationCenter`, and using `NotificationCenter` doesn't fit what I was going for. Can you explain how you intended to notify that progress to other VC? – InTaek Cho Jan 19 '21 at 12:07