How to dynamically add a section for List

I am having a heck of a problem trying to figure out how to add Sections to a List in SwiftUI (dynamically based on a change in an array value). Everything works until I try and test a variable and add a section around the row. As soon as I add some logic I get the error: Unable to infer complex closure return type; add explicit type to disambiguate. I tried moving the section to a new function returning a View but that didn't help either. Any thoughts on how to rewrite this: The SiteRow() is defined as a struct of some View and returns some stacks. I couldnt find any examples on how to do a normal list row and add a section dynamically, they were all hardcoded lists.


struct FetchView: View {

@ObservedObject var fetcher = NOCFetcher()

var lastCompany: String = ""

var body: some View {

NavigationView {

VStack {

List(fetcher.siteinfo) { // The Unable to infer complex closure error is shown here at the curly bracket

site in

if site.customer != self.lastCompany {

Section(header: Text(site.customer)) {

SiteRow(site: site)

}

self.lastCompany = site.customer

} else {

SiteRow(site: site)

}

}.navigationBarTitle(Text("Site Listing"))

}

}

}

}

Replies

hi,


(1) on the specific error, the problem lies in setting self.lastCompany = site.customer, since the if statement does not result in a view.


(2) but the real problem is that you can't just "throw in a Section" as if it were another row. rows live within Sections in a structural sense.


you have to create a list of Sections at the top level, and within each Section, create a list of rows for that section. and to do this, you have to know which Sections will be needed for your data, and within each Section, which rows belong to it.


i don't know the structure of your data, but suppose it were just flat, like "a record is some information about a site and some other data." you'd have to collect all records of interest, block them out by matching sites (this determines the number of Sections), and then pull records for those sites on demand (that determines the number of rows per section) -- we'd need to know this with UIKit methods as well.


with a little magical help from Swift structures, the following example works.


// sample SiteInfo
struct SiteInfo: Identifiable {
  let id = UUID()
  let customer: String
  let otherInfo: String
}

struct ContentView: View {

  // a "flat" database
  var siteInfoList = [
    SiteInfo(customer: "Dog", otherInfo: "1"),
    SiteInfo(customer: "Dog", otherInfo: "2"),
    SiteInfo(customer: "Cat", otherInfo: "3"),
    SiteInfo(customer: "Bird", otherInfo: "4"),
    SiteInfo(customer: "Dog", otherInfo: "5"),
    SiteInfo(customer: "Bird", otherInfo: "6"),
    SiteInfo(customer: "Cat", otherInfo: "7")
  ]

  // produces a dictionary of the form
  // [
  // "Dog" : [three SiteInfo records],
  // "Cat" : [two SiteInfo records],
  // "Bird" : [two SiteInfo records]
  // ]
  var sitesCollatedByCustomer: [String : [SiteInfo]] {
    Dictionary(grouping: siteInfoList, by: { $0.customer })
  }

  // pulls out the keys from the dictionary made above, sorted
  var uniqueCustomers: [String] {
    sitesCollatedByCustomer.map({ $0.key }).sorted()
  }

  var body: some View {
    NavigationView {
      List {
        ForEach(uniqueCustomers, id: \.self) { customer in
          Section(header: Text(customer)) {
            ForEach(self.sitesCollatedByCustomer[customer]!) { site in
              SiteRow(site: site)
            }
          }
        }
      } // end of List
        .navigationBarTitle(Text("Site Listing"))
    } // end of NavigationView
  }
}


i used a trivial SiteRow view of


struct SiteRow: View {

  var site: SiteInfo

    var body: some View {
      HStack {
        Text(site.customer)
        Spacer()
        Text(site.otherInfo)
      }
    }
}


that should do it (but be sure to fetch all the data you need in place of my siteInfoList). by the way: the View code is credit Paul Hudson (hackingwithswift.com). see his video series "Building a menu using List - SwiftUI Starter Project on Youtube.



hope that helps,

DMG