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, RemoteGridViewControlle
r 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()
}
}