Can I preload the web content for Safari View Controller?
Asked Answered
S

5

21

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.

Slop answered 1/2, 2016 at 11:40 Comment(7)
SFSafariViewController may not support for preloading. you can use NSURLCache and preloading URL using NSURLRequest and NSURLConnection. Then whenever you want to load a url into SFSafariViewController it uses the cached requests per your cache policy.Giordano
@KetanP If SFSafariViewController will use the cached requests, this kind of preloading is also good enough. is there any further info about how to do it?Slop
@KetanP I can't find a way to achieve what you said. Are you sure SFSafariViewController will use the cache if preloaded in some way or it's just an assumption?Slop
I am not checked in 'SFSafariViewController ' It is working web view. So I assume that it may work for 'SFSafariViewController'.Giordano
@KetanP Do you have further information or reference about how to do it for web view?Slop
I will send you same after some time. I busy with some urgent task.Giordano
For reference, if someone is still wondering: Safari view controller does not use the app's cache. It loads and renders its content in a helper process, which cannot access any of the app's sandbox files, including URL cache.Textualism
T
12

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


}
Trader answered 12/2, 2016 at 7:25 Comment(1)
I tested your method and it works well except... the app will crash after 30+ tries. Please take a look at this SO question I asked for this issue, please help if you can: #35598949Slop
R
4

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.

Ret answered 9/2, 2016 at 7:56 Comment(1)
I seriously doubt Apple would ever support this. Twitter has no way of knowing whether or not a user will actually want to tap a "high priority tweet", so they're just wasting everybody's battery and data.Newsprint
P
2

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.

Permatron answered 9/2, 2016 at 20:58 Comment(3)
I have thought about the last option (present it in the background somehow), but I can't figure out a way.Slop
Try using - (void)beginAppearanceTransition:(BOOL)isAppearing animated:(BOOL)animated and actually presenting after some time.Permatron
Also try a custom transitionDelegate like here teehanlax.com/blog/custom-uiviewcontroller-transitions that will present it but somewhere off screen, and the represent it normally after a few secsPermatron
F
1

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

Flong answered 12/2, 2016 at 9:23 Comment(2)
This doesn't work. For one, you can't just "load" the HTML into an SFSafariViewController, you have to present it with a URL. Secondly, SFSafariViewController only accepts HTTP or HTTPS protocol URLs, so it can't read local files or data.Lindyline
This would kill all interactive JS based aspects of the pagePermatron
G
0

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.

Geoffrey answered 8/5, 2020 at 2:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.