Skip to content

Instantly share code, notes, and snippets.

@jnewc
Last active January 21, 2024 17:40
Show Gist options
  • Star 41 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save jnewc/35692b2a5985c3c99e847ec56098a451 to your computer and use it in GitHub Desktop.
Save jnewc/35692b2a5985c3c99e847ec56098a451 to your computer and use it in GitHub Desktop.
A notes app written in >100 lines of swift using SwiftUI
import SwiftUI
let dateFormatter = DateFormatter()
struct NoteItem: Codable, Hashable, Identifiable {
let id: Int
let text: String
var date = Date()
var dateText: String {
dateFormatter.dateFormat = "MMM d yyyy, h:mm a"
return dateFormatter.string(from: date)
}
}
struct ContentView : View {
@State var items: [NoteItem] = {
guard let data = UserDefaults.standard.data(forKey: "notes") else { return [] }
if let json = try? JSONDecoder().decode([NoteItem].self, from: data) {
return json
}
return []
}()
@State var taskText: String = ""
@State var showAlert = false
@State var itemToDelete: NoteItem?
var alert: Alert {
Alert(title: Text("Hey!"),
message: Text("Are you sure you want to delete this item?"),
primaryButton: .destructive(Text("Delete"), action: deleteNote),
secondaryButton: .cancel())
}
var inputView: some View {
HStack {
TextField("Write a note ...", text: $taskText)
.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
.clipped()
Button(action: didTapAddTask, label: { Text("Add") }).padding(8)
}
}
var body: some View {
VStack {
inputView
Divider()
List(items) { item in
VStack(alignment: .leading) {
Text(item.dateText).font(.headline)
Text(item.text).lineLimit(nil).multilineTextAlignment(.leading)
}
.onLongPressGesture {
self.itemToDelete = item
self.showAlert = true
}
}
.alert(isPresented: $showAlert, content: {
alert
})
}
}
func didTapAddTask() {
let id = items.reduce(0) { max($0, $1.id) } + 1
items.insert(NoteItem(id: id, text: taskText), at: 0)
taskText = ""
save()
}
func deleteNote() {
guard let itemToDelete = itemToDelete else { return }
items = items.filter { $0 != itemToDelete }
save()
}
func save() {
guard let data = try? JSONEncoder().encode(items) else { return }
UserDefaults.standard.set(data, forKey: "notes")
}
}
@whoyawn
Copy link

whoyawn commented Aug 13, 2019

Title should be "A notes app written in < 100 lines of swift using SwiftUI" :)

@NiklasPeterson
Copy link

Hi @jnewc :) I'm just starting to learn SwiftUI. But when I try to use your code above in a Playgrounds Project I get so many errors :/

Screenshot 2021-02-10 at 07 55 59

@SammyDerksen
Copy link

SammyDerksen commented May 4, 2021

Here is the Code without errors:

import SwiftUI

let dateFormatter = DateFormatter()

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

struct ContentView : View {
    @State var items: [NoteItem] = {
        guard let data = UserDefaults.standard.data(forKey: "notes") else { return [] }
        if let json = try? JSONDecoder().decode([NoteItem].self, from: data) {
            return json
        }
        return []
    }()
    
    @State var taskText: String = ""
    
    @State var showAlert = false
    
    @State var itemToDelete: NoteItem?
    
    var alert: Alert {
        Alert(title: Text("Hey!"),
              message: Text("Are you sure you want to delete this item?"),
              primaryButton: .destructive(Text("Delete"), action: deleteNote),
              secondaryButton: .cancel())
    }
    
    var inputView: some View {
        HStack {
            TextField("Write a note ...", text: $taskText)
                .padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
                .clipped()
            Button(action: didTapAddTask, label: { Text("Add") }).padding(8)
        }
    }
    
    var body: some View {
        VStack {
            inputView
            Divider()
            List(items) { item in
                VStack(alignment: .leading) {
                    Text(item.dateText).font(.headline)
                    Text(item.text).lineLimit(nil).multilineTextAlignment(.leading)
                }
                .onLongPressGesture {
                    self.itemToDelete = item
                    self.showAlert = true
                }
            }
            .alert(isPresented: $showAlert, content: {
                alert
            })
        }
    }
    
    func didTapAddTask() {
        let id = items.reduce(0) { max($0, $1.id) } + 1
        items.insert(NoteItem(id: id, text: taskText), at: 0)
        taskText = ""
        save()
    }
    
    func deleteNote() {
        guard let itemToDelete = itemToDelete else { return }
        items = items.filter { $0 != itemToDelete }
        save()
    }
    
    func save() {
        guard let data = try? JSONEncoder().encode(items) else { return }
        UserDefaults.standard.set(data, forKey: "notes")
    }
}

@jnewc
Copy link
Author

jnewc commented May 4, 2021

Updated - thanks @SammyDerksen

@NiklasPeterson
Copy link

Awesome thanks @SammyDerksen for the updated version 👍

And also @jnewc for sharing this to begin with 😄

@teddinotteddy
Copy link

Here is the Code without errors:

import SwiftUI

let dateFormatter = DateFormatter()

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

struct ContentView : View {
    @State var items: [NoteItem] = {
        guard let data = UserDefaults.standard.data(forKey: "notes") else { return [] }
        if let json = try? JSONDecoder().decode([NoteItem].self, from: data) {
            return json
        }
        return []
    }()
    
    @State var taskText: String = ""
    
    @State var showAlert = false
    
    @State var itemToDelete: NoteItem?
    
    var alert: Alert {
        Alert(title: Text("Hey!"),
              message: Text("Are you sure you want to delete this item?"),
              primaryButton: .destructive(Text("Delete"), action: deleteNote),
              secondaryButton: .cancel())
    }
    
    var inputView: some View {
        HStack {
            TextField("Write a note ...", text: $taskText)
                .padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
                .clipped()
            Button(action: didTapAddTask, label: { Text("Add") }).padding(8)
        }
    }
    
    var body: some View {
        VStack {
            inputView
            Divider()
            List(items) { item in
                VStack(alignment: .leading) {
                    Text(item.dateText).font(.headline)
                    Text(item.text).lineLimit(nil).multilineTextAlignment(.leading)
                }
                .onLongPressGesture {
                    self.itemToDelete = item
                    self.showAlert = true
                }
            }
            .alert(isPresented: $showAlert, content: {
                alert
            })
        }
    }
    
    func didTapAddTask() {
        let id = items.reduce(0) { max($0, $1.id) } + 1
        items.insert(NoteItem(id: id, text: taskText), at: 0)
        taskText = ""
        save()
    }
    
    func deleteNote() {
        guard let itemToDelete = itemToDelete else { return }
        items = items.filter { $0 != itemToDelete }
        save()
    }
    
    func save() {
        guard let data = try? JSONEncoder().encode(items) else { return }
        UserDefaults.standard.set(data, forKey: "notes")
    }
}

@SammyDerksen for some reason playgrounds gives me an abort error anyway to fix that?

@arrrshia
Copy link

This is incredible!

Copy link

ghost commented Sep 17, 2023

It helped me to improve myself in SwiftUI at the beginning. If you want to view my project: https://github.com/leparutill/Noteman/tree/main

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment