diff --git a/ios/Classes/SwiftFlutterUploaderPlugin.swift b/ios/Classes/SwiftFlutterUploaderPlugin.swift index f7e855a2..101d0375 100644 --- a/ios/Classes/SwiftFlutterUploaderPlugin.swift +++ b/ios/Classes/SwiftFlutterUploaderPlugin.swift @@ -128,6 +128,11 @@ public class SwiftFlutterUploaderPlugin: NSObject, FlutterPlugin { return } + guard let useBackgroundUrSession = args["useBackgroundUrSession"] as? Bool else { + result(FlutterError(code: "invalid_flag", message: "useBackgroundUrSession must be set", details: nil)) + return + } + uploadTaskWithURLWithCompletion( url: url, files: files, @@ -136,6 +141,7 @@ public class SwiftFlutterUploaderPlugin: NSObject, FlutterPlugin { parameters: data, tag: tag, allowCellular: allowCellular, + useBackgroundUrSession: useBackgroundUrSession, completion: { (task, error) in if error != nil { result(error!) @@ -185,7 +191,12 @@ public class SwiftFlutterUploaderPlugin: NSObject, FlutterPlugin { return } - binaryUploadTaskWithURLWithCompletion(url: url, file: fileUrl, method: method, headers: headers, tag: tag, allowCellular: allowCellular, completion: { (task, error) in + guard let useBackgroundUrSession = args["useBackgroundUrSession"] as? Bool else { + result(FlutterError(code: "invalid_flag", message: "useBackgroundUrSession must be set", details: nil)) + return + } + + binaryUploadTaskWithURLWithCompletion(url: url, file: fileUrl, method: method, headers: headers, tag: tag, allowCellular: allowCellular, useBackgroundUrSession: useBackgroundUrSession, completion: { (task, error) in if error != nil { result(error!) } else if let uploadTask = task { @@ -215,6 +226,7 @@ public class SwiftFlutterUploaderPlugin: NSObject, FlutterPlugin { headers: [String: Any?]?, tag: String?, allowCellular: Bool, + useBackgroundUrSession: Bool, completion completionHandler:@escaping (URLSessionUploadTask?, FlutterError?) -> Void) { let request = NSMutableURLRequest(url: url) request.httpMethod = method @@ -226,7 +238,7 @@ public class SwiftFlutterUploaderPlugin: NSObject, FlutterPlugin { } } - completionHandler(self.urlSessionUploader.enqueueUploadTask(request as URLRequest, path: file.path, wifiOnly: !allowCellular), nil) + completionHandler(self.urlSessionUploader.enqueueUploadTask(request as URLRequest, path: file.path, wifiOnly: !allowCellular, backgroundConfigOnly: useBackgroundUrSession), nil) } private func uploadTaskWithURLWithCompletion( @@ -237,6 +249,7 @@ public class SwiftFlutterUploaderPlugin: NSObject, FlutterPlugin { parameters data: [String: Any?]?, tag: String?, allowCellular: Bool, + useBackgroundUrSession: Bool, completion completionHandler:@escaping (URLSessionUploadTask?, FlutterError?) -> Void) { var flutterError: FlutterError? let fileManager = FileManager.default @@ -303,7 +316,7 @@ public class SwiftFlutterUploaderPlugin: NSObject, FlutterPlugin { return } - self.makeRequest(path, url, method, headers, formData.contentType, formData.contentLength, allowCellular: allowCellular, completion: { (task, error) in + self.makeRequest(path, url, method, headers, formData.contentType, formData.contentLength, allowCellular: allowCellular, useBackgroundUrSession: useBackgroundUrSession, completion: { (task, error) in completionHandler(task, error) }) } @@ -316,6 +329,7 @@ public class SwiftFlutterUploaderPlugin: NSObject, FlutterPlugin { _ contentType: String, _ contentLength: UInt64, allowCellular: Bool, + useBackgroundUrSession: Bool, completion completionHandler: (URLSessionUploadTask?, FlutterError?) -> Void) { let request = NSMutableURLRequest(url: url) request.httpMethod = method @@ -338,7 +352,7 @@ public class SwiftFlutterUploaderPlugin: NSObject, FlutterPlugin { return } - completionHandler(urlSessionUploader.enqueueUploadTask(request as URLRequest, path: path, wifiOnly: !allowCellular), nil) + completionHandler(urlSessionUploader.enqueueUploadTask(request as URLRequest, path: path, wifiOnly: !allowCellular, backgroundConfigOnly: useBackgroundUrSession), nil) } } diff --git a/ios/Classes/URLSessionUploader.swift b/ios/Classes/URLSessionUploader.swift index 3a51fac1..43bfbb20 100644 --- a/ios/Classes/URLSessionUploader.swift +++ b/ios/Classes/URLSessionUploader.swift @@ -20,8 +20,10 @@ struct Keys { class URLSessionUploader: NSObject { static let shared = URLSessionUploader() - var session: URLSession? - var wifiSession: URLSession? + var sessionBackground: URLSession? + var sessionDefault: URLSession? + var wifiSessionBackground: URLSession? + var wifiSessionDefault: URLSession? let queue = OperationQueue() // Accessing uploadedData & runningTaskById will require exclusive access @@ -45,9 +47,9 @@ class URLSessionUploader: NSObject { delegates.append(delegate) } - func enqueueUploadTask(_ request: URLRequest, path: String, wifiOnly: Bool) -> URLSessionUploadTask? { - guard let session = self.session, - let wifiSession = self.wifiSession else { + func enqueueUploadTask(_ request: URLRequest, path: String, wifiOnly: Bool, backgroundConfigOnly: Bool) -> URLSessionUploadTask? { + guard let session = backgroundConfigOnly ? self.sessionBackground : self.sessionDefault, + let wifiSession = backgroundConfigOnly ? self.wifiSessionBackground : self.wifiSessionDefault else { return nil } @@ -76,12 +78,12 @@ class URLSessionUploader: NSObject { /// /// The description on URLSessionTask.taskIdentifier explains how the task is only unique within a session. public func identifierForTask(_ task: URLSessionUploadTask) -> String { - return "\(self.session?.configuration.identifier ?? "chillisoure.flutter_uploader").\(task.taskDescription!)" + return "\(self.sessionDefault?.configuration.identifier ?? "chillisoure.flutter_uploader").\(task.taskDescription!)" } /// Cancel a task by ID. Complete with `true`/`false` depending on whether the task was running. func cancelWithTaskId(_ taskId: String) { - guard let session = session else { + guard let session = sessionDefault else { return } @@ -100,7 +102,7 @@ class URLSessionUploader: NSObject { /// Cancel all running tasks & return the list of canceled tasks. func cancelAllTasks() { - session?.getTasksWithCompletionHandler { (_, uploadTasks, _) in + sessionDefault?.getTasksWithCompletionHandler { (_, uploadTasks, _) in for uploadTask in uploadTasks { let state = uploadTask.state let taskId = self.identifierForTask(uploadTask) @@ -145,18 +147,33 @@ class URLSessionUploader: NSObject { self.queue.maxConcurrentOperationCount = maxUploadOperation.intValue - // configure session for wifi only uploads - let wifiConfiguration = URLSessionConfiguration.background(withIdentifier: Keys.wifiBackgroundSessionIdentifier) - wifiConfiguration.httpMaximumConnectionsPerHost = maxConcurrentTasks.intValue - wifiConfiguration.timeoutIntervalForRequest = URLSessionUploader.determineTimeout() - wifiConfiguration.allowsCellularAccess = false - self.wifiSession = URLSession(configuration: wifiConfiguration, delegate: self, delegateQueue: queue) - - // configure regular session - let sessionConfiguration = URLSessionConfiguration.background(withIdentifier: Keys.backgroundSessionIdentifier) - sessionConfiguration.httpMaximumConnectionsPerHost = maxConcurrentTasks.intValue - sessionConfiguration.timeoutIntervalForRequest = URLSessionUploader.determineTimeout() - self.session = URLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: queue) + // configure background session for wifi only uploads + let wifiConfigurationBackground = URLSessionConfiguration.background(withIdentifier: Keys.wifiBackgroundSessionIdentifier) + wifiConfigurationBackground.httpMaximumConnectionsPerHost = maxConcurrentTasks.intValue + wifiConfigurationBackground.timeoutIntervalForRequest = URLSessionUploader.determineTimeout() + wifiConfigurationBackground.allowsCellularAccess = false + self.wifiSessionBackground = URLSession(configuration: wifiConfigurationBackground, delegate: self, delegateQueue: queue) + + // configure default session for wifi only uploads + let wifiConfigurationDefault = URLSessionConfiguration.default + wifiConfigurationDefault.httpMaximumConnectionsPerHost = maxConcurrentTasks.intValue + wifiConfigurationDefault.timeoutIntervalForRequest = URLSessionUploader.determineTimeout() + wifiConfigurationDefault.allowsCellularAccess = false + wifiConfigurationDefault.waitsForConnectivity = true + self.wifiSessionDefault = URLSession(configuration: wifiConfigurationDefault, delegate: self, delegateQueue: queue) + + // configure background regular session + let sessionConfigurationBackground = URLSessionConfiguration.background(withIdentifier: Keys.backgroundSessionIdentifier) + sessionConfigurationBackground.httpMaximumConnectionsPerHost = maxConcurrentTasks.intValue + sessionConfigurationBackground.timeoutIntervalForRequest = URLSessionUploader.determineTimeout() + self.sessionBackground = URLSession(configuration: sessionConfigurationBackground, delegate: self, delegateQueue: queue) + + // configure default regular session + let sessionConfigurationDefault = URLSessionConfiguration.default + sessionConfigurationDefault.httpMaximumConnectionsPerHost = maxConcurrentTasks.intValue + sessionConfigurationDefault.timeoutIntervalForRequest = URLSessionUploader.determineTimeout() + sessionConfigurationDefault.waitsForConnectivity = true + self.sessionDefault = URLSession(configuration: sessionConfigurationDefault, delegate: self, delegateQueue: queue) } private static func determineTimeout() -> Double { diff --git a/ios/Classes/UploadTask.swift b/ios/Classes/UploadTask.swift index c27b52ae..0c6a2740 100644 --- a/ios/Classes/UploadTask.swift +++ b/ios/Classes/UploadTask.swift @@ -17,12 +17,14 @@ struct UploadTask { let progress: Int let tag: String? let allowCellular: Bool + let useBackgroundUrSession: Bool - init(taskId: String, status: UploadTaskStatus, progress: Int, tag: String? = nil, allowCellular: Bool = true) { + init(taskId: String, status: UploadTaskStatus, progress: Int, tag: String? = nil, allowCellular: Bool = true, useBackgroundUrSession: Bool = true) { self.taskId = taskId self.status = status self.progress = progress self.tag = tag self.allowCellular = allowCellular + self.useBackgroundUrSession = useBackgroundUrSession } } diff --git a/lib/src/flutter_uploader.dart b/lib/src/flutter_uploader.dart index a58a4d14..a1ab5f55 100644 --- a/lib/src/flutter_uploader.dart +++ b/lib/src/flutter_uploader.dart @@ -108,6 +108,7 @@ class FlutterUploader { 'data': upload.data, 'tag': upload.tag, 'allowCellular': upload.allowCellular, + 'useBackgroundUrSession': upload.useBackgroundUrSession, }))!; } if (upload is RawUpload) { @@ -118,6 +119,7 @@ class FlutterUploader { 'headers': upload.headers, 'tag': upload.tag, 'allowCellular': upload.allowCellular, + 'useBackgroundUrSession': upload.useBackgroundUrSession, }))!; } diff --git a/lib/src/upload.dart b/lib/src/upload.dart index d5f13cdd..27e2d778 100644 --- a/lib/src/upload.dart +++ b/lib/src/upload.dart @@ -10,6 +10,7 @@ abstract class Upload { this.headers = const {}, this.tag, this.allowCellular = true, + this.useBackgroundUrSession = true, }); /// Upload link @@ -27,6 +28,11 @@ abstract class Upload { /// If uploads are allowed to use cellular connections /// Defaults to true. If false, uploads will only use wifi connections final bool allowCellular; + + /// iOS Only + /// If uploads are done using UrlSessionConfig set to background mode. + /// Defaults to true. If false, default UrlSessionConfig is used + final bool useBackgroundUrSession; } /// Standard RFC 2388 multipart/form-data upload. @@ -42,6 +48,7 @@ class MultipartFormDataUpload extends Upload { this.files, this.data, bool allowCellular = true, + bool useBackgroundUrSession = true, }) : assert(files != null || data != null), super( url: url, @@ -49,6 +56,7 @@ class MultipartFormDataUpload extends Upload { headers: headers, tag: tag, allowCellular: allowCellular, + useBackgroundUrSession: useBackgroundUrSession, ) { // Need to specify either files or data. assert(files!.isNotEmpty || data!.isNotEmpty); @@ -71,12 +79,14 @@ class RawUpload extends Upload { String? tag, this.path, bool allowCellular = true, + bool useBackgroundUrSession = true, }) : super( url: url, method: method, headers: headers, tag: tag, allowCellular: allowCellular, + useBackgroundUrSession: useBackgroundUrSession, ); /// single file to upload