DEV Community

Tom.U
Tom.U

Posted on

4 3

Web Fullscreen Sheet with SFSafariViewController in SwiftUI

Web Sheet


import SwiftUI
import SafariServices

struct ContentView: View {
    var body: some View {
        SafariButton()
    }
}

struct SafariButton: View {

    struct RootView: View, Hostable {
        @EnvironmentObject private var hostedObject: HostingObject<Self>

        var address = "https://example.com"

        func present() { // UIKit code
            let safari = SFSafariViewController(url: URL(string: address)!)
            hostedObject.viewController?.present(safari, animated: true)
        }

        var body: some View {
            Button(address, action: present)
        }
    }

    var body: some View {
        RootView().hosting()
    }
}

///=======

struct UIViewControllerView: UIViewControllerRepresentable {
    final class ViewController: UIViewController {
        var didAppear: (UIViewController) -> Void = { _ in }
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            didAppear(self)
        }
    }

    var didAppear: (UIViewController) -> Void

    func makeUIViewController(context: Context) -> UIViewController {
        let viewController = ViewController()
        viewController.didAppear = didAppear
        return viewController
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
        //
    }
}

struct UIViewControllerViewModifier: ViewModifier {
    var didAppear: (UIViewController) -> Void
    var viewControllerView: some View {
        UIViewControllerView(didAppear:didAppear).frame(width:0,height:0)
    }
    func body(content: Content) -> some View {
        content.background(viewControllerView)
    }
}

extension View {
    func uiViewController(didAppear: @escaping (UIViewController) -> ()) -> some View {
        modifier(UIViewControllerViewModifier(didAppear:didAppear))
    }
}

class HostingObject<Content: View>: ObservableObject {
    @Published var viewController: UIViewController? = nil
}

struct HostingObjectView<Content: View>: View {
    var rootView: Content
    let hostedObject = HostingObject<Content>()
    func getHost(viewController: UIViewController) {
        hostedObject.viewController = viewController.parent
    }
    var body: some View {
        rootView
            .uiViewController(didAppear: getHost(viewController:))
            .environmentObject(hostedObject)
    }
}

protocol Hostable: View {
    associatedtype Content: View
    func hosting() -> Content
}

extension Hostable {
    func hosting() -> some View {
        HostingObjectView(rootView: self)
    }
}

///=======

import PlaygroundSupport

PlaygroundPage.current.setLiveView( ContentView() )

Sentry mobile image

App store rankings love fast apps - mobile vitals can help you get there

Slow startup times, UI hangs, and frozen frames frustrate users—but they’re also fixable. Mobile Vitals help you measure and understand these performance issues so you can optimize your app’s speed and responsiveness. Learn how to use them to reduce friction and improve user experience.

Read full post →

Top comments (0)

Billboard image

📊 A side-by-side product comparison between Sentry and Crashlytics

A free guide pointing out the differences between Sentry and Crashlytics, that’s it. See which is best for your mobile crash reporting needs.

See Comparison

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay