24

I have a WKWebView which should load the following url:

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

Her is the code I use:

import UIKit
import WebKit


class MMWKBrowserController: UIViewController {

  private let closeButtonSelector: Selector = "closeButtonTapped:"

  private var urlString: String
  private let request: NSMutableURLRequest

  private var webView: WKWebView!
  private var twoLineTitleView: UIView!
  private var titleLabel: UILabel?
  private var subTitleLabel: UILabel?
  private var indicator: UIActivityIndicatorView!


  init(urlString: String) {
    self.urlString = urlString

    println("*** Using MMWKBrowserController ***")

    var url: NSURL? = NSURL(string: urlString)
    if url == nil {
      var escapedString: String = urlString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
      self.urlString = escapedString
      url = NSURL(string: escapedString)
    }

    println("url: \(url)")
    request = NSMutableURLRequest(URL: url!)

    request.setValue("Mozilla/5.0 (iPhone; CPU iPhone OS 8_4 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12H141 Safari/600.1.4", forHTTPHeaderField: "UserAgent")

    super.init(nibName: nil, bundle: nil)
  }

  required init(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }


  deinit {
    self.webView.removeObserver(self, forKeyPath: "loading")
    self.webView.removeObserver(self, forKeyPath: "title")
    self.webView.removeObserver(self, forKeyPath: "URL")
    self.webView.removeObserver(self, forKeyPath: "estimatedProgress")
    self.webView.stopLoading()
  }


  override func viewDidLoad() {
    super.viewDidLoad()
    createNavigationView()


    self.navigationController?.navigationBar.tintColor = MGColor.actionColor

    let config = WKWebViewConfiguration()
    self.webView = WKWebView(frame: self.view.bounds, configuration: config)
    self.view.addSubview(self.webView)


    indicator = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.Gray)
    //indicator.backgroundColor = UIColor(white: 0.1, alpha: 0.5)
    webView.addSubview(indicator)

    self.webView.snp_makeConstraints { (make) -> Void in
      make.edges.equalTo(self.view)
    }

    indicator.snp_makeConstraints { (make) -> Void in
      make.center.equalTo(self.webView)
    }

    webView.addObserver(self, forKeyPath: "loading", options: NSKeyValueObservingOptions.New, context: nil)
    webView.addObserver(self, forKeyPath: "title", options: NSKeyValueObservingOptions.New, context: nil)
    webView.addObserver(self, forKeyPath: "URL", options: NSKeyValueObservingOptions.New, context: nil)
    webView.addObserver(self, forKeyPath: "estimatedProgress", options: NSKeyValueObservingOptions.New, context: nil)
  }


  override func viewDidDisappear(animated: Bool) {
    super.viewDidDisappear(animated)
    self.webView.stopLoading()
  }


  private func createNavigationView() {
    let closeItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Stop, target: self, action: closeButtonSelector)
    self.navigationItem.leftBarButtonItem = closeItem

    // create center view
    let titleViewWidth = self.view.frame.size.width - 100

    twoLineTitleView = UIView(frame: CGRectMake(0, 0, titleViewWidth, 44))

    titleLabel = UILabel(frame: CGRectMake(0, 6, titleViewWidth, 16))
    titleLabel?.backgroundColor = UIColor.clearColor()
    titleLabel?.font = UIFont.boldSystemFontOfSize(16)
    titleLabel?.textAlignment = NSTextAlignment.Center

    subTitleLabel = UILabel(frame: CGRectMake(0, 21, titleViewWidth, 20))
    subTitleLabel?.backgroundColor = UIColor.clearColor()
    subTitleLabel?.font = UIFont.systemFontOfSize(10)
    subTitleLabel?.textAlignment = NSTextAlignment.Center

    twoLineTitleView.addSubview(titleLabel!)
    twoLineTitleView.addSubview(subTitleLabel!)
    self.navigationItem.titleView = twoLineTitleView
  }



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

    self.webView.loadRequest(self.request)

  }


  func closeButtonTapped(sender: UIBarButtonItem) {
    self.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
  }


  override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {

    if let wk = object as? WKWebView {
      switch keyPath {
      case "loading":
        if let val: AnyObject = change[NSKeyValueChangeNewKey] {
          if let val = val as? Bool {
            if val {
              self.indicator.startAnimating()
            }
            else {
              self.indicator.stopAnimating()
            }
          }
        }
      case "title":
        self.titleLabel?.text = self.webView.title
      case "URL":
        self.subTitleLabel?.text = self.webView.URL?.URLString
      case "estimatedProgress":
        println("progress: \(Int32(self.webView.estimatedProgress*100))")

      default: break
      }
    }
  }


}

Note: I use SDK iOS 8.4

Why does mobile Safari loads this url but WKWebView does not?

Michael
  • 32,527
  • 49
  • 210
  • 370
  • Which SDK you are using ? iOS8 or iOS 9 ? – itsji10dra Sep 07 '15 at 11:46
  • this part of url loads fine : http://buchung.salonmeister.de, the remaining part may have some characters not rexognised by the webview kit. Try with different selection. – Teja Nandamuri Sep 07 '15 at 12:35
  • 1
    Your webView works for me once I've added `webView.loadRequest(request)` (see [screenshot](https://www.evernote.com/shard/s236/sh/77ddc69e-f4a7-4745-82eb-44f85e15969e/32db870b5d0e7b40/res/4f291e0e-31cf-47fb-9c71-0dd2ea8a1197/skitch.png)) but note that your URL seems to point to a page that's no longer here. – Eric Aya Sep 07 '15 at 12:42
  • Load the URL in mobile Safari an you see that the page loads. – Michael Sep 08 '15 at 05:22
  • You may need to add the `www` to your string, apple seems to be very picky in their format. – Cyber Dec 28 '17 at 21:09
  • check https://stackoverflow.com/a/59391866/810466 – Tomer Even Dec 18 '19 at 12:21

6 Answers6

30

Add this to your plist

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

Here's the explanation for this change in 9.0 http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/

Also if you want to set it up more secure it gives you a more complex way to do that.

Randall Meyer
  • 325
  • 2
  • 6
  • 2
    Josh, could you explain why AppStore should reject your app due to these lines in plist? – Werewolf Apr 26 '18 at 11:05
  • I found out why it's rejected sometimes from Apple. If you go to the link the author posted, there is a superior way to do this in which you specify the actual domain names of the websites you are accessing for security reasons. It also warns that allowing arbitrary loads it's not recommended. – Joseph Astrahan Feb 11 '20 at 01:39
22

For me, the issue was caused by server trust check from the WKWebView.

To fix this I had to handle the challenge authentication callback and return a server trust credential.

Swift 4

func webView(_ webView: WKWebView, 
    didReceive challenge: URLAuthenticationChallenge, 
    completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) 
{
    if(challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust)
    {
        let cred = URLCredential(trust: challenge.protectionSpace.serverTrust!)
        completionHandler(.useCredential, cred)
    }
    else
    {
        completionHandler(.performDefaultHandling, nil)
    }
}
Matthew Cawley
  • 2,828
  • 1
  • 31
  • 42
6

If you are in a sandboxed macOS app, you'll need to set the Outgoing Connections (Client) capability, you won't need to mess with Allow Abitrary Loads which shouldn't come into play for trusted https requests. Xcode Capabilities Screenshot showing the Outgoing Connections (Client) checkbox checked

For iOS however, allowing client connections is the default, so you may need to implement WKNavigationDelegate methods to handle security. Make sure you aren't just trusting untrusted certificates though. This Swift Talk video from objc.io is the best resource I know of, definitely worth 20 minutes if you're working in this area: https://talk.objc.io/episodes/S01E57-certificate-pinning

nteissler
  • 1,513
  • 15
  • 16
4

I had a similar problem with a site that was also protected with a high security TLS 1.2 certificate. To get the WKWebView to accept the server's certificate, I added this code to my web view controller delegate:

-(void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler
{
    if ([[[challenge protectionSpace]authenticationMethod] isEqualToString: @"NSURLAuthenticationMethodServerTrust"]) {
        SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
        CFDataRef exceptions = SecTrustCopyExceptions(serverTrust);
        SecTrustSetExceptions(serverTrust, exceptions);
        CFRelease(exceptions);
        newCredential = [NSURLCredential credentialForTrust:serverTrust];
        completionHandler(NSURLSessionAuthChallengeUseCredential, newCredential);
    } else {
        completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, newCredential);
    }
}
saswanb
  • 1,975
  • 1
  • 17
  • 25
1

not sure if the same error reason, but the problem was the same for me under iOS9

some domains couldn't be loaded

turned out that the problem was in

- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler {

and providing back

completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);

where I should have returned

completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);

I was using WRONG code from https://github.com/ShingoFukuyama/WKWebViewTips

Michael
  • 32,527
  • 49
  • 210
  • 370
Peter Lapisu
  • 19,915
  • 16
  • 123
  • 179
  • I did not get your point. Could you please be more detailed in your description? I do not use the method ``didReceiveAuthenticationChallenge``. – Michael Oct 06 '15 at 12:39
  • i did encounter similar error behaviour, where it seemed that the webview is not loading https, turned out it was a problem in didReceiveAuthenticationChallenge – Peter Lapisu Oct 06 '15 at 15:10
  • Could you please post more code. How does your ``didReceiveAuthenticationChallenge`` method looks like? – Michael Oct 06 '15 at 18:00
  • try just to return completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); if it solves your problem or not – Peter Lapisu Oct 06 '15 at 18:12
  • What does this line do? – Michael Oct 06 '15 at 18:25
0
// Add plist file 
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
    <key>google.com</key>
    <dict>
        <key>NSExceptionAllowsInsecureHTTPLoads</key>
        <true/>
        <key>NSIncludesSubdomains</key>
        <true/>
    </dict>
</dict>

if WKWebView not support then declare .m file below code:

@interface WebScannerViewController()
{

 WKWebView *webView;

}


@end



@implementation WebScannerViewController

 - (void)viewDidLoad   {

    [super viewDidLoad];
    webView.hidden=YES;

    webView.UIDelegate = self;
    webView.navigationDelegate = self;
    self.loadingSign.hidden = NO;


        webView.frame=CGRectMake(0, 94, Width, Height-128);
    }
Daniel Muñoz
  • 547
  • 1
  • 7
  • 23
Ramani Hitesh
  • 214
  • 3
  • 15
  • Thanks for showing the plist update above, that is the correct way to do it so that apple does not reject the app for security reasons. – Joseph Astrahan Feb 11 '20 at 01:38