Home > Enterprise >  Sheet inside ForEach leads to compiler being unable to type-check this expression Error
Sheet inside ForEach leads to compiler being unable to type-check this expression Error

Time:10-12

I am trying to open a sheet when tapping on an item. I followed this questions Sheet inside ForEach doesn't loop over items SwiftUI answer. I get this Error: The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions, I don't understand what is causing it. I tried multiple solutions and they all lead to the same Error.

@State var selectedSong: Int? = nil
@State var songList: [AlbumSong] = []

VStack {
ForEach(songList.enumerated().reversed(), id: \.offset) { index, song in
  HStack {
    Text("\(index   1).").padding(.leading, 8)
      VStack {
                      Text(song.title)
                      Text(song.artist)
             }
           }.onTapGesture {
              self.selectedSong = index
           }
         }
       }
}
.sheet(item: self.$selectedSong) { selectedMovie in
       SongPickerEdit(songList: $songList, songIndex: selectedMovie)

I also tried setting songIndex to being an AlbumSong and then implemented this sheet:

.sheet(item: self.$selectedSong) { 
SongPickerEdit(songList: $songList, songIndex: self.songList[$0])
}
struct SongPickerEdit: View { 
    @Binding var songList: [AlbumSong]
    @State var songIndex: Int?
var body: some View {
  }
}

struct AlbumSong: Identifiable, Codable {
    @DocumentID var id: String?
    let title: String
    let duration: TimeInterval
    var image: String
    let artist: String
    let track: String
}

CodePudding user response:

How about making selectedSong an AlbumSong?? The item: parameter needs to be an Identifiable binding, but Int is not Identifiable.

@State var selectedSong: AlbumSong? = nil
@State var songList: [AlbumSong] = []

var body: some View {
    List {
        ForEach(songList.enumerated().reversed(), id: \.offset) { index, song in
            HStack {
                Text("\(index   1).").padding(.leading, 8)
                VStack {
                      Text(song.title)
                      Text(song.artist)
                }
            }.onTapGesture {
                self.selectedSong = song
            }
        }
    }.sheet(item: $selectedSong) { song in
        SongPickerEdit(songList: $songList, song: song)
    }
}

Note that SongPickerEdit would look like this:

struct SongPickerEdit: View {
    @State var song: AlbumSong
    var body: some View {
        Text("\(song.title), \(song.artist)")
    }
}

If you really need the index for some reason, you can add the song list binding back in and use songList.index { $0.id == song.id } to find the index if the list is not too long.

Otherwise, you can make your own Identifiable type SongAndIndex that uses the same id as AlbumSong, but with an extra index property, and use that as the type of selectedSong.

A third way would be to use the sheet(isPresented:) overload, but this way you end up with 2 sources of truth:

@State var selectedSongIndex: Int? = nil {
    didSet {
        if selectedSongIndex != nil {
            isSheetPresented = true
        }
    }
}
@State var isSheetPresented: Bool = false {
    didSet {
        if !isSheetPresented {
            selectedSongIndex = nil
        }
    }
}

...

}.sheet(isPresented: $isSheetPresented) {
    SongPickerEdit(songList: $songList, songIndex: selectedSongIndex)
}

selectedSongIndex also won't be set to nil when the user dismisses the sheet.

  • Related