From 5796f02f3f4213c2596134a64c9ef3d46f9f2fcd Mon Sep 17 00:00:00 2001 From: Ruben Nine Date: Wed, 27 Oct 2021 11:48:55 +0200 Subject: [PATCH] Ensure `completionBlock ` is called in all paths from `ImagePickerUploadController` and `URLPickerUploadController`. Show cancel button on navigation bar's left side in picker. Present alert when trying to use camera source from simulator instead of crashing. Minor updates in demo project. --- Demo/FilestackDemo.xcodeproj/project.pbxproj | 2 +- .../xcschemes/FilestackDemo.xcscheme | 2 +- Demo/FilestackDemo/AppDelegate.swift | 2 +- .../MyCustomSourceProvider.swift | 17 +++---- Demo/FilestackDemo/ViewController.swift | 15 +++--- Filestack.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Filestack.xcscheme | 2 +- .../CustomPickerUploadController.swift | 2 +- .../ImagePickerUploadController.swift | 1 + .../SourceTableViewController.swift | 46 +++++++++++++++---- .../URLPickerUploadController.swift | 3 ++ 11 files changed, 65 insertions(+), 29 deletions(-) diff --git a/Demo/FilestackDemo.xcodeproj/project.pbxproj b/Demo/FilestackDemo.xcodeproj/project.pbxproj index 690bc219..7eac81db 100644 --- a/Demo/FilestackDemo.xcodeproj/project.pbxproj +++ b/Demo/FilestackDemo.xcodeproj/project.pbxproj @@ -185,7 +185,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0900; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1310; ORGANIZATIONNAME = Filestack; TargetAttributes = { 454054801F98A69A00C12B2F = { diff --git a/Demo/FilestackDemo.xcodeproj/xcshareddata/xcschemes/FilestackDemo.xcscheme b/Demo/FilestackDemo.xcodeproj/xcshareddata/xcschemes/FilestackDemo.xcscheme index 59d37958..1d735b9c 100644 --- a/Demo/FilestackDemo.xcodeproj/xcshareddata/xcschemes/FilestackDemo.xcscheme +++ b/Demo/FilestackDemo.xcodeproj/xcshareddata/xcschemes/FilestackDemo.xcscheme @@ -1,6 +1,6 @@ = Set() { - didSet { updateUploadButton() } + didSet { updateAddButton() } } private lazy var cancelBarButton: UIBarButtonItem = { UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(cancel)) }() - private lazy var uploadBarButton: UIBarButtonItem = { - UIBarButtonItem(title: "Upload", style: .plain, target: self, action: #selector(upload)) + private lazy var addBarButton: UIBarButtonItem = { + UIBarButtonItem(title: "Add", style: .plain, target: self, action: #selector(add)) }() // MARK: - Lifecycle @@ -53,7 +53,7 @@ class MyCustomSourceProvider: UICollectionViewController, SourceProvider { // MARK: - Actions extension MyCustomSourceProvider { - @objc func upload() { + @objc func add() { let urls = Array(urls) dismiss(animated: true) { @@ -71,8 +71,9 @@ extension MyCustomSourceProvider { // MARK: - Private Functions private extension MyCustomSourceProvider { - func updateUploadButton() { - uploadBarButton.title = "Upload (\(urls.count)/\(availableURLs.count))" + func updateAddButton() { + addBarButton.isEnabled = !urls.isEmpty + addBarButton.title = "Add (\(urls.count)/\(availableURLs.count))" } } @@ -83,13 +84,13 @@ extension MyCustomSourceProvider { super.viewDidLoad() navigationItem.leftBarButtonItem = cancelBarButton - navigationItem.rightBarButtonItem = uploadBarButton + navigationItem.rightBarButtonItem = addBarButton collectionView?.backgroundColor = UIColor.black collectionView?.register(CustomCell.self, forCellWithReuseIdentifier: customCellID) collectionView?.allowsMultipleSelection = true - updateUploadButton() + updateAddButton() } override func viewDidDisappear(_ animated: Bool) { diff --git a/Demo/FilestackDemo/ViewController.swift b/Demo/FilestackDemo/ViewController.swift index aadbe5dd..07c3da10 100644 --- a/Demo/FilestackDemo/ViewController.swift +++ b/Demo/FilestackDemo/ViewController.swift @@ -55,9 +55,8 @@ extension ViewController: PickerNavigationControllerDelegate { // Dismiss the picker since we have finished picking files from the local device, and, in `storeOnly` mode, // there's no upload phase. DispatchQueue.main.async { - picker.dismiss(animated: true) { - self.presentAlert(titled: "Success", message: "Finished picking files: \(fileURLs)") - } + self.presentedViewController? + .presentAlert(titled: "Success", message: "Finished picking files: \(fileURLs)") } default: break @@ -76,13 +75,14 @@ extension ViewController: PickerNavigationControllerDelegate { print("Uploaded file URLs: \(fileURLs)") // Dismiss the picker since we finished uploading picked files. - picker.dismiss(animated: true) { + DispatchQueue.main.async { let handles = responses.compactMap { $0.json?["handle"] as? String } if !handles.isEmpty { let joinedHandles = handles.joined(separator: ", ") - self.presentAlert(titled: "Success", + self.presentedViewController? + .presentAlert(titled: "Success", message: "Finished uploading files with handles: \(joinedHandles)") } } @@ -91,8 +91,9 @@ extension ViewController: PickerNavigationControllerDelegate { /// Called when the picker finishes storing a file originating from a cloud source into the storage destination. func pickerStoredFile(picker: PickerNavigationController, response: StoreResponse) { if let handle = response.contents?["handle"] as? String { - picker.dismiss(animated: true) { - self.presentAlert(titled: "Success", message: "Finished storing file with handle: \(handle)") + DispatchQueue.main.async { + self.presentedViewController? + .presentAlert(titled: "Success", message: "Finished storing file with handle: \(handle)") } } } diff --git a/Filestack.xcodeproj/project.pbxproj b/Filestack.xcodeproj/project.pbxproj index 09eb0e67..66c2f3a3 100644 --- a/Filestack.xcodeproj/project.pbxproj +++ b/Filestack.xcodeproj/project.pbxproj @@ -821,7 +821,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0900; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1310; ORGANIZATIONNAME = Filestack; TargetAttributes = { 457084FE1F975C6C00991340 = { diff --git a/Filestack.xcodeproj/xcshareddata/xcschemes/Filestack.xcscheme b/Filestack.xcodeproj/xcshareddata/xcschemes/Filestack.xcscheme index 33b81298..1427bab9 100644 --- a/Filestack.xcodeproj/xcshareddata/xcschemes/Filestack.xcscheme +++ b/Filestack.xcodeproj/xcshareddata/xcschemes/Filestack.xcscheme @@ -1,6 +1,6 @@ Bool { urlExtractorOperation?.cancel() trackingProgress.cancel() + completionBlock?([]) return uploader?.cancel() ?? true } diff --git a/Sources/Filestack/UI/Internal/Controllers/SourceTableViewController.swift b/Sources/Filestack/UI/Internal/Controllers/SourceTableViewController.swift index 1c0d5557..e5e7eb53 100644 --- a/Sources/Filestack/UI/Internal/Controllers/SourceTableViewController.swift +++ b/Sources/Filestack/UI/Internal/Controllers/SourceTableViewController.swift @@ -30,12 +30,13 @@ class SourceTableViewController: UITableViewController { private var filePicker: (Cancellable & Monitorizable)? private var filePickerObserver: NSKeyValueObservation? - private var pickMonitorViewController: MonitorViewController? override public func viewDidLoad() { super.viewDidLoad() - navigationItem.rightBarButtonItem = cancelBarButton + + // Add cancel button to navigation bar. + navigationItem.leftBarButtonItem = cancelBarButton // Try to obtain `Client` object from navigation controller if let picker = navigationController as? PickerNavigationController { @@ -198,6 +199,22 @@ private extension SourceTableViewController { } func pick(source: LocalSource) { + #if targetEnvironment(simulator) + guard source.provider != .camera else { + deselectTableViewRows() + + let alert = UIAlertController(title: "Alert", + message: "Camera source is unavailable in simulator environment.", + preferredStyle: .alert) + + alert.addAction(UIAlertAction(title: "OK", style: .default)) + + self.present(alert, animated: true) + + return + } + #endif + guard let picker = navigationController as? PickerNavigationController else { return } let pickCompletionHandler: (([URL]) -> Void) = { urls in @@ -238,6 +255,7 @@ private extension SourceTableViewController { } func pickCompleted(picker: PickerNavigationController, urls: [URL]) -> Void { + deselectTableViewRows() dispatchPrecondition(condition: .notOnQueue(.main)) switch picker.behavior { @@ -265,7 +283,9 @@ private extension SourceTableViewController { } DispatchQueue.main.async { - picker.pickerDelegate?.pickerPickedFiles(picker: picker, fileURLs: urls) + if !urls.isEmpty { + picker.pickerDelegate?.pickerPickedFiles(picker: picker, fileURLs: urls) + } if picker.behavior == .storeOnly { // Remove any temporary files after returning from delegate call. @@ -311,13 +331,15 @@ private extension SourceTableViewController { semaphore.wait() - DispatchQueue.main.async { - picker.pickerDelegate?.pickerUploadedFiles(picker: picker, responses: responses) + if !responses.isEmpty { + DispatchQueue.main.async { + picker.pickerDelegate?.pickerUploadedFiles(picker: picker, responses: responses) - // Remove any temporary files after returning from delegate call. - let urls = responses.compactMap { $0.context as? URL } + // Remove any temporary files after returning from delegate call. + let urls = responses.compactMap { $0.context as? URL } - self.deleteTemporaryFiles(at: urls) + self.deleteTemporaryFiles(at: urls) + } } case .storeOnly: // NO-OP @@ -334,4 +356,12 @@ private extension SourceTableViewController { } } } + + func deselectTableViewRows() { + DispatchQueue.main.async { + if let selectedRow = self.tableView.indexPathForSelectedRow { + self.tableView.deselectRow(at: selectedRow, animated: true) + } + } + } } diff --git a/Sources/Filestack/UI/Internal/Controllers/URLPickerUploadController.swift b/Sources/Filestack/UI/Internal/Controllers/URLPickerUploadController.swift index 7b602b1a..cce60649 100644 --- a/Sources/Filestack/UI/Internal/Controllers/URLPickerUploadController.swift +++ b/Sources/Filestack/UI/Internal/Controllers/URLPickerUploadController.swift @@ -47,6 +47,7 @@ class URLPickerUploadController: NSObject, Cancellable, Monitorizable, Startable @discardableResult func cancel() -> Bool { observers.removeAll() + completionBlock?([]) return uploader?.cancel() ?? true } @@ -58,6 +59,8 @@ extension URLPickerUploadController { } private func doUpload(urls: [URL]) { + completionBlock?(urls) + guard let uploader = uploader else { return } let progress = Progress(totalUnitCount: Int64(urls.count * 100))