diff --git a/.swift-version b/.swift-version
new file mode 100644
index 0000000..bf77d54
--- /dev/null
+++ b/.swift-version
@@ -0,0 +1 @@
+4.2
diff --git a/SDDownloadManager.podspec b/SDDownloadManager.podspec
index e221387..ebfbdf6 100644
--- a/SDDownloadManager.podspec
+++ b/SDDownloadManager.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'SDDownloadManager'
-s.version = '1.0.3'
+s.version = '1.1.0'
s.summary = 'A simple, robust and elegant download manager written in Swift'
s.description = <<-DESC
@@ -12,7 +12,9 @@ s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'Sagar Dagdu' => 'shags032@gmail.com' }
s.source = { :git => 'https://github.com/SagarSDagdu/SDDownloadManager.git', :tag => s.version.to_s }
-s.ios.deployment_target = '11.2'
+s.swift_version = '4.2'
+s.ios.deployment_target = '10.0'
+s.framework = 'UserNotifications'
s.source_files = 'SDDownloadManager/Classes/*.swift'
end
diff --git a/SDDownloadManager.xcodeproj/project.pbxproj b/SDDownloadManager.xcodeproj/project.pbxproj
index afc6bdd..28ac302 100644
--- a/SDDownloadManager.xcodeproj/project.pbxproj
+++ b/SDDownloadManager.xcodeproj/project.pbxproj
@@ -116,7 +116,13 @@
TargetAttributes = {
ACE3B6801F35D5BA00684BE6 = {
CreatedOnToolsVersion = 8.3.3;
+ LastSwiftMigration = 1010;
ProvisioningStyle = Automatic;
+ SystemCapabilities = {
+ com.apple.BackgroundModes = {
+ enabled = 0;
+ };
+ };
};
};
};
@@ -289,7 +295,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.sagar.thinkinfinite.SDDownloadManager;
PRODUCT_NAME = "$(TARGET_NAME)";
- SWIFT_VERSION = 4.0;
+ SWIFT_VERSION = 4.2;
};
name = Debug;
};
@@ -302,7 +308,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.sagar.thinkinfinite.SDDownloadManager;
PRODUCT_NAME = "$(TARGET_NAME)";
- SWIFT_VERSION = 4.0;
+ SWIFT_VERSION = 4.2;
};
name = Release;
};
diff --git a/SDDownloadManager.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SDDownloadManager.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/SDDownloadManager.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/SDDownloadManager.xcodeproj/project.xcworkspace/xcuserdata/sagardagdu.xcuserdatad/UserInterfaceState.xcuserstate b/SDDownloadManager.xcodeproj/project.xcworkspace/xcuserdata/sagardagdu.xcuserdatad/UserInterfaceState.xcuserstate
index 6ba56b4..44b7f76 100644
Binary files a/SDDownloadManager.xcodeproj/project.xcworkspace/xcuserdata/sagardagdu.xcuserdatad/UserInterfaceState.xcuserstate and b/SDDownloadManager.xcodeproj/project.xcworkspace/xcuserdata/sagardagdu.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/SDDownloadManager.xcodeproj/xcuserdata/sagardagdu.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/SDDownloadManager.xcodeproj/xcuserdata/sagardagdu.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
new file mode 100644
index 0000000..fe2b454
--- /dev/null
+++ b/SDDownloadManager.xcodeproj/xcuserdata/sagardagdu.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -0,0 +1,5 @@
+
+
+
diff --git a/SDDownloadManager/AppDelegate.swift b/SDDownloadManager/AppDelegate.swift
index b38ee11..fd9e8a1 100644
--- a/SDDownloadManager/AppDelegate.swift
+++ b/SDDownloadManager/AppDelegate.swift
@@ -24,6 +24,7 @@
// THE SOFTWARE.
import UIKit
+import UserNotifications
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
@@ -31,8 +32,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
- func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
+ func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
+ let center = UNUserNotificationCenter.current()
+ // Request permission to display alerts and play sounds.
+ center.requestAuthorization(options: [.alert, .sound])
+ { (granted, error) in
+ // Enable or disable features based on authorization.
+ }
return true
}
@@ -58,6 +65,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
-
+ func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
+ debugPrint("handleEventsForBackgroundURLSession: \(identifier)")
+ SDDownloadManager.shared.backgroundCompletionHandler = completionHandler
+ }
}
diff --git a/SDDownloadManager/Classes/SDDownloadManager.swift b/SDDownloadManager/Classes/SDDownloadManager.swift
index 9e05a76..d55fb4e 100644
--- a/SDDownloadManager/Classes/SDDownloadManager.swift
+++ b/SDDownloadManager/Classes/SDDownloadManager.swift
@@ -24,33 +24,46 @@
// THE SOFTWARE.
import UIKit
+import UserNotifications
final public class SDDownloadManager: NSObject {
public typealias DownloadCompletionBlock = (_ error : Error?, _ fileUrl:URL?) -> Void
public typealias DownloadProgressBlock = (_ progress : CGFloat) -> Void
+ public typealias BackgroundDownloadCompletionHandler = () -> Void
// MARK :- Properties
- var session: URLSession = URLSession()
- var ongoingDownloads: [String : SDDownloadObject] = [:]
+ private var session: URLSession!
+ private var ongoingDownloads: [String : SDDownloadObject] = [:]
+ private var backgroundSession: URLSession!
+
+ public var backgroundCompletionHandler: BackgroundDownloadCompletionHandler?
+ public var showLocalNotificationOnBackgroundDownloadDone = true
+ public var localNotificationText: String?
public static let shared: SDDownloadManager = { return SDDownloadManager() }()
//MARK:- Public methods
- public func dowloadFile(withRequest request: URLRequest,
+ public func downloadFile(withRequest request: URLRequest,
inDirectory directory: String? = nil,
withName fileName: String? = nil,
+ shouldDownloadInBackground: Bool = false,
onProgress progressBlock:DownloadProgressBlock? = nil,
onCompletion completionBlock:@escaping DownloadCompletionBlock) -> String? {
if let _ = self.ongoingDownloads[(request.url?.absoluteString)!] {
- print("Already in progress")
+ debugPrint("Already in progress")
return nil
}
+ var downloadTask: URLSessionDownloadTask
+ if shouldDownloadInBackground {
+ downloadTask = self.backgroundSession.downloadTask(with: request)
+ } else{
+ downloadTask = self.session.downloadTask(with: request)
+ }
- let downloadTask = self.session.downloadTask(with: request)
let download = SDDownloadObject(downloadTask: downloadTask,
progressBlock: progressBlock,
completionBlock: completionBlock,
@@ -109,6 +122,8 @@ final public class SDDownloadManager: NSObject {
super.init()
let sessionConfiguration = URLSessionConfiguration.default
self.session = URLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: nil)
+ let backgroundConfiguration = URLSessionConfiguration.background(withIdentifier: Bundle.main.bundleIdentifier!)
+ self.backgroundSession = URLSession(configuration: backgroundConfiguration, delegate: self, delegateQueue: OperationQueue())
}
private func isDownloadInProgress(forUniqueKey key:String?) -> (Bool, SDDownloadObject?) {
@@ -121,6 +136,29 @@ final public class SDDownloadManager: NSObject {
return (false, nil)
}
+ private func showLocalNotification(withText text:String) {
+ let notificationCenter = UNUserNotificationCenter.current()
+ notificationCenter.getNotificationSettings { (settings) in
+ guard settings.authorizationStatus == .authorized else {
+ debugPrint("Not authorized to schedule notification")
+ return
+ }
+
+ let content = UNMutableNotificationContent()
+ content.title = text
+ content.sound = UNNotificationSound.default
+ let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.1,
+ repeats: false)
+ let identifier = "SDDownloadManagerNotification"
+ let request = UNNotificationRequest(identifier: identifier,
+ content: content, trigger: trigger)
+ notificationCenter.add(request, withCompletionHandler: { (error) in
+ if let error = error {
+ debugPrint("Could not schedule notification, error : \(error)")
+ }
+ })
+ }
+ }
}
extension SDDownloadManager : URLSessionDelegate, URLSessionDownloadDelegate {
@@ -164,6 +202,10 @@ extension SDDownloadManager : URLSessionDelegate, URLSessionDownloadDelegate {
didWriteData bytesWritten: Int64,
totalBytesWritten: Int64,
totalBytesExpectedToWrite: Int64) {
+ guard totalBytesExpectedToWrite > 0 else {
+ debugPrint("Could not calculate progress as totalBytesExpectedToWrite is less than 0")
+ return;
+ }
if let download = self.ongoingDownloads[(downloadTask.originalRequest?.url?.absoluteString)!],
let progressBlock = download.progressBlock {
@@ -190,4 +232,26 @@ extension SDDownloadManager : URLSessionDelegate, URLSessionDownloadDelegate {
}
}
+ public func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
+ session.getTasksWithCompletionHandler { (dataTasks, uploadTasks, downloadTasks) in
+ if downloadTasks.count == 0 {
+ OperationQueue.main.addOperation({
+ if let completion = self.backgroundCompletionHandler {
+ completion()
+ }
+
+ if self.showLocalNotificationOnBackgroundDownloadDone {
+ var notificationText = "Download completed"
+ if let userNotificationText = self.localNotificationText {
+ notificationText = userNotificationText
+ }
+
+ self.showLocalNotification(withText: notificationText)
+ }
+
+ self.backgroundCompletionHandler = nil
+ })
+ }
+ }
+ }
}
diff --git a/SDDownloadManager/Info.plist b/SDDownloadManager/Info.plist
index 9a21b4f..2504b7d 100644
--- a/SDDownloadManager/Info.plist
+++ b/SDDownloadManager/Info.plist
@@ -20,6 +20,11 @@
1
LSRequiresIPhoneOS
+ NSAppTransportSecurity
+
+ NSAllowsArbitraryLoads
+
+
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
@@ -34,10 +39,5 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
- NSAppTransportSecurity
-
- NSAllowsArbitraryLoads
-
-
diff --git a/SDDownloadManager/ViewController.swift b/SDDownloadManager/ViewController.swift
index 28a8184..7cb40a7 100644
--- a/SDDownloadManager/ViewController.swift
+++ b/SDDownloadManager/ViewController.swift
@@ -33,26 +33,37 @@ class ViewController: UIViewController {
@IBOutlet weak var progressLabel: UILabel!
@IBOutlet weak var finalUrlLabel: UILabel!
+ private let downloadManager = SDDownloadManager.shared
let directoryName : String = "TestDirectory"
+ let fiveMBUrl = "https://sample-videos.com/video123/mp4/480/big_buck_bunny_480p_5mb.mp4"
+ let tenMBUrl = "https://sample-videos.com/video123/mp4/480/big_buck_bunny_480p_10mb.mp4"
+
//MARK:- Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
+ self.setupUI()
+ self.foregrounDownloadDemo()
+ self.backgroundDownloadDemo()
+ }
+
+ private func setupUI() {
self.progressView.setProgress(0, animated: false)
self.progressLabel.text = "0.0 %"
self.finalUrlLabel.text = ""
+ }
+
+ private func foregrounDownloadDemo() {
+ let request = URLRequest(url: URL(string: self.fiveMBUrl)!)
- let request = URLRequest.init(url: URL.init(string: "http://www.sample-videos.com/video/3gp/144/big_buck_bunny_144p_5mb.3gp")!)
-
- let downloadKey = SDDownloadManager.shared.dowloadFile(withRequest: request,
- inDirectory: directoryName,
- withName: nil,
- onProgress: { [weak self] (progress) in
- let percentage = String(format: "%.1f %", (progress * 100))
- self?.progressView.setProgress(Float(progress), animated: true)
- self?.progressLabel.text = "\(percentage) %"
+ let downloadKey = self.downloadManager.downloadFile(withRequest: request,
+ inDirectory: directoryName,
+ onProgress: { [weak self] (progress) in
+ let percentage = String(format: "%.1f %", (progress * 100))
+ self?.progressView.setProgress(Float(progress), animated: true)
+ self?.progressLabel.text = "\(percentage) %"
}) { [weak self] (error, url) in
if let error = error {
print("Error is \(error as NSError)")
@@ -65,10 +76,29 @@ class ViewController: UIViewController {
}
print("The key is \(downloadKey!)")
+ }
+
+ private func backgroundDownloadDemo() {
+ let request = URLRequest(url: URL(string: self.tenMBUrl)!)
+
+ self.downloadManager.showLocalNotificationOnBackgroundDownloadDone = true
+ self.downloadManager.localNotificationText = "All background downloads complete"
+ let downloadKey = self.downloadManager.downloadFile(withRequest: request, inDirectory: directoryName, withName: directoryName, shouldDownloadInBackground: true, onProgress: { (progress) in
+ let percentage = String(format: "%.1f %", (progress * 100))
+ debugPrint("Background progress : \(percentage)")
+ }) { [weak self] (error, url) in
+ if let error = error {
+ print("Error is \(error as NSError)")
+ } else {
+ if let url = url {
+ print("Downloaded file's url is \(url.path)")
+ self?.finalUrlLabel.text = url.path
+ }
+ }
+ }
+ print("The key is \(downloadKey!)")
}
-
-
}