Background:
I’ve an app that obtain giant information within the background and it really works completely wonderful. I’m growing an motion extension for my app which permits customers so as to add obtain process with out opening the principle app.
Downside:
Once I add a obtain process from the app extension, and are available again within the app to trace the progress of the duty. The duty will not present up within the app till I recreate the session with the identical identifier for 3~4 instances. Secondly it solely return 1 process at a time. If I added a number of duties from the extension and are available again within the app it solely present me the 1 process. The subsequent one present up when the earlier one is accomplished. The obtain duties preserve working within the background appropriately however getTasksWithCompletionHandler
would not return the duties. See the hooked up video.
Goal:
Checklist down all duties contained in the host app and present the progress which are initiated from the app extension.
Workflow:
I’ve 2 session identifiers for:
Shared container identifier is about correctly.
Just one session could be energetic at a time for a similar session identifiers (both within the app or within the extension). That is why when person come again within the app. I all the time recreate the session as soon as with the identical identifier as that of extension.
Right here is how I’m organising session:
public comfort init(sessionIdentifier: String,
delegate: MZDownloadManagerDelegate? = nil,
downloadDelegate: URLSessionDownloadDelegate? = nil,
completion: (() -> Void)? = nil) {
self.init()
self.delegate = delegate
self.sessionManager = backgroundSession(identifier: sessionIdentifier,
delegate: downloadDelegate ?? self)
self.populateOtherDownloadTasks()
if let completion {
addSessionCompletion(identifier: sessionIdentifier,
handler: completion)
}
}
non-public class func sessionConfig(identifier: String) -> URLSessionConfiguration {
let configuration = URLSessionConfiguration.background(withIdentifier: identifier)
configuration.sharedContainerIdentifier = MZUtility.getAppGroupIdentifier()
return configuration
}
non-public func backgroundSession(identifier: String,
delegate: URLSessionDownloadDelegate) -> URLSession {
let config = MZDownloadManager.sessionConfig(identifier: identifier)
let session = URLSession(configuration: config,
delegate: delegate,
delegateQueue: nil)
return session
}
Right here is how I get the obtain duties contained in the session:
fileprivate func downloadTasks() -> [URLSessionDownloadTask] {
var duties: [URLSessionDownloadTask] = []
let semaphore = DispatchSemaphore(worth: 0)
sessionManager.getTasksWithCompletionHandler { (dataTasks, uploadTasks, downloadTasks) -> Void in
duties = downloadTasks
semaphore.sign()
}
let _ = semaphore.wait(timeout: DispatchTime.distantFuture)
let identifier = String(describing: sessionManager.configuration.identifier ?? "-")
debugPrint("MZDownloadManager: confId: (identifier) pending duties (duties)")
return duties
}
Right here is the code for the motion extension:
@IBAction func downloadButtonTapped(sender: UIButton) {
var titleString = (titleField.textual content ?? "")
titleString = titleString.isEmpty ? UUID().uuidString : titleString
let urlString = (inputUrlField.textual content ?? "").addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
guard let urlString,
let url = URL(string: urlString) else {
showAlert(title: "Invalid url", msg: "Please enter a sound url")
return
}
let request = URLRequest(url: url)
guard NSURLConnection.canHandle(request) else {
showAlert(title: "Invalid url", msg: "Please enter a sound url")
return
}
let identifier = MZUtility.sessionIdentifierExt
var downloadManager = MZDownloadManager(sessionIdentifier: identifier)
downloadManager.addDownloadTask(titleString, request: request)
extensionContext?.completeRequest(returningItems: [])
}
When applicationDidBecomeActive
or handleEventsForBackgroundURLSession
:
func setupDownloads() {
downloadManager.setupExtensionManager()
downloadManager.concatenateTasksWithExtensionSessions()
downloadManager.sessionManager.getAllTasks { (duties) in
DispatchQueue.primary.async {[weak self] in
guard let self else { return }
downloadManager.handleAppDidBecomeActive()
tableView.reloadData()
setupEmptyDataView()
}
}
}
Within the obtain supervisor class:
func concatenateTasksWithExtensionSessions() {
populateOtherDownloadTasks()
appExtensionManager?.populateOtherDownloadTasks()
for obtain in appExtensionManager?.downloadingArray ?? [] {
distinctAppend(downloadModel: obtain)
}
}
//When app goes in background and comeback in foreground the delegate turn out to be indifferent till resume for every process known as
func handleAppDidBecomeActive() {
let downloads = downloadingArray.filter({$0.process?.state == .working})
for obtain in downloads {
obtain.process?.resume()
}
}
func populateOtherDownloadTasks() {
for downloadTask in downloadTasks() {
let downloadModel = modelFromTaskDescription(downloadTask: downloadTask)
downloadModel.startTime = Date()
if downloadTask.state == .working {
downloadModel.standing = TaskStatus.downloading.description()
distinctAppend(downloadModel: downloadModel)
} else if(downloadTask.state == .suspended) {
downloadModel.standing = TaskStatus.paused.description()
distinctAppend(downloadModel: downloadModel)
} else {
downloadModel.standing = TaskStatus.failed.description()
}
}
}