ObservedObject ViewModel Memory Leak with SwiftUI

Viewed 163

I am experiencing memory leaks and i'm not sure the correct way to go about it while preserving the ViewModel approach.

So let's pretend we have a note-taking/scribbling app app in which you can present a screen and create a new "scribble".

Our ViewModel looks something like this...

class ScribbleViewModel: ViewModel<ScribbleContext>, ObservableObject {
  @Published var shouldShowSaveButton: Bool = false
  @Published var text: String {
    didSet {
      // Ignore duplicates
      guard oldValue != text else { return }
      shouldShowSaveButton = isTextValidForSaving(text)
    }
  }
  // down here we just have logic for user tracking dismissing/saving, etc
}

and our SwiftUI view looks something like this:

struct ScribbleView: View {
  
  @ObservedObject var viewModel: ScribbleViewModel
  
  init(viewModel: ScribbleViewModel) {
    self.viewModel = viewModel
  }

  var body: some View {
    VStack {
      ScribblesViewHeader(
        shouldShowSaveButton: viewModel.shouldShowSaveButton,
        closeAction: { [weak viewModel] in
          viewModel?.userDidPressDismissButton()
        },
        saveAction: { [weak viewModel] in
          viewModel?.userDidPressSaveButton()
        }
      )
      ScribbleInputView(text: $viewModel.text)
        .padding(Constants.horizontalEdgeInsets)
    }.navigationBarHidden(true).onAppear { [weak viewModel] in
      viewModel?.userDidLandOnScreen()
    }
  }
}

The view is created and presented as such (as you can see, we are not holding a reference to it:

let requiredContext = /* context required for the module */
let viewModel = ScribbleViewModel(context: requiredContext)
let scribbleView = ScribbleView(viewModel: viewModel)
let hostingController = UIHostingController(rootView: scribbleView)
let navigationController = UINavigationController(rootViewController: hostingController)
navigationController.modalPresentationStyle = .fullScreen
present(navigationController, animated: true, completion: nil)

When I dismiss the view, the ViewModel remains in memory, seemingly being held by its HostingController or its bindings. Its presenting view has no held reference to it, so i'm confused as to why this is happening. Am i not supposed to be binding my SwiftUI views to a held class?

I've made the references to the viewController in the childview actions weak, but do i also have to do something with the bindings?

Memory Graph

0 Answers
Related