1

I have an app where a user uploads a file in the background that usually takes a couple of seconds. The upload is kicked off when they tap a "Done" button and that also dismisses the view controller. What I would I would like to happen is an alert comes up when the download is done. I thought I would just add the code below to the upload function but it isn't working. How can I have an alert box appear to confirm that that the upload was successful?

@IBAction func tapDone(_ sender: Any) {
    self.dismiss(animated: true, completion: nil)
    if let image = newImage {
        submit2Parse(image: image)
    }
}

func submit2Parse (image: UIImage) {
    if let pfImage = image2PFFile(image: image) {
        // Insert PFFile into parse server
        let submittedImage = PFObject(className: "Images")
        submittedImage["imageFile"] = pfImage
        submittedImage["type"] = "submittedFromUserHome"
        submittedImage["bride"] = brideSwitch.isOn
        submittedImage["groom"] = groomSwitch.isOn
        submittedImage["user"] = userSwitch.isOn
        submittedImage["picturePeriod"] = pickerSelected
        submittedImage["uploadedByUserId"] = PFUser.current()?.objectId ?? ""            submittedImage["uploadedByUserName"] = PFUser.current()?.username ?? ""
        if !txtIsPlaceHolder { submittedImage["description"] = imageDescription.text }

        submittedImage.saveInBackground { (success, error) in
            if success {
                let message = "Save in bg worked"
                print(message)
                let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert)
                alert.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: {
                    (action) in
                    self.dismiss(animated: true, completion: nil)
                }))
                self.present(alert,animated: true, completion: nil)

            } else {
                print(error?.localizedDescription ?? "")
            }
        }
    }
}

The code gives me this build error:

Implicit use of 'self' in closure; use 'self.' to make capture semantics explicit

Revanth Kausikan
  • 673
  • 1
  • 9
  • 21
Daniel Patriarca
  • 361
  • 3
  • 20

3 Answers3

1

In your tapDone method, you need to utilize the completion of the dismissal of your controller, like so:

self.dismiss(animated: true) {
    if let image = newImage {
        self.submit2Parse(image: image)
    }
}

This only means that the self.submit2Parse(image: image) will ONLY be executed after you dismiss the controller. Additionally, the error

Implicit use of 'self' in closure; use 'self.' to make capture semantics explicit

only means that you need to use self. to explicitly call a variable or method when you're inside a closure. So your submit2Parse... would now be like this:

self.submit2Parse(image: image).

Glenn Posadas
  • 12,555
  • 6
  • 54
  • 95
0

If I understood your question correctly you want to dismiss your SecondViewController when user tap on tapDone button. And after that once your image upload complete then you want to present alert for success.

But if you dismiss it once button tapped you won't get any alert because your SecondViewController is no longer in window hierarchy and if you will try to present the alert you will get an error in Console like:

Attempt to present on whose view is not in the window hierarchy!

To solve this problem you need to present a alert from your first view controller which will appear after you dismiss your second view controller and you can achieve it with delegate.

Consider the below example:

First of all declare a protocol outside your FirstViewController:

protocol UplaodSuccessDelegate:class {
    func uploadSuccess(message: String)
}

then confirm your delegate to same class:

class ViewController: FirstViewController, UplaodSuccessDelegate {

then you need to pass the delegate when you present SecondViewController:

let vc = self.storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
vc.delegate = self. //Pass delegate
self.present(vc, animated: true, completion: nil)

and add delegate method to same class:

func uploadSuccess(message: String) {

    let message = "Save in bg worked"
    print(message)
    let alert = UIAlertController(title: "title", message: message, preferredStyle: UIAlertController.Style.alert)
    alert.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: {
        (action) in

    }))
    self.present(alert,animated: true, completion: nil)
}

now in your SecondViewController you need to add

weak var delegate: UplaodSuccessDelegate?

and in your tapDone method replace a code with:

self.dismiss(animated: true) {
    if let image = newImage {
        submit2Parse(image: image)
    }
}

and once your upload complete you need to call delegate method (once your upload completes) like:

self.delegate?.uploadSuccess(message: "your message")

This will call your delegate method from FirstViewController.

Dharmesh Kheni
  • 71,228
  • 33
  • 160
  • 165
  • What if I don't know what the current view controller would be? I dismiss to certain view controller but the user could easily have moved into another while the upload was occurring. I know I have seen apps alert the user after a download has a occurred like when netflix lets you know your downoads are complete. Is there not a way to change my code so that the alter gets called on whatever the currently active queue is? – Daniel Patriarca May 07 '19 at 01:27
  • 1
    If that's the case then you can present alert from AppDelegate. Check this: https://stackoverflow.com/questions/36155769/how-to-show-uialertcontroller-from-appdelegate – Dharmesh Kheni May 07 '19 at 05:27
0

When you drop the function below into the viewController that initiates the data upload in the background, and then call this function in the closure that fires when the submit completes it is able to successfully create an alert even thought the initial view controller was dismissed (or long dismissed if it was a long upload).

// This is the function that performs my background upload
    func submit2Parse (image: UIImage) {
        if let pfImage = image2PFFile(image: image) {
            // Insert PFFile into parse server
            let submittedImage = PFObject(className: "Images")
            submittedImage["imageFile"] = pfImage
            submittedImage["imageCreateDt"] = newImageCreateDate
            submittedImage["type"] = "submittedFromUserHome"
            submittedImage["bride"] = brideSwitch.isOn
            submittedImage["groom"] = groomSwitch.isOn
            submittedImage["user"] = userSwitch.isOn
            submittedImage["picturePeriod"] = pickerSelected
            submittedImage["uploadedByUserId"] = PFUser.current()?.objectId ?? ""
            submittedImage["uploadedByUserName"] = PFUser.current()?.username ?? ""
            if !txtIsPlaceHolder { submittedImage["description"] = imageDescription.text }
            // Get the image timestamp, every photo has one
            // How do you get the thumbnail image too?

            submittedImage.saveInBackground { (success, error) in
                if success {
                    let message = "Save in bg worked"
                    print(message)
                    self.showAlertFromAppDelegates()
                } else {
                    print(error?.localizedDescription ?? "")
                }
            }
        }
    }

// This is the function that creates the alert
    func showAlertFromAppDelegates(){
        var topWindow: UIWindow? = UIWindow(frame: UIScreen.main.bounds)
        topWindow?.rootViewController = UIViewController()
        topWindow?.windowLevel = UIWindow.Level.alert + 1
        let alert: UIAlertController =  UIAlertController(title: "Upload Complete", message: "Your image was successfully submitted!", preferredStyle: .alert)
        alert.addAction(UIAlertAction.init(title: "OK", style: .default, handler: { (alertAction) in
            topWindow?.isHidden = true
            topWindow = nil
        }))
        topWindow?.makeKeyAndVisible()
        topWindow?.rootViewController?.present(alert, animated: true, completion:nil)
    }
Daniel Patriarca
  • 361
  • 3
  • 20