-1

I'm stuck with adding items from struct to favorites. The idea:

  • I have a json with data for cards
  • On the main screen app shows a random card
  • The user could pick another random card or save it to favorites.

Code below.

  1. Creating a CardModel (file 1):
struct CardModel: Hashable, Codable, Identifiable {
    let id: Int
    let topic: String
    let category: String
    var saved: Bool
}
  1. Retrieving data from json and creating an array of structs (file 2):
var cardsModelArray: [CardModel] = load("LetsTalkTopics.json")

  1. Func to pickup random item from the array (file 3):
func pickRandomCard() -> CardModel {
    let randomCard = cardsModelArray.randomElement()!
    return randomCard
}
  1. Func to change the "saved" bool value (file 4)
func saveCard(card: CardModel) {
    let index = card.id
    cardsModelArray[index] = CardModel(id: index, topic: card.topic, category: card.category, saved: !card.saved)
}
  1. View file (file 5, simplified)
import SwiftUI

struct StackOverFlow: View {
    
    @State var currentCard = pickRandomCard()
    
    var body: some View {
        VStack{
            CardViewStackOver(cards: currentCard)
            Button("Show random card") {
                    currentCard = pickRandomCard()
            }
            
            Button("Save item") {
                saveCard(card: currentCard)
            }
    }
}

struct StackOverFlow_Previews: PreviewProvider {
    static var previews: some View {
        NavigationView {
            StackOverFlow()
        }
    }
}


struct CardViewStackOver: View {
    
    let cards: CardModel
    
    var body: some View {
        VStack {
            Text(cards.topic)
            Text(cards.category)
            Text(String(cards.id))
            HStack {
                if cards.saved {
                    Image(systemName: "heart.fill")
                        .font(.title)
                        .padding(15)
                        .padding(.bottom, 20)
                } else {
                    Image(systemName: "heart")
                        .font(.title)
                        .padding(15)
                        .padding(.bottom, 20)
                }
            }
        }
    }
}

But I definitely making something wrong, in a separate view I'm showing saved cards but it doesn't work (it shows some random cards, and some saved cards have duplicates). With my research, I found out that structs are immutable and when I'm trying to edit a value, basically swift creates a copy of it and makes changes in the copy. If so, what would be the right approach to create this favorite feature?

Stepan
  • 9
  • 3
  • Please try to create and post a [mre]. At least show us where and how you store and initialize your collection, show us your CardView and related classes and structs. – burnsi Sep 03 '22 at 16:43
  • @JoakimDanielson thanks for your answer. I'm totally new to programming and still trying to understand all the rules. In my (silly) case I manually added an id to the array values, from 1 to N (and already understand that my id is one more than the array index). But btw, maybe you could share some advice on what would be a more clever way to make this operation? – Stepan Sep 03 '22 at 17:19
  • So you removed the code I commented on, not sure what to make out of that. Now we don’t see what the save method does which is bad since you say you are having issues with your saved card. – Joakim Danielson Sep 03 '22 at 17:23
  • @JoakimDanielson oh, sorry (I tried to add the full code, but lost the crucial part). I've added the saved func. Meanwhile, as you mentioned about the index, I created a new func and in general, it works, but I'm sure that I chose a bad approach. New function: `func saveCardMinus(currentCard: CardModel) { var index = currentCard.id - 1 cardsModelArray[index].saved.toggle() }` – Stepan Sep 03 '22 at 18:23
  • You are still using id as index which is bad imo, instead you should find the index of the object in the array before updating the array. See for example https://stackoverflow.com/questions/62957650/how-to-get-index-of-value-exist-in-array-of-object-in-swift-5 – Joakim Danielson Sep 03 '22 at 18:40

2 Answers2

0

You could use a Binding to pass the selected card on. But I restructured the whole code as there were multiple bad practices involved:

struct StackOverFlow: View {
    //store the current selected index and the collection as state objects
    @State var currentCardIndex: Int?
    @State var cards: [CardModel] = []
    
    var body: some View {
        VStack{
            //if there is an index show the card
            if let index = currentCardIndex{
                CardViewStackOver(card: $cards[index])
            }
            Button("Show random card") {
                // just create a random index
                currentCardIndex = (0..<cards.count).randomElement()
            }
            
            Button("Save / delete item") {
                if let index = currentCardIndex{
                    //you save delete as favorite here
                    cards[index].saved.toggle()
                }
            }
        }.onAppear{
            //don´t exactly know where this function lives
            if cards.count == 0{
               cards = load("LetsTalkTopics.json")
            }
        }
    }
}

struct CardViewStackOver: View {
    //use binding wrapper here
    @Binding var card: CardModel
    
    var body: some View {
        VStack {
            Text(card.topic)
            Text(card.category)
            Text(String(card.id))
            HStack {
                Image(systemName: card.saved ? "heart.fill" : "heart")
                    .font(.title)
                    .padding(15)
                    .padding(.bottom, 20)
                    .onTapGesture {
                        card.saved.toggle()
                    }
            }
        }
    }
}
burnsi
  • 6,194
  • 13
  • 17
  • 27
0

A solution that fixed the problem:

func saveCardMinus(currentCard: CardModel) {
    var index = currentCard.id - 1
    cardsModelArray[index].saved.toggle()
}

But I'm sure that the whole solution is bad. What is the right/more proper way to solve it?

(and btw, now I face another problem: the icon for bool value updates is not in real-time, you need to open this card again to see a new value (filled heart/unfilled heart))

Stepan
  • 9
  • 3