Swiftpack.co - Package - stleamist/BetterSafariView

version Swift: 5.1+ iOS: 13.0+ SwiftPM: compatible license contact: @stleamist

BetterSafariView

A better way to present a SFSafariViewController or start a ASWebAuthenticationSession in SwiftUI.

Contents

Motivation

SwiftUI is a strong, intuitive way to build user interfaces, but was released with some part of existing elements missing. One example of those missing elements is the SFSafariViewController.

Fortunately, Apple provides a way to wrap UIKit elements into SwiftUI views. A common approach to place the SFSafariViewController inside SwiftUI is to create a simple view representing a SFSafariViewController, then present it with a sheet(isPresented:onDismiss:content:) modifier or a NavigationLink button (See RootView.swift in the demo project).

However, there’s a problem in this approach: it can’t present the SFSafariViewController with its default presentation style — a push transition covers full screen. A sheet modifier can present the view only in a modal sheet, and a navigation link shows the two navigation bars at the top so we have to deal with them. This comes down to the conclusion that there’s no option to present it the right way except for using present(_:animated:completion:) method of a UIViewController instance, but it is prohibited and not a good design to access the UIHostingController directly from the SwiftUI view.

BetterSafariView clearly achieves this goal by hosting a simple UIViewController to present a SFSafariViewController as a view’s background. In this way, a ASWebAuthenticationSession is also able to be started without any issue in SwiftUI.

Usage

With the following modifiers, you can use it in a similar way to present a sheet.

SafariView

Modifiers

.safariView(isPresented:onDismiss:content:)
.safariView(item:onDismiss:content:)

Example

import SwiftUI
import BetterSafariView

struct ContentView: View {
    
    @State private var presentingSafariView = false
    
    var body: some View {
        Button(action: {
            self.presentingSafariView = true
        }) {
            Text("Present SafariView")
        }
        .safariView(isPresented: $presentingSafariView) {
            SafariView(
                url: URL(string: "https://github.com/")!,
                configuration: SafariView.Configuration(
                    entersReaderIfAvailable: false,
                    barCollapsingEnabled: true
                )
            )
            .preferredBarAccentColor(.clear)
            .preferredControlAccentColor(.accentColor)
            .dismissButtonStyle(.done)
        }
    }
}

WebAuthenticationSession

Modifiers

.webAuthenticationSession(isPresented:content:)
.webAuthenticationSession(item:content:)

Example

import SwiftUI
import BetterSafariView

struct ContentView: View {
    
    @State private var startingWebAuthenticationSession = false
    
    var body: some View {
        Button(action: {
            self.startingWebAuthenticationSession = true
        }) {
            Text("Start WebAuthenticationSession")
        }
        .webAuthenticationSession(isPresented: $startingWebAuthenticationSession) {
            WebAuthenticationSession(
                url: URL(string: "https://github.com/login/oauth/authorize")!,
                callbackURLScheme: "github"
            ) { callbackURL, error in
                print(callbackURL, error)
            }
            .prefersEphemeralWebBrowserSession(false)
        }
    }
}

Known Issues

  • In .webAuthenticationSession(item:content:) modifier, the functionality that replaces a session on the item's identity change is not implemented, as there is no non-hacky way to be notified when the session's dismissal animation is completed.

Requirements

  • Swift 5.1+
  • iOS 13.0+

Installation

Swift Package Manager

Add the following line to the dependencies in your Package.swift file:

.package(url: "https://github.com/stleamist/BetterSafariView.git", .upToNextMajor(from: "2.2.2"))

Next, add BetterSafariView as a dependency for your targets:

.target(name: "MyTarget", dependencies: ["BetterSafariView"])

Your completed description may look like this:

// swift-tools-version:5.1

import PackageDescription

let package = Package(
    name: "MyPackage",
    dependencies: [
        .package(url: "https://github.com/stleamist/BetterSafariView.git", .upToNextMajor(from: "2.2.2"))
    ],
    targets: [
        .target(name: "MyTarget", dependencies: ["BetterSafariView"])
    ]
)

Xcode

Select File > Swift Packages > Add Package Dependency, then enter the following URL:

https://github.com/stleamist/BetterSafariView.git

For more details, see Adding Package Dependencies to Your App.

Demo

You can compare the behavior of BetterSafariView with the other ways above in the demo project. Check out the demo app by opening BetterSafariView.xcworkspace.

NOTE: This demo project is designed for iOS 14.0+, though the package is compatible with iOS 13.0+.

License

BetterSafariView is released under the MIT license. See LICENSE for details.

Github

link
Stars: 44

Dependencies

Used By

Total: 0

Releases

- 2020-09-18 23:55:02

Fixed

  • Fixed an issue where the changes of SafariView and WebAuthenticationSession is not applied after an initialization.

- 2020-08-26 12:23:28

Fixed

  • Fixed an issue where the package could not be compiled on Swift 5.2 or earlier.

- 2020-08-26 09:09:04

Added

  • SafariView now conforms to View protocol, so it can be used even in the .sheet() or the .fullScreenCover() modifiers for the advanced usage.
  • Added accentColor(_:) modifier to SafariView as a convenience method of preferredControlAccentColor(_:).
  • Added a new initializer of WebAuthenticationSession where the onCompletion closure receives a Result instance, which contains either a URL or an Error.

Fixed

  • Fixed typos on the markup.

- 2020-08-24 07:49:19

Changed

  • Coordinators are now in charge of view controller presentations, following the structure of VisualEffects.

- 2020-08-22 02:55:58

Fixed

  • Fixed typos on the markup.

- 2020-08-16 08:05:38

Added

  • You can now authenticate a user through a web authentication session by using WebAuthenticationSession.
  • With the new SafariView representation and its modifiers, configurations and properties on SFSafariViewController also could be used.
  • Using safariView(isPresented:onDismiss:content:) modifier, actions could be performed when the Safari view dismisses.
  • Using safariView(item:onDismiss:content:) modifier, the Safari view could be replaced on the item's identity change.

Changed

  • The package has been renamed to BetterSafariView from FullScreenSafariView.
  • safariView(isPresented:content:) modifier now gets a closure returning a SafariView representation instead of a URL instance.

Fixed

  • Fixed an issue where the dismissed Safari view is presented and dismissed again on iOS 14.
  • Fixed an issue where page loading and parallel push animation are not working when a modifier is attached to the view in a List.
  • Improved stability during the SwiftUI view update process.

- 2020-05-18 08:56:39

  • Initial release