Skip to content

Instantly share code, notes, and snippets.

@atierian
Last active August 21, 2023 11:53
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save atierian/9d347649f729f4a3e096067e20fa9c8e to your computer and use it in GitHub Desktop.
Save atierian/9d347649f729f4a3e096067e20fa9c8e to your computer and use it in GitHub Desktop.
Allow consumers of SwiftUI UIViewRepresentable conforming Views to inject wrapped UIView delegate implementations through ViewModifiers using a ProxyDelegate.

Allow the consumer of a SwiftUI UIViewRepresentable View to inject delegate implementations for the wrapped UIView through View Modifiers by using a proxy delegate.

This can be a helpful pattern when providing a SwiftUI wrapper for a very heavy and complex UIView, where the wrapper implements many of the delegate methods of the wrapped UIView. But you want someone consuming the wrapper to have the ability to inject their own delegate method implementations to override yours, or to leverage some of the methods your not implementing.

import UIKit
import SwiftUI
protocol HeavyUIViewDelegate: AnyObject {
func heavyView(_ view: HeavyUIView, to thing: Int)
func heavyView(_ view: HeavyUIView, titleForThing: String) -> String
}
class HeavyUIView: UIView { }
struct SwiftUIWrapper: UIViewRepresentable {
let wrappedView: HeavyUIView
let proxyDelegate = ProxyDelegate()
func makeUIView(
context: UIViewRepresentableContext<SwiftUIWrapper>
) -> some HeavyUIView {
wrappedView
}
func updateUIView(_ uiView: UIViewType, context: Context) { }
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
}
// MARK: ViewModifiers
extension SwiftUIWrapper {
func heavyViewSomeThingHappened(
_ implementation: @escaping (
_ view: HeavyUIView,
_ thing: Int
) -> Void
) -> SwiftUIWrapper {
proxyDelegate.heavyViewSomeThingHappened = implementation
return self
}
func heavyViewTitleForThing(
_ implementation: @escaping (
_ view: HeavyUIView,
_ titleForThing: String
) -> String
) -> SwiftUIWrapper {
proxyDelegate.heavyViewTitleForThing = implementation
return self
}
}
// MARK: Coordinator
extension SwiftUIWrapper {
class Coordinator: HeavyUIViewDelegate {
let control: SwiftUIWrapper
init(_ control: SwiftUIWrapper) {
self.control = control
}
func heavyView(_ view: HeavyUIView, to thing: Int) {
control.proxyDelegate.heavyViewSomeThingHappened(view, thing)
}
func heavyView(_ view: HeavyUIView, titleForThing: String) -> String {
control.proxyDelegate.heavyViewTitleForThing(view, titleForThing)
}
}
}
// MARK: ProxyDelegate
extension SwiftUIWrapper {
class ProxyDelegate {
var heavyViewSomeThingHappened: (
_ view: HeavyUIView,
_ thing: Int
) -> Void = { view, thing in
// default implementation ...
}
var heavyViewTitleForThing: (
_ view: HeavyUIView,
_ titleForThing: String
) -> String = { view, titleForThing in
// default implementation ...
String(titleForThing.reversed())
}
}
}
// MARK: Example Callsite
SwiftUIWrapper(wrappedView: .init())
.heavyViewSomeThingHappened { view, thing in
// consumer provided implementation
}
.heavyViewTitleForThing { view, titleForThing in
"\(titleForThing.reversed()) - \(titleForThing)"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment