Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIWebView does not load HTTPS Page: Error Domain=NSURLErrorDomain Code=-999 "The operation couldn’t be completed. (NSURLErrorDomain error -999.)"

Note: This question is still unanswered!

I use a UIWebView to load the following URLs:

https://buchung.salonmeister.de/ort/301655/menue/#offerId=907601&venueId=301655

https://buchung.salonmeister.de/place/#offer-details-page?id=907599&venueId=301655

http://feratel.lueneburger-heide.de/lhg/de/accommodation/detail/LUH/8634e147-e13d-40f5-8954-2ac40cfea2a7/romantik_hotel_bergström?customHeader=true

http://feratel.lueneburger-heide.de/lhg/de/accommodation/detail/LUH/8af6d1fd-af6d-4765-8025-9eb8fa05ea42/hotel%20undeloher%20hof?customHeader=true

Note that the above urls are not my urls so I cannot change their content.

When trying to load I get the following error from func webView(webView: UIWebView, didFailLoadWithError error: NSError):

Error Domain=NSURLErrorDomain Code=-999 "The operation couldn’t be completed. (NSURLErrorDomain error -999.)" UserInfo=0x7ff7d0fd6f20 {NSErrorFailingURLStringKey=https://buchung.salonmeister.de/ort/301655/menue/#offerId=907601&venueId=301655, NSErrorFailingURLKey=https://buchung.salonmeister.de/ort/301655/menue/#offerId=907601&venueId=301655}

I tested the code below on iOS7 and iOS8. Mobile Safari loads this page without any problems but in my UIWebView I see nothing.

Here is the code I use:

class ViewController: UIViewController, UIWebViewDelegate, NSURLConnectionDataDelegate {
  @IBOutlet weak var webView: UIWebView!
  var request: NSURLRequest!
  var urlString: String!
  private var isDone: Bool = false
  private var failedRequest: NSURLRequest!

  override func viewDidLoad() {
    super.viewDidLoad()

    urlString = "https://buchung.salonmeister.de/ort/301655/menue/#offerId=907601&venueId=301655"
    request = NSURLRequest(URL: NSURL(string: urlString)!)
    webView.delegate = self
  }

  override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    self.webView.loadRequest(self.request)
  }

  // MARK: UIWebViewDelegate

  func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
    println("shouldStartLoadWithRequest()")

    if !isDone {
      isDone = false

      println("shouldStartLoadWithRequest() 111")
      failedRequest = request
      webView.stopLoading()
      var connection = NSURLConnection(request: request, delegate: self, startImmediately: true)
      connection!.start()
      //      NSURLConnection(request: request, delegate: self)
      return false
    }
    println("shouldStartLoadWithRequest() -----------------------")
    return true
  }

  func webViewDidStartLoad(webView: UIWebView) {
    println("webViewDidStartLoad()")
  }

  func webViewDidFinishLoad(aWebView: UIWebView) {
    println("webViewDidFinishLoad()")
  }

  func webView(webView: UIWebView, didFailLoadWithError error: NSError) {
    println("webView(): didFailLoadWithError(): \(error)")
  }

  // MARK: NSURLConnectionDataDelegate

  func connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge) {
    println("connection willSendRequestForAuthenticationChallenge")

    if challenge.previousFailureCount == 0 {
      self.isDone = true
      println("x1")
      let credential: NSURLCredential = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!)
      challenge.sender.useCredential(credential, forAuthenticationChallenge: challenge)

    }
    else {
      println("x2")
      challenge.sender.cancelAuthenticationChallenge(challenge)
    }
  }

  func connection(connection: NSURLConnection, didReceiveResponse response: NSURLResponse) {
    println("connection didReceiveResponse")
    self.isDone = true

    connection.cancel()
    self.webView.loadRequest(self.failedRequest)
  }

  func connection(connection: NSURLConnection, canAuthenticateAgainstProtectionSpace protectionSpace: NSURLProtectionSpace) -> Bool {
    println("connection canAuthenticateAgainstProtectionSpace")
    return protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust
  }
}

I created a sample project with the above code here: https://github.com/confile/UIWebView-https-url

How do I get my UIWebView load the url correctly?

like image 363
confile Avatar asked Aug 20 '15 15:08

confile


1 Answers

I ran your code. It worked fine approximately 50% of the time, and showed the -999 error the other times.

That error code is NSURLErrorCancelled, as per URL Loading System Error Codes in Apple's Foundation Constants Reference. So something is cancelling the page load.

I printed the page content that was received, using this:

println(aWebView.stringByEvaluatingJavaScriptFromString("document.body.innerHTML"));

I see that the Javascript in the page contains this:

if (mobilePath) {
   document.body.style.display = 'none';
   document.location = mobilePath;  
}

My guess is that this Javascript starts to run as soon as the content is loaded. This results in a race condition where sometimes the page is immediately redirected to a different document.location, and so the first page load is cancelled before it loads completely.

You should change the server so that it does not send this Javascript.

Alternatively, if you just want to load the website like a normal website, then you can let UIWebView handle the redirects for you. In the case of this site, I got it working properly by setting the User-agent to pretend to be mobile Safari. I also removed shouldStartLoadWithRequest because it was only interfering with things.

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    var request = NSMutableURLRequest(URL: NSURL(string: urlString)!)
    var userAgent = ["UserAgent": "mozilla/5.0 (iphone; cpu iphone os 7_0_2 like mac os x) applewebkit/537.51.1 (khtml, like gecko) version/7.0 mobile/11a501 safari/9537.53"]
    NSUserDefaults.standardUserDefaults().registerDefaults(userAgent as [NSObject : AnyObject])

    self.webView.loadRequest(request)
  }

If I don't have the code to pretend to be mobile Safari, then the page content loads but it doesn't do the right thing. It just stops at a spinner. I presume that the website is sending different Javascript depending on which rendering engine it thinks it is talking to. If you don't specify any User-agent then you are at the mercy of whatever their web designer has done in terms of specifying the default behavior.

like image 114
Ewan Mellor Avatar answered Sep 21 '22 20:09

Ewan Mellor