create a journaling app where you can write notes and save them once you close the app, allow to edit the note and cancel it, use swiftui in swift

To create a journaling app with the above requirements, we can use SwiftUI and CoreData.

First, we need to create a new SwiftUI project in Xcode. We can then create a new CoreData model file by selecting File > New > File and selecting "Data Model" under "Core Data". Let's call it "Journal".

In the "Journal" model, we will create an entity called "Note". The "Note" entity will have the following attributes:

  • id (String)
  • title (String)
  • body (String)
  • timestamp (Date)

Once we have created the CoreData model, we can then create a new SwiftUI view called "NoteListView". In this view, we will fetch all the Note entities from the CoreData database and display them in a list.

main.swift
struct NoteListView: View {
    @Environment(\.managedObjectContext) private var viewContext

    @FetchRequest(
        entity: Note.entity(),
        sortDescriptors: [NSSortDescriptor(keyPath: \Note.timestamp, ascending: false)],
        animation: .default)
    private var notes: FetchedResults<Note>

    var body: some View {
        NavigationView {
            List {
                ForEach(notes) { note in
                    NavigationLink(destination: NoteDetailView(note: note)) {
                        Text(note.title ?? "Untitled")
                    }
                }
                .onDelete(perform: deleteNotes)
            }
            .navigationBarTitle("Notes")
            .navigationBarItems(trailing: NavigationLink(destination: AddNoteView()) {
                Image(systemName: "plus")
            })
        }
    }

    private func deleteNotes(offsets: IndexSet) {
        withAnimation {
            offsets.map { notes[$0] }.forEach(viewContext.delete)

            do {
                try viewContext.save()
            } catch {
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }
}
1232 chars
40 lines

In NoteListView we fetch all the notes from the database and display them using SwiftUI's List view. We also define a deleteNotes function which enables us to delete a note by swiping left on it.

Next, we need to create a NoteDetailView which displays a single note.

main.swift
struct NoteDetailView: View {
    @Environment(\.managedObjectContext) private var viewContext
    @ObservedObject var note: Note

    @State private var isEditing = false
    @State private var editableTitle: String = ""
    @State private var editableBody: String = ""

    var body: some View {
        VStack {
            if isEditing {
                TextField("Title", text: $editableTitle)
                    .font(.largeTitle)
                    .multilineTextAlignment(.center)
                    .padding(10)

                Divider()

                TextEditor(text: $editableBody)
                    .frame(minHeight: 300)
            } else {
                Text(note.title ?? "")
                    .font(.largeTitle)
                Divider()
                Text(note.body ?? "")
            }
        }
        .navigationBarItems(
            trailing: isEditing ?
                Button("Cancel") {
                    self.isEditing = false
                    self.editableTitle = note.title ?? ""
                    self.editableBody = note.body ?? ""
                }
                : Button("Edit") {
                    self.isEditing = true
                    self.editableTitle = note.title ?? ""
                    self.editableBody = note.body ?? ""
                })
        .onDisappear(perform: saveEdits)
        .onAppear {
            self.editableTitle = note.title ?? ""
            self.editableBody = note.body ?? ""
        }
    }

    private func saveEdits() {
        if !isEditing { return }

        viewContext.performAndWait {
            note.title = editableTitle
            note.body = editableBody
            note.timestamp = Date()

            do {
                try viewContext.save()
            } catch {
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }

        self.isEditing.toggle()
    }
}
1973 chars
66 lines

In NoteDetailView, we display a single note and give the user the option to edit it. We achieve this using the isEditing state property. When the user taps the "Edit" button, isEditing is set to true, and the user can edit the note. When the user taps the "Cancel" button, isEditing is set back to false, and the note reverts to its original state. When the user navigates away from the NoteDetailView, we save any editing changes they made.

Finally, we will create an AddNoteView which allows the user to create a new note.

main.swift
struct AddNoteView: View {
    @Environment(\.managedObjectContext) private var viewContext
    @Environment(\.presentationMode) private var presentationMode

    @State private var title: String = ""
    @State private var body: String = ""

    var body: some View {
        NavigationView {
            VStack {
                TextField("Title", text: $title)
                Divider()
                TextEditor(text: $body)
                    .frame(minHeight: 300)
            }
            .navigationBarTitle("New Note")
            .navigationBarItems(trailing: Button("Save") {
                let newNote = Note(context: viewContext)
                newNote.id = UUID().uuidString
                newNote.title = title
                newNote.body = body
                newNote.timestamp = Date()

                do {
                    try viewContext.save()
                    self.presentationMode.wrappedValue.dismiss()
                } catch {
                    let nsError = error as NSError
                    fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
                }
            })
        }
    }
}
1152 chars
35 lines

In AddNoteView, we allow the user to create a new note by entering a title and body. When the user taps the "Save" button, we create a new Note entity in CoreData and save it to the database. We then dismiss the AddNoteView and return to the NoteListView.

With all the views in place, we can now run the app and test it out. This journaling app allows the user to create, edit and delete notes. Once a note is created, it is saved to the database even after the app is closed.

gistlibby LogSnag