Joe Huang
Joe Huang

Reputation: 6550

Can I preload the web content for Safari View Controller?

I can create Safari View Controller without problem:

let svc = SFSafariViewController(URL: NSURL(string: remote_url)!, entersReaderIfAvailable: true)
self.presentViewController(svc, animated: true, completion: nil)

Is there any way I can preload the URL before I present the view controller to the user?

For example, I can preload the URL (web content) in the background first, and after the user clicks on something, I can show the Safari View Controller with the content right away. The user will feel the page loading is faster or instant.

P.S. Workarounds/hacks are also acceptable. For example, using cache or starting the view controller in background, etc.

EDIT: please consider SFSafariViewController only.

Upvotes: 21

Views: 5907

Answers (5)

Jake Stoeffler
Jake Stoeffler

Reputation: 2875

While it's technically possible to use the solution above to achieve what you're asking, this may not pass App Store review. Per the SFSafariViewController docs:

In accordance with App Store Review Guidelines, this view controller must be used to visibly present information to users; the controller may not be hidden or obscured by other views or layers. Additionally, an app may not use SFSafariViewController to track users without their knowledge and consent.

Upvotes: 0

Sucharu Hasija
Sucharu Hasija

Reputation: 1126

you can download the web page using the following code . and represent it with the help of svc let data:NSData? do { let weatherData = try NSData(contentsOfURL: NSURL(string: remote_url)!, options: NSDataReadingOptions()) data = weatherData print(weatherData) } catch { print(error) }

and load it when you needed in the svc

Upvotes: 1

Mikael
Mikael

Reputation: 2395

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)
  }


}

Upvotes: 12

Alistra
Alistra

Reputation: 5195

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.

Upvotes: 2

Daniel Galasko
Daniel Galasko

Reputation: 24237

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.

Upvotes: 4

Related Questions