5

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.

Community
  • 1
  • 1
nervous-energy
  • 367
  • 6
  • 10

3 Answers3

2

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)
}
Evgenii
  • 36,389
  • 27
  • 134
  • 170
  • 1
    This is an unexpected solution, but it works, thank you. Just so I am clear: – nervous-energy Aug 02 '15 at 14:24
  • 1
    In UpdateViewController we are defining a variable 'clearUpdateAvailable' that is an empty closure. This is called when the view disappears. Meanwhile in ViewController we are presenting the view WITHOUT a segue. As we create the UpdateViewController object we declare its clearUpdateAvailable variable as 'self', i.e. the closure takes the ViewController as an argument + when called it resets the updateAvailable Bool and updates the UI... This is very clever, and I thank you for it, but I still find closures a bit baffling. (Sorry accidental double comment- I couldn't delete the first) – nervous-energy Aug 02 '15 at 14:36
  • 1
    @nervous-energy, yeah, closures can be tricky to grasp, I had same issues at first. But not to worry, with practice it will sink in. In some situations I find closures easier to use in Swift than the traditional delegates approach. Delegates are still good for other purposes. One thing to remember is when one uses `self` it is better to make it weak by writing `[weak self]` inside the closures to avoid reference cycle and memory leaks. – Evgenii Aug 03 '15 at 03:29
1

Pass your main ViewController to the UpdateViewController in the prepareForSegue method.

When you finish the update, update the updateAvailable flag for the ViewController controller.

This way on dismissing and appearing of the main ViewController the updateAvailable in ViewController will be YES.

Remember to set the accessibilty for the updateAvailable flag and the reference of the ViewController in UpdateViewController to public. to be able to update those values out side of the class.

hasan
  • 23,815
  • 10
  • 63
  • 101
0

On my Second View Controller I am collecting the data and want to send it back to Perent Controller. Onces the parent controller recieves the data, I want to fire the function called " callGetSearchedJobs()". But It doesn't get recalled after the Second View Controller gets dismissed.

override func viewDidDisappear(_ animated: Bool){

    super.viewDidDisappear(animated)
    if let jobOffeVC = presentedViewController as? JobOffersVC {

        DispatchQueue.main.async {
            jobOffeVC.callGetSearchedJobs()
            jobOffeVC.jobOfferTable.reloadData()
        }
    }
}

First View Controller:

extension JobOffersVC: JobFilltersVCDelegate{

func sendDatatoJobOffersVC(myData: SearchJobInputModel){

     searchJobs = myData
  }

}

//AMRK:- Api Methods extension JobOffersVC{

func callGetSearchedJobs(){
    JobOrderServiceManager.shareInstance.getSearchedJobOrders(postData: searchJobs){ result in
        
        if let result = result{
            Swift.dump(result)
        }
    }
}