Skip to content

Instantly share code, notes, and snippets.

@curioustechizen
Last active March 9, 2025 16:52
Show Gist options
  • Save curioustechizen/4d191dcd4d633aac62419f756ff0f650 to your computer and use it in GitHub Desktop.
Save curioustechizen/4d191dcd4d633aac62419f756ff0f650 to your computer and use it in GitHub Desktop.
SwiftUI, stateless views and embedding non-SwiftUI views using UIViewControllerRepresentable
import SwiftUI
struct CorrectRepresentableExample: View {
@State private var clickCount: Int = 0
var body: some View {
VStack {
TopView {
self.clickCount += 1
}
.frame(maxHeight: .infinity)
CorrectStatefulBottomViewRepresentable(clickCount: self.$clickCount)
.frame(maxHeight: .infinity)
}
}
}
private struct CorrectStatefulBottomViewRepresentable: UIViewControllerRepresentable {
// To be able to propagate state changes as desired, we have to abandon the stateless approach
// Instead we need to use a stateful approach using SwiftUI @Binding
@Binding var clickCount: Int
func makeUIViewController(context: Context) -> UIViewController {
let bottomView = BottomView(clickCount: $clickCount)
return UIHostingController(rootView: bottomView)
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
// No need to update anything here, instead the @Binding var above results in state propagation
}
}
private struct TopView: View {
let onClick: () -> Void
var body: some View {
VStack {
Text("Correct UIVC Representable example")
Button("Click me", action: onClick)
}
}
}
private struct BottomView: View {
@Binding var clickCount: Int
var body: some View {
Text("\(clickCount) clicks")
}
}
#Preview {
CorrectRepresentableExample()
}
import SwiftUI
struct IncorrectRepresentableExample: View {
@State private var clickCount: Int = 0
var body: some View {
VStack {
TopView {
self.clickCount += 1
}
.frame(maxHeight: .infinity)
IncorrectStatelessBottomViewRepresentable(clickCount: self.clickCount)
.frame(maxHeight: .infinity)
}
}
}
private struct IncorrectStatelessBottomViewRepresentable: UIViewControllerRepresentable {
// We try to use a stateless approach for a UIVCRepresentable.
// However this is incorrect: the state change is not propagated to the SwiftUI view
// embedded inside the UIHostingViewController
let clickCount: Int
func makeUIViewController(context: Context) -> UIViewController {
let bottomView = BottomView(clickCount: clickCount)
return UIHostingController(rootView: bottomView)
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
// If BottomView was a regular UIKit view, then we could have held on to an instance
// and updated a property here.
// However it is a SwiftUI view so that is not possible.
}
}
private struct TopView: View {
let onClick: () -> Void
var body: some View {
VStack {
Text("Incorrect UIVC Representable example")
Button("Click me", action: onClick)
}
}
}
private struct BottomView: View {
let clickCount: Int
var body: some View {
Text("\(clickCount) clicks")
}
}
#Preview {
IncorrectRepresentableExample()
}
import SwiftUI
struct StatefulCounter: View {
@State private var clickCount = 0
var body: some View {
VStack {
StatelessTopView {
self.clickCount += 1
}.frame(maxHeight: .infinity)
StatelessBottomView(clickCount: self.clickCount)
.frame(maxHeight: .infinity)
}
}
}
private struct StatelessTopView: View {
let onClick: () -> Void
var body: some View {
Button("Click me", action: onClick)
}
}
private struct StatelessBottomView: View {
// This view is completely stateless. It only uses the clickCount property passed to it.
let clickCount: Int
var body: some View {
VStack {
Text("Stateless example")
Text("\(self.clickCount) clicks")
}
}
}
#Preview {
StatelessExample()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment