Memory leak in refreshable view

I'm trying to call the refresh function in the view model from the .refreshable in a view.

I have tested it with an empty function in the viewModel and it causes a memory leak in this view

struct TopicListView: View {
  @StateObject private var viewModel: TopicListViewModel
  @State private var boardId: String
  @State private var boardName: String
   
  init(dataFetchable: DataFetchable, boardId: String, boardName: String) {
    self._viewModel = StateObject(wrappedValue: TopicListViewModel(dataFetchable: dataFetchable))
    self.boardId = boardId
    self.boardName = boardName
  }
   
  var body: some View {
    List(viewModel.topics.indices, id: \.self) { index in
      HStack {
        TopicView(title: viewModel.topics[index].title, lastPostTime: viewModel.topics[index].lastPostTime, formatter: viewModel.dateFormatter)
         
        NavigationLink(destination: PostView(dataFetchable: viewModel.dataFetchable, title: viewModel.topics[index].title, topicId: viewModel.topics[index].id)) {
           
          EmptyView()
        }
        .frame(width: 0)
        .opacity(0)
      }
      .onAppear {
        if index == viewModel.topics.count-1 {
          viewModel.page+=1
          viewModel.fetchTopics(boardId)
        }
      }
    }
    .listStyle(.plain)
    .navigationBarTitle(boardName)
    .refreshable {
      await viewModel.doNotThing()
    }
    .onAppear {
      viewModel.boardId = boardId
    }
  }
}

However, the PostView which I have linked with a NavigationLink have no issue

struct PostView: View {
  @State private var title: String
  @State private var topicId: String
  @StateObject private var viewModel: PostViewModel
   
  init(dataFetchable: DataFetchable, title: String, topicId: String) {
    self._viewModel = StateObject(wrappedValue: PostViewModel(dataFetchable: dataFetchable))
    self.topicId = topicId
    self.title = title
  }
   
  var body: some View {
    List(viewModel.posts, id: \.id) { item in
      TextblockView(textBlock: item.textBlock)
    }
    .listStyle(.plain)
    .navigationBarTitle(title)
    .navigationBarTitleDisplayMode(.inline)
    .refreshable {
      await viewModel.doNotThing()
    }
    .onAppear(perform: {
      viewModel.topicId = topicId
    })
     
  }
}

Here is the memory graph

Any ideas how to fix it?

Perhaps I don't understand your problem, but where do you see a memory leak?

The SwiftUI „runtime“ is keeping the StateObject alive. That's an intended function.

I add .navigationBarTitleDisplayMode(.inline) to the List of TopicListView and the issue magically fixed...

I was running into the same issue, and your work around of adding .navigationBarTitleDisplayMode(.inline) also worked for me :)

Seems like there's a bug somewhere on .refreshable with List that causes this retain cycle. Filed a Feedback Assistant (Radar) ticket - FB10640905

Has there been any update to this issue? I've just encountered this bug and I can't find much about it online other than this thread.

I'm trying to create a state object wrapper for Apollo iOS watch queries and was hoping to rely on deinit on ObservedObject to clean up the watcher. This bug prevents deinit from ever getting called (and of course, memory leak is bad in general).

I also encountered this bug, and it was fixed as well by adding .navigationBarTitleDisplayMode()

I've been loosing some hairs from this one, hopefully it can get fixed.

I realized now that it only works using .inline. That's a problem.

Did any of you find a way to get around it?

Here is what I would say is a much more appropriate fix for this issue—create the weak reference yourself:

.refreshable { [weak viewModel] in
      await viewModel?.doNotThing()
}
Memory leak in refreshable view
 
 
Q