OOProg
OOProg

Reputation: 189

In swift, how can I wait until a server response is received before I proceed?

I would like to only execute a segue if I get a certain response from the server. In swift, how can I wait until I get a response to continue?

Upvotes: 0

Views: 2349

Answers (1)

Rob
Rob

Reputation: 437381

Bottom line, you don't "wait" for the response, but rather simply specify what you want to happen when the response comes in. For example, if you want to perform a segue when some network request is done, you should employ the completion handler pattern.

The issue here is that you're probably accustomed to just hooking your UI control to a segue in Interface Builder. In our case, we don't want to do that, but rather we want to perform the network request, and then have its completion handler invoke the segue programmatically. So, we have to create a segue that can be performed programmatically and then hook your button up to an @IBAction that performs the network request and, if appropriate, performs the segue programmatically. But, note, there should be no segue hooked up to the button directly. We'll do that programmatically.

For example:

  1. Define the segue to be between the two view controllers by control-dragging from the view controller icon in the bar above the first scene to the second scene:

    enter image description here

  2. Give that segue a storyboard identifier by selecting the segue and going to the "Attributes Inspector" tab:

    enter image description here

  3. Hook up the button (or whatever is going to trigger this segue) to an @IBAction.

    enter image description here

  4. Write an @IBAction that performs network request and, upon completion, programmatically invokes that segue:

    @IBAction func didTapButton(_ sender: Any) {
        let request = URLRequest(...).       // prepare request however your app requires
    
        let waitingView = showWaitingView()  // present something so that the user knows some network request is in progress
    
        // perform network request
    
        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            // regardless of how we exit this, now that request is done, let's 
            // make sure to remove visual indication that network request was underway
    
            defer {
                DispatchQueue.main.async {
                    waitingView.removeFromSuperview()
                }
            }
    
            // make sure there wasn't an error; you'll undoubtedly have additional
            // criteria to apply here, but this is a start
    
            guard let data = data, error == nil else {
                print(error ?? "Unknown error")
                return
            }
    
            // parse and process the response however is appropriate in your case, e.g., if JSON:
            //
            // guard let responseObject = try? JSONSerialization.jsonObject(with data) else {
            //     // handle parsing error here
            //     return
            // }
            //
            // // do whatever you want with the parsed JSON here
    
            // do something with response
    
            DispatchQueue.main.async {
                performSegue(withIdentifier: "SegueToSceneTwo", sender: self)
            }
        }
        task.resume()
    }
    
    /// Show some view so user knows network request is underway
    ///
    /// You can do whatever you want here, but I'll blur the view and add `UIActivityIndicatorView`.
    
    private func showWaitingView() -> UIView {
        let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .Dark))
        effectView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(effectView)
        NSLayoutConstraint.activateConstraints([
            effectView.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor),
            effectView.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor),
            effectView.topAnchor.constraintEqualToAnchor(view.topAnchor),
            effectView.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor)
        ])
    
        let spinner = UIActivityIndicatorView(activityIndicatorStyle: .WhiteLarge)
        effectView.addSubview(spinner)
        spinner.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activateConstraints([
            spinner.centerXAnchor.constraintEqualToAnchor(view.centerXAnchor),
            spinner.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor)
        ])
        spinner.startAnimating()
    
        return effectView
    }
    

Upvotes: 2

Related Questions