mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2025-04-27 02:14:45 +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 preferences: UserPreferences
|
||||||
@EnvironmentObject private var routerPath: RouterPath
|
@EnvironmentObject private var routerPath: RouterPath
|
||||||
|
|
||||||
@State private var presentedURL: URL?
|
@StateObject private var safariManager = InAppSafariManager()
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
func body(content: Content) -> some View {
|
||||||
content
|
content
|
||||||
.environment(\.openURL, OpenURLAction { url in
|
.environment(\.openURL, OpenURLAction { url in
|
||||||
// Open internal URL.
|
// Open internal URL.
|
||||||
routerPath.handle(url: url)
|
routerPath.handle(url: url)
|
||||||
})
|
})
|
||||||
.onOpenURL(perform: { url in
|
.onOpenURL { url in
|
||||||
// Open external URL (from icecubesapp://)
|
// Open external URL (from icecubesapp://)
|
||||||
let urlString = url.absoluteString.replacingOccurrences(of: "icecubesapp://", with: "https://")
|
let urlString = url.absoluteString.replacingOccurrences(of: "icecubesapp://", with: "https://")
|
||||||
guard let url = URL(string: urlString), url.host != nil else { return }
|
guard let url = URL(string: urlString), url.host != nil else { return }
|
||||||
_ = routerPath.handle(url: url)
|
_ = routerPath.handle(url: url)
|
||||||
})
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
routerPath.urlHandler = { url in
|
routerPath.urlHandler = { url in
|
||||||
if url.absoluteString.contains("@twitter.com"), url.absoluteString.hasPrefix("mailto:") {
|
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 {
|
guard let scheme = url.scheme, ["https", "http"].contains(scheme.lowercased()) else {
|
||||||
return .systemAction
|
return .systemAction
|
||||||
}
|
}
|
||||||
|
return safariManager.open(url)
|
||||||
presentedURL = url
|
}
|
||||||
return .handled
|
}
|
||||||
|
.background {
|
||||||
|
WindowReader { window in
|
||||||
|
self.safariManager.windowScene = window.windowScene
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.fullScreenCover(item: $presentedURL, content: { url in
|
}
|
||||||
SafariView(url: url, inAppBrowserReaderView: preferences.inAppBrowserReaderView)
|
}
|
||||||
.edgesIgnoringSafeArea(.all)
|
|
||||||
})
|
private class InAppSafariManager: NSObject, ObservableObject, SFSafariViewControllerDelegate {
|
||||||
}
|
var windowScene: UIWindowScene?
|
||||||
|
let viewController: UIViewController = UIViewController()
|
||||||
struct SafariView: UIViewControllerRepresentable {
|
var window: UIWindow?
|
||||||
let url: URL
|
|
||||||
let inAppBrowserReaderView: Bool
|
@MainActor
|
||||||
|
func open(_ url: URL) -> OpenURLAction.Result {
|
||||||
func makeUIViewController(context _: UIViewControllerRepresentableContext<SafariView>) -> SFSafariViewController {
|
guard let windowScene = windowScene else { return .systemAction }
|
||||||
let configuration = SFSafariViewController.Configuration()
|
|
||||||
configuration.entersReaderIfAvailable = inAppBrowserReaderView
|
self.window = setupWindow(windowScene: windowScene)
|
||||||
|
|
||||||
let safari = SFSafariViewController(url: url, configuration: configuration)
|
let configuration = SFSafariViewController.Configuration()
|
||||||
safari.preferredBarTintColor = UIColor(Theme.shared.primaryBackgroundColor)
|
configuration.entersReaderIfAvailable = UserPreferences.shared.inAppBrowserReaderView
|
||||||
safari.preferredControlTintColor = UIColor(Theme.shared.tintColor)
|
|
||||||
return safari
|
let safari = SFSafariViewController(url: url, configuration: configuration)
|
||||||
}
|
safari.preferredBarTintColor = UIColor(Theme.shared.primaryBackgroundColor)
|
||||||
|
safari.preferredControlTintColor = UIColor(Theme.shared.tintColor)
|
||||||
func updateUIViewController(_: SFSafariViewController, context _: UIViewControllerRepresentableContext<SafariView>) {}
|
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