Can I preload the web content for Safari View Controller?

You could try using a http cache, but I don't think it would work as the Safari View Controller is working as a separate process (probably the same as Safari), so that's why it e.g. circumvents ATS.

The only way I can think of this working is to somehow force the user's Safari to load it? openURL: or adding to Reading List maybe? This doesn't sound like a viable solution.

You can always experiment with custom presentation of the view controller, attach it the view hierarchy, trigger appearance events, but set its frame to CGRectMake(0,0,1,1) or attach it somewhere off-screen, then wait a while and represent it with a correct frame.


Sadly this behaviour is not supported with the current implementation of SFSafariViewController. I would encourage filing a radar with Apple to add support for this behaviour but like others have suggested your best bet is to use WKWebView and start loading before its added to the hierarchy.

I came across a lovely radar from Twitter that actually mentions exactly what you're asking for. I think you might find the following requests useful:

High Priority: - Ability to warm the SFSafariViewController before actually presenting it with a URL, URL request, HTML data or file on disk - Currently, are investing heavily into warming the shared URL cache for high priority Tweets so that if the user hits that Tweet we will open UIWebView (sadly not WKWebView) with that pre-cached web page. If we could just warm an SFSafariViewController with the desired link, this would eliminate an enormous amount of effort on our end.

You can see in their implementation they simply cache responses using UIWebView since WKWebView seems to obfuscate the caching semantics a bit. The only risk is that UIWebView is a likely candidate for deprecation as you see in their docs "In apps that run in iOS 8 and later, use the WKWebView class instead of using UIWebView."

So unfortunately it seems that their are many hoops you need to jump through to get this all going so your best bet for now is to just pester Apple and dupe Twitters radar.


Here is a solution. Obviously, if you click on the button right away you'll see the loading. But basically, I load the Browser and put the view behind another one and I put a button in this other view.

When you press the button, the browser is bring to the front, already loaded. The only problem here is that I'm not using any transition but that's one solution at least.

import UIKit
import SafariServices

class ViewController: UIViewController {
  var svc = SFSafariViewController(URL: NSURL(string: "https://microsoft.com/")!, entersReaderIfAvailable: true)
  var safariView:UIView?
  let containerView = UIView()
  let btn = UIButton()

  override func viewDidLoad() {
    super.viewDidLoad()
    //let tmpView = svc.view
    addChildViewController(svc)
    svc.didMoveToParentViewController(self)
    svc.view.frame = view.frame
    containerView.frame = view.frame
    containerView.backgroundColor = UIColor.redColor()
    safariView = svc.view
    view.addSubview(safariView!)
    view.addSubview(containerView)

    btn.setTitle("Webizer", forState: UIControlState.Normal)
    btn.titleLabel!.textColor = UIColor.blackColor()
    btn.addTarget(self, action: "buttonTouched:", forControlEvents: .TouchUpInside)
    btn.frame = CGRectMake(20, 50, 100, 100)
    containerView.addSubview(btn)

    view.sendSubviewToBack(safariView!)

    // Do any additional setup after loading the view, typically from a nib.
  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
  }

  @IBAction func buttonTouched(sender: AnyObject) {
    view.bringSubviewToFront(safariView!)
    //self.presentViewController(svc, animated: true, completion: nil)
  }


}