Skip to content

Instantly share code, notes, and snippets.

@leok7v
Created October 2, 2025 01:05
Show Gist options
  • Select an option

  • Save leok7v/55326e84344293cfd173cac3ab745f2e to your computer and use it in GitHub Desktop.

Select an option

Save leok7v/55326e84344293cfd173cac3ab745f2e to your computer and use it in GitHub Desktop.
Platform Independent unifying Platform[Web]View templates using ViewRepresentable to minimize/reduce #if #else #endif in SwiftUI code
import SwiftUI
/// Platform Independent unifying ViewRepresentable template to minimize #if #else #endif in SwiftUI code
#if canImport(UIKit)
import UIKit
public typealias _BaseView = UIView
public typealias PlatformViewRepresentable = UIViewRepresentable
#else
import AppKit
public typealias _BaseView = NSView
public typealias PlatformViewRepresentable = NSViewRepresentable
#endif
public struct PlatformView<V: _BaseView>: PlatformViewRepresentable {
public init(make: @escaping (Context) -> V,
update: @escaping (V, Context) -> Void = { _, _ in }) {
self.make = make
self.update = update
}
private let make: (Context) -> V
private let update: (V, Context) -> Void
public typealias Coordinator = Void
#if canImport(UIKit)
public typealias Context = UIViewRepresentableContext<PlatformView<V>>
public typealias UIViewType = V
public func makeUIView(context: Context) -> V { make(context) }
public func updateUIView(_ v: V, context: Context) { update(v, context) }
#else
public typealias Context = NSViewRepresentableContext<PlatformView<V>>
public typealias NSViewType = V
public func makeNSView(context: Context) -> V { make(context) }
public func updateNSView(_ v: V, context: Context) { update(v, context) }
#endif
}
public struct PlatformViewWithCoordinator<V: _BaseView, C>: PlatformViewRepresentable {
public init(make_coordinator: @escaping () -> C,
make: @escaping (Context, C) -> V,
update: @escaping (V, Context, C) -> Void = { _, _, _ in }) {
self.make_coordinator = make_coordinator
self.make = make
self.update = update
}
private let make_coordinator: () -> C
private let make: (Context, C) -> V
private let update: (V, Context, C) -> Void
public typealias Coordinator = C
public func makeCoordinator() -> C { make_coordinator() }
#if canImport(UIKit)
public typealias Context =
UIViewRepresentableContext<PlatformViewWithCoordinator<V, C>>
public typealias UIViewType = V
public func makeUIView(context: Context) -> V {
make(context, context.coordinator)
}
public func updateUIView(_ v: V, context: Context) {
update(v, context, context.coordinator)
}
#else
public typealias Context =
NSViewRepresentableContext<PlatformViewWithCoordinator<V, C>>
public typealias NSViewType = V
public func makeNSView(context: Context) -> V {
make(context, context.coordinator)
}
public func updateNSView(_ v: V, context: Context) {
update(v, context, context.coordinator)
}
#endif
}
/// No coordinator
/// let anyView = PlatformView<_BaseView>(
/// make: { _ in _BaseView(frame: .zero) },
/// update: { v, _, _ in v.isHidden = false }
/// )
/// With coordinator
/// final class MyCoord { var tag = 0 }
/// let withCoord = PlatformViewWithCoordinator<_BaseView, MyCoord>(
/// make_coordinator: { MyCoord() },
/// make: { _, c in let v = _BaseView(frame: .zero); c.tag = 42; return v },
/// update: { v, _, c in _ = (v, c) }
/// )
import SwiftUI
import WebKit
/// Platform Independent unifying WebView template to reduce #if #else #endif in SwiftUI code
public struct PlatformWebView: View {
public typealias Created = (WKWebView) -> Void
public typealias Configuration = (WKWebViewConfiguration) -> Void
public typealias DidFinish = (WKWebView) -> Void
public typealias Policy = (WKWebView, WKNavigationAction,
@escaping (WKNavigationActionPolicy) -> Void) -> Void
public init(configuration: Configuration? = nil,
created: Created? = nil,
policy: Policy? = nil,
didFinish: DidFinish? = nil) {
self.created = created
self.configuration = configuration
self.policy = policy
self.didFinish = didFinish
}
private let created: Created?
private let configuration: Configuration?
private let didFinish: DidFinish?
private let policy: Policy?
public final class Coordinator: NSObject, WKNavigationDelegate {
var parent: PlatformWebView
init(_ parent: PlatformWebView) { self.parent = parent }
public func webView(_ v: WKWebView, didFinish n: WKNavigation!) {
parent.didFinish?(v)
}
public func webView(_ v: WKWebView,
decidePolicyFor a: WKNavigationAction,
decisionHandler d: @escaping
(WKNavigationActionPolicy) -> Void) {
if let f = parent.policy {
f(v, a, d)
} else {
d(.allow)
}
}
}
public var body: some View {
PlatformViewWithCoordinator<WKWebView, Coordinator>(
make_coordinator: { Coordinator(self) },
make: { _, context in
let c = WKWebViewConfiguration()
configuration?(c)
let v = WKWebView(frame: .zero, configuration: c)
v.navigationDelegate = context
created?(v)
return v
},
update: { _, _, _ in }
)
}
}
#Preview("Basic") {
/// usage example:
PlatformWebView(
configuration: { c in
c.preferences.shouldPrintBackgrounds = false
if #available(macOS 11.0, *) {
} else { // before MacOS 11.0
c.preferences.javaScriptEnabled = true
}
},
created: { w in
#if os(iOS)
w.isOpaque = false
w.backgroundColor = .clear
w.allowsBackForwardNavigationGestures = false
w.translatesAutoresizingMaskIntoConstraints = true
w.autoresizingMask = [.flexibleWidth]
w.scrollView.backgroundColor = .clear
w.scrollView.isScrollEnabled = false
w.scrollView.contentInsetAdjustmentBehavior = .never
w.scrollView.delaysContentTouches = false
w.scrollView.pinchGestureRecognizer?.isEnabled = false
#elseif os(macOS)
w.setValue(false, forKey: "drawsBackground")
w.translatesAutoresizingMaskIntoConstraints = true
w.autoresizingMask = [.width]
#endif
let html = """
<html><head><meta name='viewport' content='width=device-width, \
initial-scale=1'></head><body style='font:16px -apple-system'>
<h2>PlatformWebView</h2><p>Hello, preview.</p><p id='progress'>Loading...</p></body></html>
"""
w.loadHTMLString(html, baseURL: nil)
},
policy: { view, action, decision in
if action.navigationType == .linkActivated,
let url = action.request.url {
#if os(iOS)
UIApplication.shared.open(url, options: [:],
completionHandler: nil)
#elseif os(macOS)
NSWorkspace.shared.open(url)
#endif
decision(.cancel)
} else {
decision(.allow)
}
},
didFinish: { w in
w.evaluateJavaScript("""
document.getElementById('progress').textContent = 'Loaded.'
""")
}
)
.frame(width: 360, height: 240)
.background(Color.white)
.overlay(Color.orange.opacity(0.25).blendMode(.multiply))
}
@leok7v
Copy link
Author

leok7v commented Oct 2, 2025

Screenshot 2025-10-01 at 6 07 39 PM Screenshot 2025-10-01 at 6 08 04 PM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment