4

I have a SFSafariViewController opening at the click of a button inside a UIActionSheet. It has been working fine and is still working fine on all the versions of iOS except iOS 11. Is there something they have changed regarding the SFSafariViewController in iOS 11 or in Xcode 9.0 that might have caused this issue?

UPDATE - So it seems like its Xcode 9.0 that is causing this issue. I have tried running it on different iOS versions and all of them seem to be giving this issue. It used to work fine when I ran it using Xcode 8.3.3, something I don't have anymore :(

Here's the code -

- (void)presentWebView:(NSString *)url {
url = [url stringByReplacingOccurrencesOfString:@" " withString:@"+"];
url = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *URL = [NSURL URLWithString:url];

if (URL) {
    if ([SFSafariViewController class] != nil) {
        SFSafariViewController *sfvc = [[SFSafariViewController alloc] initWithURL:URL];
        sfvc.delegate = self;
        [self.tabBarController presentViewController:sfvc animated:YES completion:nil];
    } else {
        if (![[UIApplication sharedApplication] openURL:URL]) {
            NSLog(@"%@%@",@"Failed to open url:",[url description]);
        }
    }
} else {
    // will have a nice alert displaying soon.
}

}

genaks
  • 757
  • 2
  • 10
  • 24

6 Answers6

7

I've managed to fix this in my code. I hope this helps anyone else with a similar problem.

I had the exact same problem as described here. I tried everything above and unfortunately nothing worked.

In my app there were different windows. The fix was to ensure the window that would show SFSafariViewController was 'key' before presenting it. For example:

class MyViewController: UIViewcontroller {
    func showSafariViewController() {
        // imagine we have 2 windows, one for 'normal' content (key window) and one for 'other' content. lets say we're showing the 'other' content window, and have hidden the 'normal' window. you can see the 'other' content window in the app, but it won't be the key window!
        let window = // get the 'other' content window

        // make sure we make it the key window before presenting safari
        window.makeKey()

        // now present safari and celebrate victory by triumphantly slurping on your hot black coffee
        let mySafariViewController = SFSafariViewController(...)
        self.present(mySafariViewController ...)
    }
}

I suspect Apple are searching for a SFSafariViewController instance in the window UIApplication.shared.keyWindow. Perhaps they're adding a child view from somewhere else. In the documentation it states The user's activity and interaction with SFSafariViewController are not visible to your app, so perhaps it's the bug is something related to an added level of security https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller

larromba
  • 306
  • 2
  • 11
  • This fixed my problem! Thank you! PS: You can easily get the root window by using `UIApplication.shared.windows[0]` (maybe that'll help someone) – Julien Perrenoud Jan 10 '18 at 01:28
  • I'm using the method like suggested but getting issue - Unbalanced calls to begin/end appearance transitions for . – Apple May 17 '18 at 14:46
  • @Apple unfortunately i'm not sure what to advise without seeing any code - there could be a few reasons for this that are unrelated to this thread. might be worth starting a new thread? else maybe read this and see if it helps https://stackoverflow.com/questions/9088465/unbalanced-calls-to-begin-end-appearance-transitions-for-detailviewcontroller?answertab=votes#tab-top – larromba Jun 22 '18 at 19:59
5

I have tried to do it using delay and make view of controller loading.Both are working for me.

Method 1. Using delay.

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
      let controller = SFSafariViewController(url: url)
      controller.modalPresentationStyle = .overFullScreen
      self.present(controller, animated: true, completion: nil)
      controller.delegate = self
    }

Method 2. Loading view.

      let controller = SFSafariViewController(url: url)
      let _ = controller.view
      controller.modalPresentationStyle = .overFullScreen
      self.present(controller, animated: true, completion: nil)
      controller.delegate = self
Surjeet Rajput
  • 1,251
  • 17
  • 24
  • 3
    Method two fixed the issue on iOS 12 with Xcode 11. The issue doesn't show up on iOS 13. – Totka Nov 27 '19 at 10:11
3

Very similar to https://openradar.appspot.com/29108332

To fix it, you can disable the lazy loading of the view:

SFSafariViewController *viewController = [[SFSafariViewController alloc] init...];
(void)viewController.view;
...
[controller presentViewController:viewController animated:YES completion:nil];
Evgeniy Yurtaev
  • 264
  • 2
  • 6
  • by the way what does this - (void)viewController.view; - do? – genaks Sep 27 '17 at 15:41
  • this causes the view to load: ["If you access this property and its value is currently nil, the view controller automatically calls the loadView() method and returns the resulting view."](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621460-view) – Evgeniy Yurtaev Sep 28 '17 at 06:38
  • 1
    Worked for me!! – Swift Rabbit Feb 19 '19 at 22:00
1

Update

Turns out the old answer didn't work, it just worked since I put breakpoints. If I added a thread sleep seems it worked in XCode9, but that's not the best solution. Anyone have another better solution?

SFSafariViewController *sfcontroller = [[SFSafariViewController alloc] initWithURL:url];
if (@available(iOS 11.0, *)) {
    [NSThread sleepForTimeInterval:0.5f];
}
sfcontroller.delegate = self;
[controller presentViewController:sfcontroller animated:NO completion:nil];

Old Answer

I have the same issue as genaks and tinkered around with SFViewController.

Seems like this code works for me

SFSafariViewController *sfcontroller = [[SFSafariViewController alloc] initWithURL:url];
if (@available(iOS 11.0, *)) {
    SFSafariViewControllerConfiguration *config = [[SFSafariViewControllerConfiguration alloc] init];
    config.barCollapsingEnabled = NO;
    sfcontroller = [[SFSafariViewController alloc] initWithURL:url configuration: config];
} else {
    // Fallback on earlier versions
}

sfcontroller.delegate = self;

[controller presentViewController:sfcontroller animated:YES completion:nil];

In ios 11, they introduce SFSafariViewControllerConfiguration, and by default the barCollapsingEnabled is true and it seems the one that causing my blank SafariView. Hope this solves yours too

0

We had this issue: our URL was https://our-side.com/index.html (an Angular site that would redirect to the /account route). When we removed the index.html, the SFSafariViewController loaded correctly!

Max Chuquimia
  • 7,494
  • 2
  • 40
  • 59
-3

The only thing that has worked so far for me is to make the SafariViewController the rootviewcontroller in the following manner -

    ((XYZAppDelegate *)[UIApplication sharedApplication].delegate).window.rootViewController = self.svc;
    [((XYZAppDelegate *)[UIApplication sharedApplication].delegate).window makeKeyAndVisible];
genaks
  • 757
  • 2
  • 10
  • 24