What is the reason that my list is not functioning/ saving?

Hello, I am a newbie, I am working on 100days of swift. I do not get any errors. I am trying to display words on my list and save them. I am working on a flashcard app. I have 3 files, two of them are views.

My model

import Foundation

import SwiftUI

struct Knowledge: Identifiable,Codable {

    var id = UUID()

    var term: String

    var explanation: String?

    var tags: String?

}

class Deck: ObservableObject {

   @Published var knowledgeArray = [Knowledge]()

    init() {

        if let savedItems = UserDefaults.standard.data(forKey: "KnowledgeItems") {

            if let decodedItems = try? JSONDecoder().decode([Knowledge].self, from: savedItems) {

                knowledgeArray = decodedItems

                return

            }

        }

        knowledgeArray = []

    }
}

my second view that makes adds terms.

import SwiftUI

import Foundation

struct addCard: View {

    @Environment(\.presentationMode) var presentationMode

    @Environment(\.dismiss) var dismiss
    @State private var terms = ""

    @State private var tags = ""

    @State private var explanations = ""

    @StateObject var KN: Deck

    var body: some View {

        VStack {

            Form {

                TextField("Term", text: $terms)

                TextField("Explanation", text: $explanations)

                TextField("Tag", text: $tags)

            }

            Button("Save") {

                let item = Knowledge(term: terms, explanation: explanations , tags: tags)

                KN.knowledgeArray.append(item)

                self.presentationMode.wrappedValue.dismiss()


                print("Button is working")

            }

            .padding()

        }

    }

}

and my final list view

import SwiftUI

struct cardList: View {

   
    @ObservedObject var decks = Deck()

    @State private var showingaddCard = false

    var body: some View {

        NavigationView {

            List {

                ForEach(decks.knowledgeArray) { cardName in

                    Text(cardName.term)

                        .font(.title)

                }

                .onDelete(perform: removeItems)

            }

            .toolbar {

                Button {

                    showingaddCard = true

                } label: {

                    Image(systemName: "plus")

                }

            }

            .navigationTitle("Card List")

        }

        .sheet(isPresented: $showingaddCard) {

            addCard(KN: Deck())

        }

    }

    func removeItems(at offsets: IndexSet) {

        decks.knowledgeArray.remove(atOffsets: offsets)
    }

}

Any advice is appreciated.

Answered by Claude31 in 693680022

You misused Environment Object.

This should work:

struct Knowledge: Identifiable, Codable {
    var id = UUID()
    var term: String
    var explanation: String?
    var tags: String?
}

class Deck: ObservableObject {

   @Published var knowledgeArray : [Knowledge] = []

    init() {

        if let savedItems = UserDefaults.standard.data(forKey: "KnowledgeItems") {
            if let decodedItems = try? JSONDecoder().decode([Knowledge].self, from: savedItems) {
                knowledgeArray = decodedItems
                return
            }
        }
        knowledgeArray = []

    }
}

struct AddCard: View {

    @Environment(\.presentationMode) var presentationMode
    @Environment(\.dismiss) var dismiss
    
    @State private var terms = ""
    @State private var tags = ""
    @State private var explanations = ""
//    @StateObject
    @EnvironmentObject var KN: Deck

    var body: some View {

        VStack {
            Form {
                TextField("Term", text: $terms)
                TextField("Explanation", text: $explanations)
                TextField("Tag", text: $tags)
            }

            Button("Save") {
                let item = Knowledge(term: terms, explanation: explanations , tags: tags)
                KN.knowledgeArray.append(item)
                let encoder = JSONEncoder()
                if let encoded = try? encoder.encode(KN.knowledgeArray) {
                    let defaults = UserDefaults.standard
                    defaults.set(encoded, forKey: "KnowledgeItems")
                }
                self.presentationMode.wrappedValue.dismiss()

                print("Button is working")
            }

            .padding()

        }

    }

}

struct cardList: View {

    @ObservedObject var decks = Deck()

    @State private var showingaddCard = false

    var body: some View {

        NavigationView {
            List {
                ForEach(decks.knowledgeArray) { cardName in
                    Text(cardName.term)
                        .font(.title)
                }
                .onDelete(perform: removeItems)

            }

            .toolbar {
                Button {
                    showingaddCard = true
                } label: {
                    Image(systemName: "plus")
                }
            }

            .navigationTitle("Card List")

        }
        
        .sheet(isPresented: $showingaddCard) {
            AddCard().environmentObject(decks)
        }
        

    }

    func removeItems(at offsets: IndexSet) {

        decks.knowledgeArray.remove(atOffsets: offsets)
    }

}

I do not get any errors

Are you sure ? There is an error in your code:

There misses a closing curly bracket at the end of

struct Knowledge: Identifiable, Codable {
  • addCard() is never called. Where and how do you add card ?
  • What is your problem ? What do you get ? What do you expect ?

So, to get something on screen, I created 2 knowledge in code, so that knowledgeArray is no more empty:

    init() {

        if let savedItems = UserDefaults.standard.data(forKey: "KnowledgeItems") {

            if let decodedItems = try? JSONDecoder().decode([Knowledge].self, from: savedItems) {
                knowledgeArray = decodedItems
                return
            }
        }
        let someKnowledge1 = Knowledge(id: UUID(), term: "First")
        let someKnowledge2 = Knowledge(id: UUID(), term: "Second")
        knowledgeArray = [someKnowledge1, someKnowledge2]

    }

hold on let me edit my code.

You need also to save to UserDefaults.

With code like:

let encoder = JSONEncoder()
if let encoded = try? encoder.encode(knowledgeArray) {
    let defaults = UserDefaults.standard
    defaults.set(encoded, forKey: "KnowledgeItems")
}
Accepted Answer

You misused Environment Object.

This should work:

struct Knowledge: Identifiable, Codable {
    var id = UUID()
    var term: String
    var explanation: String?
    var tags: String?
}

class Deck: ObservableObject {

   @Published var knowledgeArray : [Knowledge] = []

    init() {

        if let savedItems = UserDefaults.standard.data(forKey: "KnowledgeItems") {
            if let decodedItems = try? JSONDecoder().decode([Knowledge].self, from: savedItems) {
                knowledgeArray = decodedItems
                return
            }
        }
        knowledgeArray = []

    }
}

struct AddCard: View {

    @Environment(\.presentationMode) var presentationMode
    @Environment(\.dismiss) var dismiss
    
    @State private var terms = ""
    @State private var tags = ""
    @State private var explanations = ""
//    @StateObject
    @EnvironmentObject var KN: Deck

    var body: some View {

        VStack {
            Form {
                TextField("Term", text: $terms)
                TextField("Explanation", text: $explanations)
                TextField("Tag", text: $tags)
            }

            Button("Save") {
                let item = Knowledge(term: terms, explanation: explanations , tags: tags)
                KN.knowledgeArray.append(item)
                let encoder = JSONEncoder()
                if let encoded = try? encoder.encode(KN.knowledgeArray) {
                    let defaults = UserDefaults.standard
                    defaults.set(encoded, forKey: "KnowledgeItems")
                }
                self.presentationMode.wrappedValue.dismiss()

                print("Button is working")
            }

            .padding()

        }

    }

}

struct cardList: View {

    @ObservedObject var decks = Deck()

    @State private var showingaddCard = false

    var body: some View {

        NavigationView {
            List {
                ForEach(decks.knowledgeArray) { cardName in
                    Text(cardName.term)
                        .font(.title)
                }
                .onDelete(perform: removeItems)

            }

            .toolbar {
                Button {
                    showingaddCard = true
                } label: {
                    Image(systemName: "plus")
                }
            }

            .navigationTitle("Card List")

        }
        
        .sheet(isPresented: $showingaddCard) {
            AddCard().environmentObject(decks)
        }
        

    }

    func removeItems(at offsets: IndexSet) {

        decks.knowledgeArray.remove(atOffsets: offsets)
    }

}

Thanks for feedback. Great it works.

And don't forget to close the thread.

.

But why the state object did not work?

This should give you further explanations:

h t t p s : / / jaredsinclair.com/2020/05/07/swiftui-cheat-sheet.html

What is the reason that my list is not functioning/ saving?
 
 
Q