In iOS 9, why is SFSafariViewController is being pushed instead of presented modally?

Here's a simple way to obtain a vertical modal presentation of a SFSafariViewController:

let safari = SFSafariViewController(URL: url)
safari.modalPresentationStyle = .overFullScreen
presentViewController(safari, animated: true, completion: nil)

I just had the same issue. Also, the done button work even if you don't set up the delegate. Not sure why it happens. However, I found a workaround: wrapping the safari controller in a navigation controller and hiding the navigation bar.

func openURL(url:NSURL) {

    if #available(iOS 9.0, *) {
        let safariController = SFSafariViewController(url: url)
        safariController.delegate = self
        let navigationController = UINavigationController(rootViewController: safariController)
        navigationController.setNavigationBarHidden(true, animated: false)
        self.present(navigationController, animated: true, completion: nil)
    } else {
        UIApplication.sharedApplication().openURL(url)
    }
}

To use the default modal transition style, you can simply set the transitioning delegate equal to self.

let svc = SFSafariViewController(url: url)
svc.transitioningDelegate = self //use default modal presentation instead of push
present(svc, animated: true, completion: nil)

You'll need to adopt the UIViewControllerTransitioningDelegate protocol in your view controller, but there are no required functions to implement.

This was mentioned in Session 225 at WWDC, What's New in Safari View Controller.