@Observable object - How to initialize it only once for identical views

@Observable object - How to initialize it only once for identical views



Oct 9, 2023


iOS 17 introduced @Observable. that's an effective way to implement a stateful model object.

Seems there is no way to associate instances with views

However, we are not able to use @StateObject as the model object does not have ObservableObject protocol. An advantage of using @StateObject is to make the object initialized once only for the view. It will keep going on until the view identifier is changed.

I put some examples. We have an Observable implemented object.

@Observable final class Controller { ... }

then using like this

struct MyView: View {

  let controller: Controller

  // or
  init(value: Value) {
    self.controller = .init(value: value)

  // or
  init(controller: Controller) {
    self.controller = controller


This case causes a problem in that the view body uses the passed controller anyway.
Even passed a different controller, views use it.

Plus, in the case of initializing a controller takes expensive costs, which decreases performance.

so how do we manage this kind of use case?

A workaround - making a wrapper view that provides instances for views

anyway I made a utility view that provides an observable object lazily.

public struct ObjectProvider<Object, Content: View>: View {

  @State private var object: Object?

  private let _objectInitializer: () -> Object
  private let _content: (Object) -> Content

  public init(object: @autoclosure @escaping () -> Object, @ViewBuilder content: @escaping (Object) -> Content) {
    self._objectInitializer = object
    self._content = content

  public var body: some View {
    Group {
      if let object = object {
      } else {
          .onAppear {
            assert(object == nil, "it should not be running twice or more.")
            guard object == nil else { return }
            object = _objectInitializer()


ObjectProvider(object: Controller() { controller in

  MyView(controller: controller)


I hope it should be better ways rather than this workaround I made.

Updated 2023-12-21

import SwiftUI

struct ObservableEdge<O: Observable>: DynamicProperty {

  @State private var box: Box<O> = .init()

  var wrappedValue: O {
    if let value = box.value {
      return value
    } else {
      box.value = factory()
      return box.value!

  private let factory: () -> O

  init(wrappedValue factory: @escaping @autoclosure () -> O) {
    self.factory = factory

  private final class Box<Value> {
    var value: Value?