Skip to content

Instantly share code, notes, and snippets.

@christianselig
Created April 15, 2024 18:30
Show Gist options
  • Save christianselig/9fddd8ba0059f8b069fa46ca7dabb58f to your computer and use it in GitHub Desktop.
Save christianselig/9fddd8ba0059f8b069fa46ca7dabb58f to your computer and use it in GitHub Desktop.
import SwiftUI
struct ContentView: View {
var body: some View {
IceCreamView(viewModel: ViewModel(iceCream: "banana"))
}
}
struct IceCreamView: View {
@State var viewModel: ViewModel
var body: some View {
VStack {
Text("Flavor: \(viewModel.iceCream)")
// This random number changes every 1 second
// and ideally it wouldn't
Text("Enjoy your ice cream! \(arc4random_uniform(9999999))")
}
.overlay {
// Option 1: ❌
if let secondsSinceCreation = viewModel.secondsSinceCreation {
// Still will cause updates as view model is still rebuilding the body
IceCreamOverlayView(secondsSinceCreation: secondsSinceCreation)
}
// Option 2: ✅
MaybeExistsIceCreamOverlayView(viewModel: viewModel)
}
}
}
// ❌ This would not be enough, as it would be recreated
// each time secondsSinceCreation changes
struct IceCreamOverlayView: View {
let secondsSinceCreation: Int
var body: some View {
Text("Ordered \(secondsSinceCreation) second(s) ago")
.padding(.top, 100)
}
}
// ✅ This WOULD be enough, but feels weird. What
// do you even call it?
struct MaybeExistsIceCreamOverlayView: View {
let viewModel: ViewModel
var body: some View {
if let secondsSinceCreation = viewModel.secondsSinceCreation {
// Still will cause updates as view model is still rebuilding the body
IceCreamOverlayView(secondsSinceCreation: secondsSinceCreation)
}
}
}
@Observable class ViewModel {
let iceCream: String
var secondsSinceCreation: Int?
init(iceCream: String) {
self.iceCream = iceCream
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
if let secondsSinceCreation = self.secondsSinceCreation {
self.secondsSinceCreation = secondsSinceCreation + 1
} else {
self.secondsSinceCreation = 1
}
}
}
}
@aerickson14
Copy link

Here's my attempt to refactor in a way that I would

  1. View isolation for the flavour view so the view doesn't compute if none of its values change
  2. Option 1 is ok in my mind because there is very little overhead for a view to be reinstantiated
import SwiftUI

struct ContentView: View {
    var body: some View {
        IceCreamView(viewModel: ViewModel(iceCream: "banana"))
    }
}

struct IceCreamView: View {
    @State var viewModel: ViewModel

    var body: some View {
        IceCreamFlavorView(flavor: viewModel.iceCream)
            .overlay {
                IceCreamOverlayView(secondsSinceCreation: viewModel.secondsSinceCreation)
            }
    }
}

struct IceCreamFlavorView: View {
    let flavor: String

    var body: some View {
        VStack {
            Text("Flavor: \(flavor)")
            Text("Enjoy your ice cream! \(arc4random_uniform(9999999))")
        }
    }
}

struct IceCreamOverlayView: View {
    let secondsSinceCreation: Int?

    init(secondsSinceCreation: Int?) {
        self.secondsSinceCreation = secondsSinceCreation
    }

    var body: some View {
        if let secondsSinceCreation {
            Text("Ordered \(secondsSinceCreation) second(s) ago")
                .padding(.top, 100)
        }
    }
}

@Observable class ViewModel {
    let iceCream: String
    var secondsSinceCreation: Int?

    init(iceCream: String) {
        self.iceCream = iceCream

        Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
            if let secondsSinceCreation = self.secondsSinceCreation {
                self.secondsSinceCreation = secondsSinceCreation + 1
            } else {
                self.secondsSinceCreation = 1
            }
        }
    }
}

@christianselig
Copy link
Author

@aerickson14 This would still result in the number updating, I believe

@aerickson14
Copy link

The seconds ago number will updated but the random number in IceCreamFlavorView will remain the same. I just double checked and still seeing that behaviour.

@christianselig
Copy link
Author

@aerickson14 Interesting, okay thanks! Will have to dig a bit deeper and try to understand

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