Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix one crash, safer self, add finished task in background #81

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 57 additions & 45 deletions MZDownloadManager/Classes/MZDownloadManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@

import UIKit
fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l < r
case (nil, _?):
return true
default:
return false
}
switch (lhs, rhs) {
case let (l?, r?):
return l < r
case (nil, _?):
return true
default:
return false
}
}

fileprivate func > <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l > r
default:
return rhs < lhs
}
switch (lhs, rhs) {
case let (l?, r?):
return l > r
default:
return rhs < lhs
}
}


Expand Down Expand Up @@ -75,7 +75,7 @@ open class MZDownloadManager: NSObject {
fileprivate weak var delegate: MZDownloadManagerDelegate?

open var downloadingArray: [MZDownloadModel] = []

public convenience init(session sessionIdentifer: String, delegate: MZDownloadManagerDelegate, sessionConfiguration: URLSessionConfiguration? = nil, completion: (() -> Void)? = nil) {
self.init()
self.delegate = delegate
Expand Down Expand Up @@ -135,6 +135,9 @@ extension MZDownloadManager {
} else if(downloadTask.state == .suspended) {
downloadModel.status = TaskStatus.paused.description()
downloadingArray.append(downloadModel)
} else if(downloadTask.state == .completed) {
downloadModel.status = TaskStatus.downloading.description()
downloadingArray.append(downloadModel)
} else {
downloadModel.status = TaskStatus.failed.description()
}
Expand All @@ -143,26 +146,29 @@ extension MZDownloadManager {

fileprivate func isValidResumeData(_ resumeData: Data?) -> Bool {

guard resumeData != nil || resumeData?.count > 0 else {
guard let resumeData = resumeData, resumeData.count > 0 else {
return false
}

do {
var resumeDictionary : AnyObject!
resumeDictionary = try PropertyListSerialization.propertyList(from: resumeData!, options: PropertyListSerialization.MutabilityOptions(), format: nil) as AnyObject
var localFilePath = (resumeDictionary?["NSURLSessionResumeInfoLocalPath"] as? String)

if localFilePath == nil || localFilePath?.count < 1 {
localFilePath = (NSTemporaryDirectory() as String) + (resumeDictionary["NSURLSessionResumeInfoTempFileName"] as! String)
guard let resumeDictionary = try? PropertyListSerialization.propertyList(from: resumeData, options: PropertyListSerialization.MutabilityOptions(), format: nil) as AnyObject else {
return false
}

var localFilePath: String? = resumeDictionary["NSURLSessionResumeInfoLocalPath"] as? String

if (localFilePath?.isEmpty ?? true) {
guard let filename = resumeDictionary["NSURLSessionResumeInfoTempFileName"] as? String else {
return false
}

let fileManager : FileManager! = FileManager.default
debugPrint("resume data file exists: \(fileManager.fileExists(atPath: localFilePath! as String))")
return fileManager.fileExists(atPath: localFilePath! as String)
} catch let error as NSError {
debugPrint("resume data is nil: \(error)")
localFilePath = NSTemporaryDirectory() + filename
}
guard let tempfile = localFilePath else {
return false
}
let fileManager: FileManager = FileManager.default
debugPrint("resume data file exists: \(fileManager.fileExists(atPath: tempfile))")

return fileManager.fileExists(atPath: tempfile)
}
}

Expand All @@ -171,21 +177,28 @@ extension MZDownloadManager: URLSessionDownloadDelegate {
public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
for (index, downloadModel) in self.downloadingArray.enumerated() {
if downloadTask.isEqual(downloadModel.task) {
DispatchQueue.main.async(execute: { () -> Void in
DispatchQueue.main.async(execute: {[weak self] () -> Void in
guard let self = self else {
return
}
let taskStartedDate = downloadModel.startTime ?? Date()
let timeInterval = taskStartedDate.timeIntervalSinceNow
let downloadTime = TimeInterval(-1 * timeInterval)

let receivedBytesCount = Double(downloadTask.countOfBytesReceived)
let totalBytesCount = Double(downloadTask.countOfBytesExpectedToReceive)
let progress = Float(receivedBytesCount / totalBytesCount)

let taskStartedDate = downloadModel.startTime ?? Date()
let timeInterval = taskStartedDate.timeIntervalSinceNow
let downloadTime = TimeInterval(-1 * timeInterval)
guard totalBytesWritten != 0 && downloadTime != 0 else {
return
}

let speed = Float(totalBytesWritten) / Float(downloadTime)
let progress = Float(receivedBytesCount / totalBytesCount)
let speedFloat = Float(totalBytesWritten) / Float(downloadTime)
let speed = max(1, Int64(speedFloat))

let remainingContentLength = totalBytesExpectedToWrite - totalBytesWritten

let remainingTime = remainingContentLength / Int64(speed)
let remainingTime = remainingContentLength / speed
let hours = Int(remainingTime) / 3600
let minutes = (Int(remainingTime) - hours * 3600) / 60
let seconds = Int(remainingTime) - hours * 3600 - minutes * 60
Expand All @@ -196,8 +209,8 @@ extension MZDownloadManager: URLSessionDownloadDelegate {
let downloadedFileSize = MZUtility.calculateFileSizeInUnit(totalBytesWritten)
let downloadedSizeUnit = MZUtility.calculateUnit(totalBytesWritten)

let speedSize = MZUtility.calculateFileSizeInUnit(Int64(speed))
let speedUnit = MZUtility.calculateUnit(Int64(speed))
let speedSize = MZUtility.calculateFileSizeInUnit(speed)
let speedUnit = MZUtility.calculateUnit(speed)

downloadModel.remainingTime = (hours, minutes, seconds)
downloadModel.file = (totalFileSize, totalFileSizeUnit as String)
Expand Down Expand Up @@ -229,21 +242,19 @@ extension MZDownloadManager: URLSessionDownloadDelegate {
if fileManager.fileExists(atPath: basePath) {
let fileURL = URL(fileURLWithPath: destinationPath as String)
debugPrint("directory path = \(destinationPath)")

do {
try fileManager.moveItem(at: location, to: fileURL)
} catch let error as NSError {
debugPrint("Error while moving downloaded file to destination path:\(error)")
DispatchQueue.main.async(execute: { () -> Void in
self.delegate?.downloadRequestDidFailedWithError?(error, downloadModel: downloadModel, index: index)
DispatchQueue.main.async(execute: {[weak self] () -> Void in
self?.delegate?.downloadRequestDidFailedWithError?(error, downloadModel: downloadModel, index: index)
})
}
} else {
//Opportunity to handle the folder doesnot exists error appropriately.
//Move downloaded file to destination
//Delegate will be called on the session queue
//Otherwise blindly give error Destination folder does not exists

if let _ = self.delegate?.downloadRequestDestinationDoestNotExists {
self.delegate?.downloadRequestDestinationDoestNotExists?(downloadModel, index: index, location: location)
} else {
Expand All @@ -261,8 +272,10 @@ extension MZDownloadManager: URLSessionDownloadDelegate {
debugPrint("task id: \(task.taskIdentifier)")
/***** Any interrupted tasks due to any reason will be populated in failed state after init *****/

DispatchQueue.main.async {

DispatchQueue.main.async {[weak self] in
guard let self = self else {
return
}
let err = error as NSError?

if (err?.userInfo[NSURLErrorBackgroundTaskCancelledReasonKey] as? NSNumber)?.intValue == NSURLErrorCancelledReasonUserForceQuitApplication || (err?.userInfo[NSURLErrorBackgroundTaskCancelledReasonKey] as? NSNumber)?.intValue == NSURLErrorCancelledReasonBackgroundUpdatesDisabled {
Expand Down Expand Up @@ -417,7 +430,6 @@ extension MZDownloadManager {
downloadModel.status = TaskStatus.downloading.description()

downloadingArray[index] = downloadModel

delegate?.downloadRequestDidResumed?(downloadModel, index: index)
}

Expand Down