mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2025-03-29 04:55:28 +00:00
Fix In-App Safari (#945)
* Fix In-App Safari * Open SFSafariViewController in a separate window
This commit is contained in:
parent
53f364b232
commit
94d50fafc4
1 changed files with 101 additions and 28 deletions
|
@ -14,20 +14,20 @@ private struct SafariRouter: ViewModifier {
|
|||
@EnvironmentObject private var preferences: UserPreferences
|
||||
@EnvironmentObject private var routerPath: RouterPath
|
||||
|
||||
@State private var presentedURL: URL?
|
||||
|
||||
@StateObject private var safariManager = InAppSafariManager()
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.environment(\.openURL, OpenURLAction { url in
|
||||
// Open internal URL.
|
||||
routerPath.handle(url: url)
|
||||
})
|
||||
.onOpenURL(perform: { url in
|
||||
.onOpenURL { url in
|
||||
// Open external URL (from icecubesapp://)
|
||||
let urlString = url.absoluteString.replacingOccurrences(of: "icecubesapp://", with: "https://")
|
||||
guard let url = URL(string: urlString), url.host != nil else { return }
|
||||
_ = routerPath.handle(url: url)
|
||||
})
|
||||
}
|
||||
.onAppear {
|
||||
routerPath.urlHandler = { url in
|
||||
if url.absoluteString.contains("@twitter.com"), url.absoluteString.hasPrefix("mailto:") {
|
||||
|
@ -45,31 +45,104 @@ private struct SafariRouter: ViewModifier {
|
|||
guard let scheme = url.scheme, ["https", "http"].contains(scheme.lowercased()) else {
|
||||
return .systemAction
|
||||
}
|
||||
|
||||
presentedURL = url
|
||||
return .handled
|
||||
return safariManager.open(url)
|
||||
}
|
||||
}
|
||||
.background {
|
||||
WindowReader { window in
|
||||
self.safariManager.windowScene = window.windowScene
|
||||
}
|
||||
}
|
||||
.fullScreenCover(item: $presentedURL, content: { url in
|
||||
SafariView(url: url, inAppBrowserReaderView: preferences.inAppBrowserReaderView)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
})
|
||||
}
|
||||
|
||||
struct SafariView: UIViewControllerRepresentable {
|
||||
let url: URL
|
||||
let inAppBrowserReaderView: Bool
|
||||
|
||||
func makeUIViewController(context _: UIViewControllerRepresentableContext<SafariView>) -> SFSafariViewController {
|
||||
let configuration = SFSafariViewController.Configuration()
|
||||
configuration.entersReaderIfAvailable = inAppBrowserReaderView
|
||||
|
||||
let safari = SFSafariViewController(url: url, configuration: configuration)
|
||||
safari.preferredBarTintColor = UIColor(Theme.shared.primaryBackgroundColor)
|
||||
safari.preferredControlTintColor = UIColor(Theme.shared.tintColor)
|
||||
return safari
|
||||
}
|
||||
|
||||
func updateUIViewController(_: SFSafariViewController, context _: UIViewControllerRepresentableContext<SafariView>) {}
|
||||
}
|
||||
}
|
||||
|
||||
private class InAppSafariManager: NSObject, ObservableObject, SFSafariViewControllerDelegate {
|
||||
var windowScene: UIWindowScene?
|
||||
let viewController: UIViewController = UIViewController()
|
||||
var window: UIWindow?
|
||||
|
||||
@MainActor
|
||||
func open(_ url: URL) -> OpenURLAction.Result {
|
||||
guard let windowScene = windowScene else { return .systemAction }
|
||||
|
||||
self.window = setupWindow(windowScene: windowScene)
|
||||
|
||||
let configuration = SFSafariViewController.Configuration()
|
||||
configuration.entersReaderIfAvailable = UserPreferences.shared.inAppBrowserReaderView
|
||||
|
||||
let safari = SFSafariViewController(url: url, configuration: configuration)
|
||||
safari.preferredBarTintColor = UIColor(Theme.shared.primaryBackgroundColor)
|
||||
safari.preferredControlTintColor = UIColor(Theme.shared.tintColor)
|
||||
safari.delegate = self
|
||||
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.viewController.present(safari, animated: true)
|
||||
}
|
||||
|
||||
return .handled
|
||||
}
|
||||
|
||||
func setupWindow(windowScene: UIWindowScene) -> UIWindow {
|
||||
let window = self.window ?? UIWindow(windowScene: windowScene)
|
||||
|
||||
window.rootViewController = viewController
|
||||
window.makeKeyAndVisible()
|
||||
|
||||
switch Theme.shared.selectedScheme {
|
||||
case .dark:
|
||||
window.overrideUserInterfaceStyle = .dark
|
||||
case .light:
|
||||
window.overrideUserInterfaceStyle = .light
|
||||
}
|
||||
|
||||
self.window = window
|
||||
return window
|
||||
}
|
||||
|
||||
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
|
||||
window?.resignKey()
|
||||
window?.isHidden = false
|
||||
window = nil
|
||||
}
|
||||
}
|
||||
|
||||
private struct WindowReader: UIViewRepresentable {
|
||||
var onUpdate: (UIWindow) -> Void
|
||||
|
||||
func makeUIView(context: Context) -> InjectView {
|
||||
InjectView(onUpdate: onUpdate)
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: InjectView, context: Context) {
|
||||
}
|
||||
|
||||
class InjectView: UIView {
|
||||
var onUpdate: (UIWindow) -> Void
|
||||
|
||||
init(onUpdate: @escaping (UIWindow) -> Void) {
|
||||
self.onUpdate = onUpdate
|
||||
super.init(frame: .zero)
|
||||
isHidden = true
|
||||
isUserInteractionEnabled = false
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func willMove(toWindow newWindow: UIWindow?) {
|
||||
super.willMove(toWindow: newWindow)
|
||||
|
||||
if let window = newWindow {
|
||||
onUpdate(window)
|
||||
} else {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
if let window = self?.window {
|
||||
self?.onUpdate(window)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue