Search code examples
google-cloud-firestoreswiftuiswiftui-navigationlink

NavigationLink Dismisses after Textfield Value is Updated to Firestore


I'm trying to make something similar to the iOS notes app but for journaling; basically, I want there to be a list of journal entry cells users can scroll through which each display a detail view after they're clicked on where the user can view and edit their journal entry. The updating works fine, the only issue is that JournalDetailView dismisses itself after updateEntry() is called (after the user taps the "Done" button). I'm guessing this is because updateEntry() forces the view to reload, but I'm not sure how to get around this.

Here's the model:

struct JournalEntry: Identifiable, Hashable, Codable {
    @DocumentID var id: String? = UUID().uuidString
    @ServerTimestamp var date: Timestamp?
    var text: String
    var userId: String?
}

Here's the view code:

struct JournalCellView: View {
    @ObservedObject var vm: JournalViewModel
    @Binding var addButtonTapped: Bool
    @State var showDetail = false
    @State var entry: JournalEntry
    var body: some View {
        NavigationLink(destination: JournalDetailView(vm: vm, entry: $entry, text: entry.text), isActive: $showDetail, label: {
                VStack {
                    HStack {
                    Text(entry.date!.dateValue(), style: .date)
                        .fontWeight(.bold)
                        .font(.system(size: 18))
                        .foregroundColor(.black)
                        .padding(.bottom, 3)
                        Spacer()
                    }
                    HStack {
                    Text(entry.text)
                        .font(.system(size: 14))
                        .foregroundColor(.secondary)
                        .lineLimit(1)
                        Spacer()
                    }
                }.padding()
                .background(RoundedRectangle(cornerRadius: 18).foregroundColor(.white))
                .padding(.vertical, 4)
                .onTapGesture {
                    showDetail = true
                }
                .onAppear {
                    if addButtonTapped {
                        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
                           showDetail = true
                        }
                        addButtonTapped = false
                    }
                }
            })
    }
}
struct JournalDetailView: View {
    @Environment(\.presentationMode) var presentationMode
    @ObservedObject var vm: JournalViewModel
    @Binding var entry: JournalEntry
    @State var text: String
    @State var isTyping = false
    var body: some View {
        VStack {
            HStack {
                Button(action: {
                    presentationMode.wrappedValue.dismiss()
                }, label: { Image(systemName: "chevron.left").foregroundColor(.burgundy) })
                
                Spacer()
                if isTyping {
                    Button(action: {
                        endEditing()
                        updateEntry()
                        isTyping = false
                    }) {
                        Text("Done")
                            .foregroundColor(.burgundy)
                    }
                } else {
                    Text("")
                }
                
            }.padding(.vertical)
            
            Text(entry.date!.dateValue(), style: .date)
            TextEditor(text: $text)
                .onTapGesture {
                    isTyping = true
                }
            Spacer()
        }.padding()
        .navigationBarHidden(true)
    }
    func updateEntry() {
        vm.updateJournalEntry(docID: entry.id!, date: entry.date!, text: text)
    }
}

Here's updateJournalEntry():

func updateJournalEntry(docID: String, date: Timestamp, text: String) {
        db.collection("journals").document(docID
        ).updateData(["date": date, "text": text, "userId": Auth.auth().currentUser!.uid])
    }

Solution

  • I managed to get around this by only updating after the view would be dismissed naturally using .onDisappear and .onReceive. Not the cleanest solution, but it works. If someone has another suggestion, please contribute!

    .onDisappear {
                updateEntry()
            }
            .onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) { _ in
                updateEntry()
            }