Search code examples
swiftcocoa-touchuiviewcontrollerdelegatesswift2

Dismiss ViewController AND reload previous / pass data back?


My app downloads updates from a server. To do this it compares the data on the device with what is available on the server. If there is a mismatch, a Boolean 'updateAvailable' is set to true, and an interactive UILabel is shown to the user indicating they can download the update.

(Slightly Simplified Code)

class ViewController: UIViewController {

@IBOutlet weak var updateLabel: UILabel!

func updateChecker(){
    let deviceVersion = self.settingsController.getDeviceVersion()

    //Get Server Version (Async)
    _ = settingsController.getServerVersion() { (data) -> () in

        dispatch_async(dispatch_get_main_queue()){

            if deviceVersion != data{
                self.updateAvailable = true
            }
            else{
                self.updateAvailable = false
            }

            //Update UI
            self.updateUI()    
         }
    }
}

func updateUI(){
    if updateAvailable{
        updateLabel.text = "Update Available"
    }
    else{
        updateLabel.text = "App Up To Date"
    }
}


On touching this update label, the app triggers a custom segue to the 'updateViewController':

 @IBAction func getUpdateLabelPressed(sender: UITapGestureRecognizer) {
    if updateAvailable{
        self.performSegueWithIdentifier("segueToUpdateView", sender: self)
    }



The UpdateViewController downloads data from the server (async) and is then dismissed:

class UpdateViewController: UIViewController{
override func viewDidLoad() {
    super.viewDidLoad()        

    //Create Settings Controller + Get Update        
    let sc = SettingsController()
    sc.getUpdate({ (success) -> () in

        if success{
            dispatch_async(dispatch_get_main_queue()){    
            print("Update Success")
            self.dismissViewControllerAnimated(true, completion: nil)       
            }
         }
        else{   
            dispatch_async(dispatch_get_main_queue()){    
            print("Update Failed")
            self.dismissViewControllerAnimated(true, completion: nil)
            }
        }
     })       
}


On dismissal of the UpdateViewController, the main ViewController is shown, however 'updateAvailable' is still set to true, and the updateLabel is still active.

Triggering another custom Segue back to the ViewController / Root ViewController is not an option for me, as this breaks background update fetching by creating more than one instance of 'ViewController' in the Stack.

onViewDidAppear() is also inappropriate in this case, as it would cause heavy network / server traffic.

How can I can dismiss the UpdateViewController AND Reload the previous ViewController OR Send back data that triggers ViewController's updateChecker() method? OR clear the entire ViewControllerStack and restart the app?

I understand that delegate arrangement seems appropriate, but the previous SO questions and answers on this topic such as this one are for Objective-C, and I have not been able to adapt these answers for Swift 2.0.

Any help would be greatly appreciated.


Solution

  • Create a closure property clearUpdateAvailable in UpdateViewController:

    class UpdateViewController: UIViewController {
      var clearUpdateAvailable: (()->())?
    
      override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
        clearUpdateAvailable?()
      }
    }
    

    You can call clearUpdateAvailable in viewWillDisappear or in the method where you receive the data from server.

    In ViewController, supply a closure when presenting UpdateViewController by ID.

    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    if let updateViewController = storyboard.instantiateViewControllerWithIdentifier("updateViewController") as? UpdateViewController {
      updateViewController.clearUpdateAvailable = { [weak self] in
        self?.updateAvailable = false
        self?.updateUI()
      } 
      presentViewController(updateViewController, animated: true, completion: nil)
    }