0

I have an array that contains the data that is displayed on a list. When the user hits "new", a sheet pops up to allow the user to enter a new item to the list.

I just added a swipe option to edit this item and I wanted to reuse the same sheet to edit the item's text. But I'm having problems understanding how to check whether a specific item was selected (by UUID?) to pass to the sheet, or it's a new item.

Code:

let dateFormatter = DateFormatter()

struct NoteItem: Codable, Hashable, Identifiable {
    let id: UUID
    var text: String
    var date = Date()
    var dateText: String {
        dateFormatter.dateFormat = "EEEE, MMM d yyyy, h:mm a"
        return dateFormatter.string(from: date)
    }
    var tags: [String] = []
}

struct ContentView: View {
    @EnvironmentObject private var data: DataModel
    @State private var selectedItemId: UUID?
    @State var searchText: String = ""
    @State private var sheetIsShowing = false

    NavigationView {
        List(filteredNotes) { note in
             VStack(alignment: .leading) {
                 //....
                 // not relevant code
             }
             .swipeActions(allowsFullSwipe: false) {
                 Button(action: {
                      selectedItemId = note.id
                      self.sheetIsShowing = true
                 } ) {
                     Label("Edit", systemImage: "pencil")
                 }
             }
        }
        .toolbar {
            // new item
            Button(action: {
                 self.sheetIsShowing = true
            }) {
                 Image(systemName: "square.and.pencil")
            }
        }
        .sheet(isPresented: $sheetIsShowing) {
                if self.selectedItemId == NULL { // <-- this is giving me an error
                    let Note = NoteItem(id: UUID(), text: "New Note", date: Date(), tags: [])
                    SheetView(isVisible: self.$sheetIsShowing, note: Note)
                } else {
                    let index = data.notes.firstIndex(of: selectedItemId)
                    SheetView(isVisible: self.$sheetIsShowing, note: data.notes[index])
                }
            }

    }
}

My rationale was to check whether self.selectedItemId == NULL was null or not, if not then pass that element to the sheet to be edited, if yes, the as it as a new element.

What am I doing wrong? And if there is a standard way to pass information to the sheet based on whether there is an item select or not, could you show me?

Thanks!

Aleph
  • 465
  • 2
  • 12
  • You must use the sheet in the 2 places : one in foreach list the other for new. In this case you can inform the sheet it s is new or existing to be modified. – Ptit Xav Mar 16 '22 at 20:47
  • I'm having trouble understanding you. Are you telling me I need to code two different sheets? Or pass that it's a new item or an edit of an existing one? – Aleph Mar 16 '22 at 20:49
  • Two .sheet modifiers : one after .wipe, second after .toolbar. The sheet then can be the same – Ptit Xav Mar 16 '22 at 20:51
  • even if it's the same code? – Aleph Mar 16 '22 at 20:52

2 Answers2

0

Swift uses nil, not null, so the compiler is complaining when you are comparing selected items to null. However, you will have another issue. Your selectedItemId is optional, so you can't just use it in your else clause to make your note. You are better off using an if let to unwrap it. Change it to:

    .sheet(isPresented: $sheetIsShowing) {
        if let selectedItemId = selectedItemId,
            let index = data.notes.firstIndex(where: { $0.id == selectedItemId }) {
            SheetView(isVisible: self.$sheetIsShowing, note: data.notes[index])

        } else {
            let note = NoteItem(id: UUID(), text: "New Note", date: Date(), tags: [])
            SheetView(isVisible: self.$sheetIsShowing, note: note)

        }
    }

edit:

I realized that you were attempting to use two optionals without unwrapping them, so I changed this to an if let to make sure both are safely unwrapped.

Yrb
  • 8,103
  • 2
  • 14
  • 44
  • Thank you. I get the same error with nil: `The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions`. If I remove the if, then it compiles without an issue – Aleph Mar 16 '22 at 21:30
  • I was already editing my answer as I found another bug. Part of the problem is this is not a [Minimal, Reproducible Example (MRE)](https://stackoverflow.com/help/minimal-reproducible-example), so we can't see what you are seeing. The error you are seeing generally means there is a syntax error or logic error. – Yrb Mar 16 '22 at 21:32
  • And I found a third error. then you use `let index = data.notes.firstIndex(of: selectedItemId)`, `index` will be an optional, so I included that in the `if let` to safely unwrap it. – Yrb Mar 16 '22 at 21:35
  • Yeah, thank you I saw that. I winds down to `Cannot convert value of type 'UUID' to expected argument type 'NoteItem'` in `data.notes.firstIndex(of: selectedItemId)` – Aleph Mar 16 '22 at 21:38
  • Try the edit. It needed to be a comparison. `$0` is an unnamed argument that represents an element in your `data.notes` array as it is iterated through. – Yrb Mar 16 '22 at 21:42
0

From this post, you can do like this in your case:

struct SheetForNewAndEdit: View {
    @EnvironmentObject private var data: DataModel
    @State var searchText: String = ""
    // The selected row
    @State var selectedNote: NoteItem? = nil
    @State private var sheetNewNote = false
    // for test :
    @State private var filteredNotes: [NoteItem] = [
        NoteItem(id: UUID(), text: "111"),
        
        NoteItem(id: UUID(), text: "222")];
    
    var body: some View {
        NavigationView {
            List(filteredNotes) { note in
                VStack(alignment: .leading) {
                    //....
                    // not relevant code
                    
                    Text(note.text)
                }
                .swipeActions(allowsFullSwipe: false) {
                    Button(action: {
                        // the action select the note to display
                        selectedNote = note
                    } ) {
                        Label("Edit", systemImage: "pencil")
                    }
                }
            }
            // sheet is displayed depending on selected note
            .sheet(item: $selectedNote, content: {
                note in
                SheetView(note: note)
            })
            // moved tool bar one level (inside navigation view)
            .toolbar {
                // Toolbar item to have toolbar
                ToolbarItemGroup(placement: .navigationBarTrailing) {
                    ZStack {
                        Button(action: {
                            // change bool value
                            self.sheetNewNote.toggle()
                        }) {
                            Image(systemName: "square.and.pencil")
                        }
                    }
                }
            }
            .sheet(isPresented: $sheetNewNote) {
                let Note = NoteItem(id: UUID(), text: "New Note", date: Date(), tags: [])
                SheetView(note: Note)
            }
        }
    }
}

Note : SheetView does not need any more a boolean, but you can add one if you orefer

Ptit Xav
  • 3,006
  • 2
  • 6
  • 15
  • Great, thank you. I need to figure out how to not create a new item each time you edit one, but this makes it clear. – Aleph Mar 16 '22 at 22:13