SwiftUI List not showing data when wrapping in List

I’m attempting to follow the structure in the ListSwiftUI example at
https://github.com/realm/realm-cocoa/blob/master/examples/ios/swift/ListSwiftU
that Jason demoed in the webinar at

It is my understanding that I need both List and ForEach in order to use the .onMove and .onDelete magic that Jason demoed in the webinar that showed this stuff, but I cannot figure out how to actually show the data when wrapping the ForEach in a List.

My Situation

In my view I am using

@ObservedRealmObject var huntlet: Huntlet

My list looks like

Text("Huntlet is called\(huntlet.title)")
Text("Huntlet has \(huntlet.tasks.count) task(s)")

List {
  ForEach(huntlet.tasks) { task in
    TaskListEditRowView(task: task)
  }
}

// Also, just for reference
struct TaskListEditRowView: View {
  @ObservedRealmObject var task: Task
  
  var body: some View {
    TextField("Task Name", text: $task.title)
      .foregroundColor(Color("navy"))
  }
}

Note: The first two Text() views in this struct are just to verify that the data is getting there.

This code is producing this result:

When I take the list out and just use ForEach, it loads the data:

// List {
  ForEach(huntlet.tasks) { task in
    TaskListEditRowView(task: task)
  }
// } 

produces:

Any ideas?

Hey @Kurt_Libby1– could you show us a bit more code? Maybe how that View is being instantiated, and the full View code?

SwiftUI can be a bit finicky around how you declare your Views, and if not done in the way it expects, it will fail silently.

Also– is there any reason you need the wrapping List here?

My understanding with wrapping the List is that I need to include the Edit environment in order to access the .onDelete and .onMove methods, but that these are on the ForEach.

Some things to note:

  • I have a custom Edit button that is sending through on the Binding
  • The Hunt object is Read only, so that shows when the Edit button isn’t pressed
  • The Huntlet object is Read/Write for the user and then a Realm Trigger/Function updates the Hunt object when the Huntlet object is updated.

The code I was referencing in the first post is in the else statement.

Here’s the whole file:

import SwiftUI
import RealmSwift

struct TaskListView: View {
  
  @ObservedRealmObject var hunt: Hunt
  @ObservedRealmObject var huntlet: Huntlet

  @Binding var editMode: Bool
  
  var body: some View {
    VStack {
      if !editMode {
        ForEach(hunt.tasks) { task in
          TaskCardView(task: task, hunt: hunt)
            .padding([.leading, .trailing])
        }
      } else {
        if huntlet.title != "" {
          Text("Huntlet is called\(huntlet.title)")
          Text("Huntlet has \(huntlet.tasks.count) task(s)")
          List {
            ForEach(huntlet.tasks) { task in
              TaskListEditRowView(task: task)
            }
          }
        }
      }
    }
  }
}

struct TaskListEditRowView: View {
  @ObservedRealmObject var task: Task
  
  var body: some View {
    TextField("Task Name", text: $task.title)
      .font(Font.custom("RedHatDisplay-Bold", size: 14))
      .foregroundColor(Color("navy"))
  }
}

I assume that it will end up like this:

If this is an iOS application, you do not need the Edit environment modifier for this. Simple having onMove and onDelete will enable their behaviour. However to explicitly show the move handle and delete button, you are meant to use the builtin EditButton now (which I would generally place in a navigation bar).

It’s possible swiftUI doesn’t like the Either result from your if statement. If you remove the if and simply add the edit button, you will likely see different results.

Thanks.

I stripped out everything and tried to just wrap the ForEach in a List and am getting nothing:

struct TaskListView: View {
  
  @ObservedRealmObject var hunt: Hunt
  @ObservedRealmObject var huntlet: Huntlet
  
  @Binding var editMode: Bool
  
  var body: some View {
      List {
        ForEach(hunt.tasks) { task in
          TaskCardView(task: task, hunt: hunt)
            .padding([.leading, .trailing])
        }
    }
  }
}

Again, commenting out the List wrapper and just using ForEach brings the list elements back.

Also, I tried to use .onMove and .onDelete without using List and there is no swipe function to access the Delete button and no ability to click and drag for moving, so it looks like those actually aren’t enabled without the List wrapper wrapped around the ForEach.

This may be helpful to see:

Can you try the offending ForEach with the List wrapper with non-Realm data? That will at least test if it has to do with Realm or not.

The video helps :smile:

Looks like it’s not a Realm thing, but something isn’t right in SwiftUI.

At least now I can go explore the vast sparse landscape that is SwiftUI documentation :joy:

I’ll explore and post an update when/if I find a solution.

Any pointers on potential culprits would be appreciated.

FOUND IT! :tada:

A few parent views up there was a ScrollView

Since Lists are inherently scrollable, this isn’t supported. Switching the ScrollView to a VStack made the lists show up. :man_facepalming:t2:

Well, that worked to get the list to show up, allow swipe to delete, drag to move, edit fields, etc.

But all of these actual editing functions crash the app.

The change is saved and then the app crashes.

Here is the log: https://www.dropbox.com/s/hwqqppuc714pdbh/EditListCrashLog.rtf?dl=0

Not sure if I should file a crash report or if I’m just doing something wrong.

UPDATE: It works sometimes. I started making a video to show what happens and if I edit the objects, the crash happens. But if I first add a new task and then interact with the editing, it works.

Here’s that video:

Hmm… this appears to be the same issue as https://github.com/realm/realm-cocoa/issues/7086 if you want to chime in there.

Hi @Kurt_Libby1, have you been able to fix the issue when writing on the background?, Are your using Sync in your project?

Hi Diana,

I have not been able to fix it as is.

Instead I updated to Realm Cocoa 1.6 so that I can use the new property wrappers. When I pass in the realm with .environment() and use @ObservedRealmObject or @ObservedResults, it seems to update as needed and removes that need to freeze the lists.

You can see more of it here: https://youtu.be/npglFqQqODk?t=1587

@Andrew_Morgan explains the old way and then shows the new way on how to deal with the Lists, and that seems to have fixed the issue for now.

Hi @Kurt_Libby1

I’m happy that you’ve been able to get a solution to this issue, using the new SwiftUI implementation, so If you have any issues in the future with this or any other realm related crash, do not doubt to contact us.
We’ll keep looking into this issue, as we haven’t been able to reproduce it in our local environment.

2 Likes