How to read out values of Indexset elements in SwiftUI?

Viewed 669

I've got a pretty straightforward ForEach List and deleting an entry with onDelete() works without issues. However since one of the variables in the element stores a file's url, that I don't want to leave behind, I'm looking to implement a clean up through FileManager. But for the life of me I can't seem to get access to the actual url to plug into the FileManager action.

Here's what I've got, with associated errors:

struct ListItem: Identifiable, Codable {
    let id = UUID()
    let name: String
    let fileurl: URL
}

class ItemList: ObservableObject {
    @Published var items: [ListItem] {
        didSet {
            let encoder = JSONEncoder()
            if let encoded = try? encoder.encode(items) {
                UserDefaults.standard.set(encoded, forKey: "Items")
            }
        }
    }
    
    init() {
        if let items = UserDefaults.standard.data(forKey: "Items") {
            let decoder = JSONDecoder()
            if let decoded = try? decoder.decode([ListItem].self, from: items) {
                self.items = decoded
                return
            }
        }
        
        self.items = []
    }
}

struct ItemListView: View {
    func getDocumentsDirectory() -> URL {
        return FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
    }
    @ObservedObject var itemlist = ItemList()
    var supportFolder: URL {
        return FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
    }
@EnvironmentObject var thingy: Thingy
    var body: some View {
        VStack {
            Spacer()
            Spacer()
            Spacer()
            
            List {
                ForEach(itemlist.items) { item in
                    HStack {
                        VStack(alignment: .leading) {
                            Text(item.name)
                                .font(.headline)
                            Text(item.fileurl.lastPathComponent)
                        }
                        .onTapGesture {
self.thingy.load(self.supportFolder.appendingPathComponent("/listfiles/\(item.fileurl.lastPathComponent)")) //No issues with item.fileurl here
                            print("Selected: \(item.fileurl)")
                        }
                    }
                }
                .onDelete(perform: removeItems)
                
            }.disabled(keyMissing)
                .navigationBarTitle("List files", displayMode: .large).accentColor(Color.blue)
                .navigationBarItems(trailing:
                    Button(action: {
                        …
                    }
                    ) {NavigationLink(…) {
                        Image(systemName: "plus")
                        }


                        
                    }
                                        
            )
            Button(…){…}  
        }
            
        .onAppear(perform: {…})
            .onDisappear(…)
    }
    
    
    //    MARK: below is the issue
    func removeItems(at offsets: IndexSet) {
        
   do {
            try FileManager.default.removeItem(atPath: (itemlist.items.fileurl).path)
            print("File is deleted")
        }
        catch { print("Couldn't delete file")} //This would be ideal, but doesn't work stating "Value of type '[ListItem]' has no member 'fileurl'"



    itemlist.items.remove(atOffsets: offsets) //This works fine
        
       
    }
}

Ideally I would be able to simply plugin item list.items.fileurl but it acts like that's unknown.

I've also tried to do a huge workaround with list.items.description (and similar) but there's no consistent way to consistently extract the right part of the outpuT String. And to be honest feels rather odd I can't access the value of fileurl, I feel I'm staring the solution in the face but can't see it.

I've also tried to simply apply the logic at onDelete()

Like so:

.onDelete{
                    do {
                        try FileManager.default.removeItem(atPath: (binlist.items.fileloc).path)
                        print("File is deleted")
                    }
                    catch { print("Couldn't delete file")}
                }

But that throws me a Cannot convert value of type '() -> ()' to expected argument type 'Optional<(IndexSet) -> Void>'

I feel a bit out of my element here and can't find good documentation on this.

Edit: @Asperi's solution worked for me, can't believe I was staring the solution right in the face prior to posting this.

1 Answers

It can be like below

func removeItems(at offsets: IndexSet) {

    for i in offsets {
        do {
            try FileManager.default.removeItem(atPath: (itemlist.items[i].fileurl).path)
            print("File is deleted")
        }
        catch { print("Couldn't delete file")}
    }

    itemlist.items.remove(atOffsets: offsets) //This works fine
}
Related