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.