Reputation: 807
I'm using marmelroy/Zip framework to zip/unzip files in my project, and JGProgressHUD to show the progress of the operation.
I'm able to see the HUD if I try to show it from the ViewDidLoad method, but if I use it in the closure associated to the progress feature of the quickZipFiles method (like in the code sample), the hud is shown just at the end of the operation.
I guess this could be related to a timing issue, but since I'm not too much into completion handlers, closures and GDC (threads, asynchronous tasks, etc.) I would like to ask for a suggestion.
Any ideas?
// In my class properties declaration
var hud = JGProgressHUD(style: .dark)
// In my ViewDidLoad
self.hud.indicatorView = JGProgressHUDPieIndicatorView()
self.hud.backgroundColor = UIColor(white: 0, alpha: 0.7)
// In my method
do {
self.hud.textLabel.text = NSLocalizedString("Zipping files...", comment: "Zipping File Message")
self.hud.detailTextLabel.text = "0%"
if !(self.hud.isVisible) {
self.hud.show(in: self.view)
}
zipURL = try Zip.quickZipFiles(documentsList, fileName: "documents", progress: { (progress) -> () in
let progressMessage = "\(round(progress*100))%"
print(progressMessage)
self.hud.setProgress(Float(progress), animated: true)
self.hud.textLabel.text = NSLocalizedString("Zipping files...", comment: "Zipping File Message")
self.hud.detailTextLabel.text = progressMessage
if (progress == 1.0) {
self.hud.dismiss()
}
})
} catch {
print("Error while creating zip...")
}
Upvotes: 1
Views: 2868
Reputation: 34253
ZIP Foundation comes with built-in support for progress reporting and cancelation.
So if you can switch ZIP library, this might be a better fit for your project. (Full disclosure: I am the author of this library)
Here's some sample code that shows how you can zip a directory and display operation progress on a JGProgressHUD
. I just zip the main bundle's directory here as example.
The ZIP operation is dispatched on a separate thread so that your main thread can update the UI. The progress
var is a default Foundation (NS)Progress object which reports changes via KVO.
import UIKit
import ZIPFoundation
import JGProgressHUD
class ViewController: UIViewController {
@IBOutlet weak var progressLabel: UILabel!
var indicator = JGProgressHUD()
var isObservingProgress = false
var progressViewKVOContext = 0
@objc
var progress: Progress?
func startObservingProgress()
{
guard !isObservingProgress else { return }
progress = Progress()
progress?.completedUnitCount = 0
self.indicator.progress = 0.0
self.addObserver(self, forKeyPath: #keyPath(progress.fractionCompleted), options: [.new], context: &progressViewKVOContext)
isObservingProgress = true
}
func stopObservingProgress()
{
guard isObservingProgress else { return }
self.removeObserver(self, forKeyPath: #keyPath(progress.fractionCompleted))
isObservingProgress = false
self.progress = nil
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == #keyPath(progress.fractionCompleted) {
DispatchQueue.main.async {
self.indicator.progress = Float(self.progress?.fractionCompleted ?? 0.0)
if let progressDescription = self.progress?.localizedDescription {
self.progressLabel.text = progressDescription
}
if self.progress?.isFinished == true {
self.progressLabel.text = ""
self.indicator.progress = 0.0
}
}
} else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
@IBAction func cancel(_ sender: Any) {
self.progress?.cancel()
}
@IBAction func createFullArchive(_ sender: Any) {
let directoryURL = Bundle.main.bundleURL
let tempArchiveURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(ProcessInfo.processInfo.globallyUniqueString).appendingPathExtension("zip")
self.startObservingProgress()
DispatchQueue.global().async {
try? FileManager.default.zipItem(at: directoryURL, to: tempArchiveURL, progress: self.progress)
self.stopObservingProgress()
}
}
}
Upvotes: 4
Reputation: 318824
Looking at the implementation of the zip library, all of the zipping/unzipping and the calls to the progress handlers are being done on the same thread. The example shown on the home page isn't very good and can't be used as-is if you wish to update the UI with a progress indicator while zipping or unzipping.
The solution is to perform the zipping/unzipping in the background and in the progress block, update the UI on the main queue.
Assuming you are calling your posted code from the main queue (in response to the user performing some action), you should update your code as follows:
// In my class properties declaration
var hud = JGProgressHUD(style: .dark)
// In my ViewDidLoad
self.hud.indicatorView = JGProgressHUDPieIndicatorView()
self.hud.backgroundColor = UIColor(white: 0, alpha: 0.7)
self.hud.textLabel.text = NSLocalizedString("Zipping files...", comment: "Zipping File Message")
self.hud.detailTextLabel.text = "0%"
if !(self.hud.isVisible) {
self.hud.show(in: self.view)
}
DispatchQueue.global().async {
defer {
DispatchQueue.main.async {
self.hud.dismiss()
}
}
do {
zipURL = try Zip.quickZipFiles(documentsList, fileName: "documents", progress: { (progress) -> () in
DispatchQueue.main.async {
let progressMessage = "\(round(progress*100))%"
print(progressMessage)
self.hud.setProgress(Float(progress), animated: true)
self.hud.textLabel.text = NSLocalizedString("Zipping files...", comment: "Zipping File Message")
self.hud.detailTextLabel.text = progressMessage
}
})
} catch {
print("Error while creating zip...")
}
}
Upvotes: 2