Skip to content

iOS Uploader Stability changes #3

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
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
77 changes: 41 additions & 36 deletions ios/Classes/SwiftFlutterUploaderPlugin.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Alamofire
import Flutter
import UIKit
import Alamofire

private let validHttpMethods = ["POST", "PUT", "PATCH"]

Expand Down Expand Up @@ -36,18 +36,18 @@ public class SwiftFlutterUploaderPlugin: NSObject, FlutterPlugin {
registrar.addApplicationDelegate(instance)
}

init (_ channel: FlutterMethodChannel, progressEventChannel: FlutterEventChannel, resultEventChannel: FlutterEventChannel) {
init(_ channel: FlutterMethodChannel, progressEventChannel: FlutterEventChannel, resultEventChannel: FlutterEventChannel) {
self.channel = channel

self.progressEventChannel = progressEventChannel
self.progressHandler = CachingStreamHandler()
progressHandler = CachingStreamHandler()
progressEventChannel.setStreamHandler(progressHandler)

self.resultEventChannel = resultEventChannel
self.resultHandler = CachingStreamHandler()
resultHandler = CachingStreamHandler()
resultEventChannel.setStreamHandler(resultHandler)

self.taskQueue = DispatchQueue(label: "chillisource.flutter_uploader.dispatch.queue")
taskQueue = DispatchQueue(label: "chillisource.flutter_uploader.dispatch.queue", qos: .utility)
super.init()

urlSessionUploader.addDelegate(self)
Expand Down Expand Up @@ -87,8 +87,8 @@ public class SwiftFlutterUploaderPlugin: NSObject, FlutterPlugin {
guard let args = call.arguments as? [String: Any?],
let urlString = args["url"] as? String,
let method = args["method"] as? String,
let files = args["files"] as? [Any] else {

let files = args["files"] as? [Any]
else {
result(FlutterError(code: "invalid_parameters", message: "Invalid parameters passed", details: nil))
return
}
Expand Down Expand Up @@ -127,19 +127,21 @@ public class SwiftFlutterUploaderPlugin: NSObject, FlutterPlugin {
parameters: data,
tag: tag,
allowCellular: allowCellular,
completion: { (task, error) in
completion: { task, error in
if error != nil {
result(error!)
} else if let uploadTask = task {
result(self.urlSessionUploader.identifierForTask(uploadTask))
}
})
}
)
}

private func enqueueBinaryMethodCall(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
guard let args = call.arguments as? [String: Any?],
let urlString = args["url"] as? String,
let method = args["method"] as? String else {
let urlString = args["url"] as? String,
let method = args["method"] as? String
else {
result(FlutterError(code: "invalid_parameters", message: "Invalid parameters passed", details: nil))
return
}
Expand Down Expand Up @@ -176,7 +178,7 @@ public class SwiftFlutterUploaderPlugin: NSObject, FlutterPlugin {
return
}

binaryUploadTaskWithURLWithCompletion(url: url, file: fileUrl, method: method, headers: headers, tag: tag, allowCellular: allowCellular, completion: { (task, error) in
binaryUploadTaskWithURLWithCompletion(url: url, file: fileUrl, method: method, headers: headers, tag: tag, allowCellular: allowCellular, completion: { task, error in
if error != nil {
result(error!)
} else if let uploadTask = task {
Expand All @@ -187,14 +189,15 @@ public class SwiftFlutterUploaderPlugin: NSObject, FlutterPlugin {

private func cancelMethodCall(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
if let args = call.arguments as? [String: Any?],
let taskId = args[Key.taskId] as? String {
let taskId = args[Key.taskId] as? String
{
urlSessionUploader.cancelWithTaskId(taskId)
}

result(nil)
}

private func cancelAllMethodCall(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
private func cancelAllMethodCall(_: FlutterMethodCall, _ result: @escaping FlutterResult) {
urlSessionUploader.cancelAllTasks()

result(nil)
Expand All @@ -204,20 +207,21 @@ public class SwiftFlutterUploaderPlugin: NSObject, FlutterPlugin {
file: URL,
method: String,
headers: [String: Any?]?,
tag: String?,
tag _: String?,
allowCellular: Bool,
completion completionHandler:@escaping (URLSessionUploadTask?, FlutterError?) -> Void) {
completion completionHandler: @escaping (URLSessionUploadTask?, FlutterError?) -> Void)
{
let request = NSMutableURLRequest(url: url)
request.httpMethod = method
request.setValue("*/*", forHTTPHeaderField: "Accept")

headers?.forEach { (key, value) in
headers?.forEach { key, value in
if let value = value as? String {
request.setValue(value, forHTTPHeaderField: key)
}
}

completionHandler(self.urlSessionUploader.enqueueUploadTask(request as URLRequest, path: file.path, wifiOnly: !allowCellular), nil)
completionHandler(urlSessionUploader.enqueueUploadTask(request as URLRequest, path: file.path, wifiOnly: !allowCellular), nil)
}

private func uploadTaskWithURLWithCompletion(
Expand All @@ -226,27 +230,29 @@ public class SwiftFlutterUploaderPlugin: NSObject, FlutterPlugin {
method: String,
headers: [String: Any?]?,
parameters data: [String: Any?]?,
tag: String?,
tag _: String?,
allowCellular: Bool,
completion completionHandler:@escaping (URLSessionUploadTask?, FlutterError?) -> Void) {
completion completionHandler: @escaping (URLSessionUploadTask?, FlutterError?) -> Void
) {
var flutterError: FlutterError?
let fileManager = FileManager.default
var fileCount: Int = 0
var fileCount = 0
let formData = MultipartFormData()
let tempDirectory = NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)

if data != nil {
data?.forEach({ (key, value) in
data?.forEach { key, value in
if let value = value as? String {
formData.append(value.data(using: .utf8)!, withName: key)
}
})
}
}

for file in files {
guard let file = file as? [String: Any],
let fieldname = file[Key.fieldname] as? String,
let path = file[Key.path] as? String else {
let path = file[Key.path] as? String
else {
continue
}

Expand Down Expand Up @@ -294,7 +300,7 @@ public class SwiftFlutterUploaderPlugin: NSObject, FlutterPlugin {
return
}

self.makeRequest(path, url, method, headers, formData.contentType, formData.contentLength, allowCellular: allowCellular, completion: { (task, error) in
makeRequest(path, url, method, headers, formData.contentType, formData.contentLength, allowCellular: allowCellular, completion: { task, error in
completionHandler(task, error)
})
}
Expand All @@ -307,15 +313,16 @@ public class SwiftFlutterUploaderPlugin: NSObject, FlutterPlugin {
_ contentType: String,
_ contentLength: UInt64,
allowCellular: Bool,
completion completionHandler: (URLSessionUploadTask?, FlutterError?) -> Void) {
completion completionHandler: (URLSessionUploadTask?, FlutterError?) -> Void
) {
let request = NSMutableURLRequest(url: url)
request.httpMethod = method
request.setValue("*/*", forHTTPHeaderField: "Accept")
request.setValue("\(contentType)", forHTTPHeaderField: "Content-Type")
request.setValue("\(contentLength)", forHTTPHeaderField: "Content-Length")

if let headers = headers {
headers.forEach { (key, value) in
headers.forEach { key, value in
if let value = value as? String {
request.setValue(value, forHTTPHeaderField: key)
}
Expand All @@ -334,8 +341,8 @@ public class SwiftFlutterUploaderPlugin: NSObject, FlutterPlugin {
}

/// UIApplicationDelegate
extension SwiftFlutterUploaderPlugin {
public func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) -> Bool {
public extension SwiftFlutterUploaderPlugin {
func application(_: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) -> Bool {
NSLog("ApplicationHandleEventsForBackgroundURLSession: \(identifier)")
if identifier == Keys.backgroundSessionIdentifier {
urlSessionUploader.backgroundTransferCompletionHander = completionHandler
Expand All @@ -344,7 +351,7 @@ extension SwiftFlutterUploaderPlugin {
return true
}

public func applicationWillTerminate(_ application: UIApplication) {
func applicationWillTerminate(_: UIApplication) {
// runningTaskById.removeAll()
// uploadedData.removeAll()
// queue.cancelAllOperations()
Expand All @@ -355,15 +362,15 @@ extension SwiftFlutterUploaderPlugin: UploaderDelegate {
func uploadEnqueued(taskId: String) {
resultHandler.add(taskId, [
Key.taskId: taskId,
Key.status: UploadTaskStatus.enqueue.rawValue
Key.status: UploadTaskStatus.enqueue.rawValue,
])
}

func uploadProgressed(taskId: String, inStatus: UploadTaskStatus, progress: Int) {
progressHandler.add(taskId, [
Key.taskId: taskId,
Key.status: inStatus.rawValue,
Key.progress: progress
Key.progress: progress,
])
}

Expand All @@ -373,9 +380,8 @@ extension SwiftFlutterUploaderPlugin: UploaderDelegate {
Key.status: UploadTaskStatus.completed.rawValue,
Key.message: message ?? NSNull(),
Key.statusCode: statusCode,
Key.headers: headers
Key.headers: headers,
])

}

func uploadFailed(taskId: String, inStatus: UploadTaskStatus, statusCode: Int, errorCode: String, errorMessage: String?, errorStackTrace: [String]) {
Expand All @@ -385,8 +391,7 @@ extension SwiftFlutterUploaderPlugin: UploaderDelegate {
Key.statusCode: statusCode,
Key.code: errorCode,
Key.message: errorMessage ?? NSNull(),
Key.details: errorStackTrace
Key.details: errorStackTrace,
])
}

}
35 changes: 20 additions & 15 deletions ios/Classes/URLSessionUploader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,25 +52,30 @@ class URLSessionUploader: NSObject {
}

let activeSession = wifiOnly ? wifiSession : session
let uploadTask = activeSession.uploadTask(
with: request as URLRequest,
fromFile: URL(fileURLWithPath: path)
)
do {
let uploadTask = try activeSession.uploadTask(
with: request as URLRequest,
fromFile: URL(fileURLWithPath: path)
)

// Create a random UUID as task description (& ID).
uploadTask.taskDescription = UUID().uuidString
// Create a random UUID as task description (& ID).
uploadTask.taskDescription = UUID().uuidString

let taskId = identifierForTask(uploadTask)
let taskId = identifierForTask(uploadTask)

delegates.uploadEnqueued(taskId: taskId)
delegates.uploadEnqueued(taskId: taskId)

uploadTask.resume()
uploadTask.resume()

semaphore.wait()
self.runningTaskById[taskId] = UploadTask(taskId: taskId, status: .enqueue, progress: 0)
semaphore.signal()
semaphore.wait()
self.runningTaskById[taskId] = UploadTask(taskId: taskId, status: .enqueue, progress: 0)
semaphore.signal()

return uploadTask
return uploadTask
} catch {
NSLog("Caught uploadTask exception \(error)")
return nil
}
}

///
Expand Down Expand Up @@ -148,14 +153,14 @@ class URLSessionUploader: NSObject {
wifiConfiguration.httpMaximumConnectionsPerHost = maxConcurrentTasks.intValue
wifiConfiguration.timeoutIntervalForRequest = URLSessionUploader.determineTimeout()
wifiConfiguration.allowsCellularAccess = false
wifiConfiguration.httpShouldUsePipelining = true
wifiConfiguration.httpShouldUsePipelining = 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()
sessionConfiguration.httpShouldUsePipelining = true
sessionConfiguration.httpShouldUsePipelining = false
self.session = URLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: queue)
}

Expand Down