Search code examples
iosswiftuiwebview

Load WebView before transitioning to it


I had to update an app that i didn't touch for quite a while and now am facing an 'Fatal error: Unexpectedly found nil while unwrapping an Optional value' in DetailViewController::refreshWebView() when executing webView.loadRequest(myRequest) because webView is nil at this point in time.

I didn't change anything relating to how the DetailViewController is loaded and assigned to MasterViewController, so I am very confused as why this does not work anymore.

Was there something changed that i am not aware of? Or did I implement this whole thing incorrectly in the first place and it was coincidence that it worked?

import UIKit

protocol EventSelectionDelegate: class {
    func eventSelected(_ newEvent: String)
}

class MasterViewController: UIViewController, UIWebViewDelegate {

    var activityView: UIActivityIndicatorView!
    weak var delegate: EventSelectionDelegate?

    func detailChosen(_ detailUrlString: String) {
        delegate?.eventSelected(detailUrlString)
        activityView.startAnimating()
    }

    func transitionToDetail() {
        if let detailViewController = self.delegate as? DetailViewController {
            DispatchQueue.main.async {
                self.activityView.stopAnimating()
                self.splitViewController?.showDetailViewController(detailViewController, sender: nil)
            }
        }
    }
}

// Helper function inserted by Swift 4.2 migrator.
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
    return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
}
import UIKit

class DetailViewController: UIViewController, UIWebViewDelegate {

    @IBOutlet weak var webView: UIWebView!

    var animationWaitDelegate: MasterViewController!
    var eventUrl: String! {
        didSet {
            self.refreshWebView()
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        webView.delegate = self
    }

    func refreshWebView() {
        let myURL = URL(string:eventUrl!)
        let myRequest = URLRequest(url: myURL!)
        webView.loadRequest(myRequest)
    }

    func webViewDidFinishLoad(_ webView: UIWebView) {
        animationWaitDelegate.transitionToDetail()
    }
}

extension DetailViewController: EventSelectionDelegate {
    func eventSelected(_ newEvent: String) {
        eventUrl = newEvent
    }
}

// Helper function inserted by Swift 4.2 migrator.
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
    return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
}

PS: I found a workaround in the meantime where I added a flag in DetailViewController that allows me to call refreshWebView in viewDidLoad if webView was nil the first time it was called.


Solution

  • First you have to update following code for null check, which will prevent crashing your app.

    func refreshWebView() {
        if webView != nil {
             let myURL = URL(string:eventUrl!)
             let myRequest = URLRequest(url: myURL!)
             webView.loadRequest(myRequest)
        }
    }
    

    After at transition add following code will fix your issue.

    func transitionToDetail() {
        if let detailViewController = self.delegate as? DetailViewController {
            DispatchQueue.main.async {
                self.activityView.stopAnimating()
                detailViewController.loadViewIfNeeded()   // Add this line of code
                self.splitViewController?.showDetailViewController(detailViewController, sender: nil)
            }
        }
    }
    

    loadViewIfNeeded() method will load your controls before open screen if required.