0

I am trying to implement a SwiftUI list for a sidebar view which has at least 3 different types of data points: a) a fixed list of enums, b) a list of 'tags' coming from a @FetchRequest using Core Data, c) a similar list of 'groups' coming from a different @FetchRequest.

I'm struggling with how to handle multiple selection with List in this setup. The user should be able to select from different sections (and I get change notifications to fine-tune the handling). I have tried making the 'selection' type to be UUID, and setting the id for each leaf view explicitly, but it doesn't seem to work (I don't the selection highlight).

This is the list I made:

struct CombinedListView: View {
    @FetchRequest(
        entity: CJTag.entity(),
        sortDescriptors: [NSSortDescriptor(keyPath: \CJTag.displayOrder, ascending: true)]
    )
    var tags: FetchedResults<CJTag>

    @FetchRequest(
        entity: PrivateGroups.entity(),
        sortDescriptors: [NSSortDescriptor(keyPath: \PrivateGroups.displayOrder, ascending: true)]
    )
    var groups: FetchedResults<PrivateGroups>

    @State private var selectedItems = Set<UUID>()
    
    var body: some View {
        NavigationView {
            VStack {
                List(selection: $selectedItems) {
                    
                    // section for Tabs
                    Section(header: Text("Main Tabs")) {
                        ForEach(MainTab.allCases, id: \.rawValue) { tab in
                            Text(tab.rawValue)
                                .id(tab.id)
                        }
                    }
                    
                    // Section for Tags
                    if !tags.isEmpty {
                        Section(header: Text("Tags")) {
                            ForEach(tags) { tag in
                                Text(tag.tagName ?? "Tag")
                                    .id(tag.objectID.uriRepresentation().absoluteString) // Directly tag with UUID
                                    .contentShape(Rectangle())
                            }
                        }
                    }
                    // Section for Groups
                    if !groups.isEmpty {
                        Section(header: Text("Groups")) {
                            ForEach(groups) { group in
                                Text(group.groupName ?? "Group")
                                    .id(group.objectID.uriRepresentation().absoluteString)
                                    .contentShape(Rectangle())
                            }
                        }
                    }
                }
                .listStyle(SidebarListStyle())
                .navigationTitle("Selectable List")
            }
        }
    }
}

I know that if I just had NSManagedObjects in the list, I could set the 'selection' type to be NSManagedObjectID and it would work. But I needed it to support a list of enum cases as well.

I tried setting the tag for each row view as well (using the same stuff as id modifier), but that doesn't work either. I'm sure it's a case of mismatched 'types' for selection, but I can't figure out the best setup to accomplish this.

EDIT:

Added code for MainTab:

// Enum for Main Tabs
enum MainTab: String, CaseIterable, Identifiable {
    case home = "Home"
    case favorites = "Favorites"
    case settings = "Settings"
    case profile = "Profile"
    case help = "Help"

    var id: String { rawValue }
    var iconName: String { rawValue.lowercased() }
}
4
  • 2
    Note NavigationView is deprecated use NavigationStack instead. .id(tag.objectID.uriRepresentation().absoluteString) does not seem to be of type UUID, more like a String. You could/should try using .tag(UUID type here) instead of .id(...). And ensure that tab.id is of type UUID. You mentioned NSManagedObjects, is this for a macOS App only? Can you show the code for MainTab. Commented Dec 12, 2024 at 7:28
  • I added the MainTab enum code. Yes, this is for macOS only. Also, the object.objectID.uriRepresentation().absoluteString should be unique, so does that qualify as a UUID type itself, or do I have to add that conformance separately (if so, how)? NSManagedObjects already have automatic conformance to Identifiable, using NSManagedObjectID Commented Dec 12, 2024 at 7:37
  • 1
    Note, UUID is a type different to String. Just because the strings are unique does not make them of type UUID. So try this instead, var selectedItems = Set<String>() with .tag(...) Commented Dec 12, 2024 at 7:46
  • That works! It works with both .tag(...) or .id(...) Commented Dec 12, 2024 at 8:07

2 Answers 2

0

The selections do not work because you have different types for your

@State private var selectedItems = Set<UUID>()

and the .id(...) which are Strings. They must be of the same type to work.

So change your selection to match

@State private var selectedItems = Set<String>()

and I recommend using .tag(...) instead of .id(...).

And as I mentioned in my comment, NavigationView is deprecated use NavigationStack instead.

Sign up to request clarification or add additional context in comments.

Comments

0

How about using an enum? E.g.

    enum Selection: Hashable {
        case platform(id: Platform.ID)
        case game(id: Game.ID)
        // etc.
    }

Then the tag could be:

.tag(Selection.platform(id: platform.id))

It would probably be a good idea to upgrade from the old buggy NavigationView to the new NavigationSplitView/NavigationStack. Those use a NavigationPath type which can store different types in the array which might solve your problem.

By the way, you can make a single @FetchRequest return multiple entities if you implement entity inheritance, e.g. fetch the parent entity then all the child entities will be returned.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.