Variable overriding itself when adding to a list

This is yet another expansion of my TextField and function questions.

The code for my Lunch Card app is pretty much set in stone, except for a little bug with a variable. This bug happens when you create a new card. Here's the code for both CardsView and AddView.

CardView:
Code Block
//
//  CardsView.swift
//  Lunch Card (iOS)
//
//  Created by Joshua Srery on 12/17/20.
//  Additional code by OOPer on Apple Developer Forums
//
import SwiftUI
struct Card: Identifiable {
    let id = UUID()
    let title: String
}
struct CardsView: View {
    @StateObject var cardsInfo = CardsInfo()
    @StateObject var sheetInfo = SheetInfo()
    @State private var editMode = EditMode.inactive
    
    var body: some View {
        NavigationView {
            List {
                ForEach(cardsInfo.cards) { cards in
                    NavigationLink(destination: CardFullView(cname: cardsInfo.newCard.cname, name: cardsInfo.newCard.name, id: cardsInfo.newCard.id)) {
                        CardRow(cname: cardsInfo.newCard.cname, name: cardsInfo.newCard.name, id: cardsInfo.newCard.id)
                    }
                }
                .onDelete(perform: onDelete)
                .onMove(perform: onMove)
            }.listStyle(PlainListStyle())
            .navigationTitle("Cards")
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    EditButton()
                }
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button(action: {
                        self.sheetInfo.showSheetView.toggle()
                    }) {
                        Image(systemName: "plus")
                    }
                }
            }
            .environment(\.editMode, $editMode)
            .sheet(isPresented: $sheetInfo.showSheetView) {
                AddView(cardsInfo: cardsInfo, sheetInfo: sheetInfo)
            }
        }
    }
    
    private func onDelete(offsets: IndexSet) {
        cardsInfo.cards.remove(atOffsets: offsets)
    }
    private func onMove(source: IndexSet, destination: Int) {
        cardsInfo.cards.move(fromOffsets: source, toOffset: destination)
    }
}


AddView:
Code Block
//
//  AddView.swift
//  Lunch Card (iOS)
//
//  Created by Joshua Srery on 12/18/20.
// Additional code by OOPer on Apple Developer Forums
//
import SwiftUI
struct AddView: View {
    @ObservedObject var cardsInfo: CardsInfo
    @ObservedObject var sheetInfo: SheetInfo
    var body: some View {
        NavigationView {
            VStack {
                CardView(name: cardsInfo.newCard.name, id: cardsInfo.newCard.id)
                TextField("Name", text: $cardsInfo.newCard.name)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .shadow(radius: 10)
                TextField("ID", text: $cardsInfo.newCard.id)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .keyboardType(.numberPad)
                    .shadow(radius: 10)
                TextField("Card Name", text: $cardsInfo.newCard.cname)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .shadow(radius: 10)
                Button(action: {
                    cardsInfo.add()
                    sheetInfo.showSheetView = false
                }) {
                    Text("Create")
                        .bold()
                }
                .disabled(self.cardsInfo.newCard.name.isEmpty self.cardsInfo.newCard.id.isEmpty self.cardsInfo.newCard.cname.isEmpty)
                .foregroundColor(.white)
                .padding()
                .padding(.horizontal, 100)
                .background(Color.accentColor)
                .cornerRadius(10)
            }.padding()
            .navigationTitle(cardsInfo.newCard.cname)
            .navigationBarTitleDisplayMode(.inline)
        }
    }
}

AddView shows as a sheet.

When looking, you can see that cardsInfo.newCard is being used to signify - if not obvious - a new card. But, the bug here is that when you add multiple cards, the previous cards are being overridden.

A solution I'm thinking of is adding the already-added cards into some kind of database or text file, but (1) I have literally no idea how I would approach that, and (2) that would probably mean rewriting about 3/4 of the existing code, which is a path that I am trying to avoid.

Accepted Reply

The word override can be interpreted in several ways, so I may be mistaking something, but your problem seems to be caused by your List in CardsView.

Code Block
NavigationLink(destination: CardFullView(cname: cardsInfo.newCard.cname, name: cardsInfo.newCard.name, id: cardsInfo.newCard.id)) {
CardRow(cname: cardsInfo.newCard.cname, name: cardsInfo.newCard.name, id: cardsInfo.newCard.id)
}

You use ForEach for cardsInfo.cards, receiving each card in card. But your CardRow always shows cardsInfo.newCard ignoring each card.
So, every row in your List shows always the only CardInfo kept in cardsInfo.newCard.

You may need to have an Array of CardInfo in your CardsInfo, and show it in your CardsView:

CardsInfo

Code Block
struct CardInfo: Identifiable { //<- Needs `Identifiable` to use with `ForEach`
var name: String = ""
var id: String = ""
var cname: String = ""
}
class CardsInfo: ObservableObject {
@Published var newCard: CardInfo = CardInfo()
@Published var cards: [CardInfo] = [] //<- Make this an Array of `CardInfo`, not of `Card`
func add() {
cards.append(newCard) //<- Add `newCard` to `cards`
}
}


CardsView:

Code Block
var body: some View {
NavigationView {
List {
Text("\(sheetInfo.showSheetView.description)")
ForEach(cardsInfo.cards) { card in
NavigationLink(destination: CardFullView(cname: card.cname, name: card.name, id: card.id)) { //<-
CardRow(cname: card.cname, name: card.name, id: card.id) //<-
}
}
.onDelete(perform: onDelete)
.onMove(perform: onMove)
}
//...
}
}


If this change causes something wrong, please try to explain that showing --
  • Steps to reproduce

  • What you expect

  • What you actually get


Replies

The word override can be interpreted in several ways, so I may be mistaking something, but your problem seems to be caused by your List in CardsView.

Code Block
NavigationLink(destination: CardFullView(cname: cardsInfo.newCard.cname, name: cardsInfo.newCard.name, id: cardsInfo.newCard.id)) {
CardRow(cname: cardsInfo.newCard.cname, name: cardsInfo.newCard.name, id: cardsInfo.newCard.id)
}

You use ForEach for cardsInfo.cards, receiving each card in card. But your CardRow always shows cardsInfo.newCard ignoring each card.
So, every row in your List shows always the only CardInfo kept in cardsInfo.newCard.

You may need to have an Array of CardInfo in your CardsInfo, and show it in your CardsView:

CardsInfo

Code Block
struct CardInfo: Identifiable { //<- Needs `Identifiable` to use with `ForEach`
var name: String = ""
var id: String = ""
var cname: String = ""
}
class CardsInfo: ObservableObject {
@Published var newCard: CardInfo = CardInfo()
@Published var cards: [CardInfo] = [] //<- Make this an Array of `CardInfo`, not of `Card`
func add() {
cards.append(newCard) //<- Add `newCard` to `cards`
}
}


CardsView:

Code Block
var body: some View {
NavigationView {
List {
Text("\(sheetInfo.showSheetView.description)")
ForEach(cardsInfo.cards) { card in
NavigationLink(destination: CardFullView(cname: card.cname, name: card.name, id: card.id)) { //<-
CardRow(cname: card.cname, name: card.name, id: card.id) //<-
}
}
.onDelete(perform: onDelete)
.onMove(perform: onMove)
}
//...
}
}


If this change causes something wrong, please try to explain that showing --
  • Steps to reproduce

  • What you expect

  • What you actually get


This works! Thank you!