UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Day 58 CoreData One to many relationship Country to Candy - What is the best way to delete a candy object in the list

Forums > 100 Days of SwiftUI

struct OneToManyCandyView: View { @Environment(.managedObjectContext) var moc @FetchRequest(sortDescriptors: []) var countries: FetchedResults<Country>

var body: some View {
    VStack {
        List {
            ForEach(countries, id: \.self) { country in
                Section(country.wrappedFullName) {
                    ForEach(country.candyArray, id: \.self) { candy in
                        Text(candy.wrappedName)
                    }
                }
            }
        }

        Button("Add") {
            let candy1 = Candy(context: moc)
            candy1.name = "Mars"
            candy1.origin = Country(context: moc)
            candy1.origin?.shortName = "UK"
            candy1.origin?.fullName = "United Kingdom"

            let candy2 = Candy(context: moc)
            candy2.name = "KitKat"
            candy2.origin = Country(context: moc)
            candy2.origin?.shortName = "UK"
            candy2.origin?.fullName = "United Kingdom"

            let candy3 = Candy(context: moc)
            candy3.name = "Twix"
            candy3.origin = Country(context: moc)
            candy3.origin?.shortName = "UK"
            candy3.origin?.fullName = "United Kingdom"

            let candy4 = Candy(context: moc)
            candy4.name = "Toblerone"
            candy4.origin = Country(context: moc)
            candy4.origin?.shortName = "CH"
            candy4.origin?.fullName = "Switzerland"

            if moc.hasChanges {
                try? moc.save()
            }
        }
    }
}

}

2      

hi mrad,

to delete from the sectioned list by swiping, attach this .onDelete modifier to the inner loop:

ForEach(country.candyArray, id: \.self) { candy in
  Text(candy.wrappedName)
}
.onDelete { offsets in deleteCandies(at: offsets, country: country) }

and then add the function deleteCandies to your View:

func deleteCandies(at offsets: IndexSet, country: Country) {

  // (1) identify candies to delete in the country's candyArray
  var candiesToDelete = [Candy]()
  for offset in offsets {
    candiesToDelete.append(country.candyArray[offset])
  }

  // (2) ask the managed object context to delete each of these
  for candy in candiesToDelete {
    moc.delete(candy)
  }

  // (3) save to disk
  try? moc.save()
}

hope that helps,

DMG

3      

Thank you DMG.

2      

Hi @delawaremathguy,

I know this is many months later but I am asking in hopes that you might see this and help me. I've been trying to take your solution above and adapt it to work with a .onTapGesture modifier instead of the .onDelete modifer like so:

List {
    ForEach(countries, id: \.self) { country in
        Section(country.wrappedFullName) {
            ForEach(country.candyArray, id: \.self) { candy in
                Text(candy.wrappedName)
                    .onTapGesture {
                        offsets in deleteCandies(at: offsets, country: country)
                    }
            }
        }
    }
}

The problem I have been running into is trying to figure out how to get the IndexSet which your function requires.

So how could I find the IndexSet and make all this work with the .onTapGesutre modifer as shown above.

Thanks

2      

The .onTapGesture should provide the candy entity for the row tapped.

Offsets are provided by the .onDelete modifier and aren't available/provided by .onTapGesture.

I think - but haven't verfied - adding this to the .onTapGesture should do the trick...

moc.delete(candy)

try? moc.save()

2      

Sadly this won't work, and its because the data of the ForEach loop is coming from country.candyArray which looks like this:

public var candyArray: [Candy] {
        let set = candy as? Set<Candy> ?? []

        return set.sorted {
            $0.wrappedName < $1.unwrappedName
        }
    }

2      

So I have created a janky modified func from Chat-GPT which creates an somewhat solution and I am not entirely confident its actually deleteing anything.

@State private var indexSet = IndexSet()

func indexSet(for candy: Candy, at country: Country) -> IndexSet {
    var indexSet = IndexSet()
        for (index, arrayItem) in country.candyArray.enumerated() {
            if arrayItem == candy {
                indexSet.insert(index)
            }
        }
        return indexSet
    }
}
ForEach(countries, id: \.self) { country in
    Section(country.wrappedFullName) {
         ForEach(country.candyArray, id: \.self) { candy in
             Text(candy.wrappedName)
                 .onTapGesture {
                     indexSet = self.indexSet(for: candy, at: country)
                         deleteTask(at: indexSet, country: country)

                         print(group.taskArray.count)
                  }
             }
       }
 }

It will "delete" and save but there is no animation and I honestly believe there has to be a more elegant way that will make swiftui more happy

Edit: I have got the animation to work and that is by using withAnimation in the deleteCandies function

2      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.