UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Modifying Boolean value with @StateObject

Forums > SwiftUI

I was trying to use @StateObject to recreate the functionality of Landmarks app, which Apple released as tutorial last year. I have the following code. It's a basic app that you can see JSON objects' isFavorite value true or false. In Apple's example it's possible to change the isFavorite value by tapping on a button, and then persist the change.

Still, I do not fully understand Apple's example here. How it is possible to persist the value without rewriting the JSON itself? Isn't it a bad example?

import SwiftUI
import Combine
import Foundation

struct Item: Codable, Identifiable, Equatable {
    var id: Int
    var name: String
    var isFavorite: Bool
}

final class UserData: ObservableObject {
    @Published var items = Bundle.main.decode([Item].self, from: "data.json")
    @Published var showFavorites = false
}

struct ContentView: View {
    @State var itemID = Item.ID()
    @StateObject var userData = UserData()

    var body: some View {
        NavigationView {
            VStack {
                Toggle(isOn: $userData.showFavorites) {
                    Text("Show Favorites Only")
                }
                List {
                    ForEach(userData.items) { item in
                        if !userData.showFavorites || item.isFavorite {
                            NavigationLink(destination: ContentDetail(itemID: item.id - 1)) {
                                ContentRow(item: item)
                            }
                        }
                    }
                }
            }
        }
    }
}

struct ContentRow: View {
    var item: Item

    var body: some View {
        HStack {
            Text(item.name)
            Spacer()
            if item.isFavorite {
                Image(systemName: "star.fill")
                    .imageScale(.medium)
                    .foregroundColor(.yellow)
            }
        }
    }
}

struct ContentDetail: View {
    @State var itemID = Item.ID()
    @StateObject var userData = UserData()

    var body: some View {
        VStack {
            Button {
                userData.items[itemID].isFavorite.toggle()
            } label: {
                if userData.items[itemID].isFavorite {
                    Image(systemName: "star.fill")
                        .foregroundColor(Color.yellow)
                } else {
                    Image(systemName: "star")
                        .foregroundColor(Color.gray)
                }
            }
            Text(userData.items[itemID].name)
        }
    }
}

extension Bundle {
    func decode<T: Decodable>(_ type: T.Type, from file: String, dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .deferredToDate, keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys) -> T {
        guard let url = self.url(forResource: file, withExtension: nil) else {
            fatalError("Failed to locate \(file) in bundle.")
        }

        guard let data = try? Data(contentsOf: url) else {
            fatalError("Failed to load \(file) from bundle.")
        }

        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = dateDecodingStrategy
        decoder.keyDecodingStrategy = keyDecodingStrategy

        do {
            return try decoder.decode(T.self, from: data)
        } catch DecodingError.keyNotFound(let key, let context) {
            fatalError("Failed to decode \(file) from bundle due to missing key '\(key.stringValue)' not found – \(context.debugDescription)")
        } catch DecodingError.typeMismatch(_, let context) {
            fatalError("Failed to decode \(file) from bundle due to type mismatch – \(context.debugDescription)")
        } catch DecodingError.valueNotFound(let type, let context) {
            fatalError("Failed to decode \(file) from bundle due to missing \(type) value – \(context.debugDescription)")
        } catch DecodingError.dataCorrupted(_) {
            fatalError("Failed to decode \(file) from bundle because it appears to be invalid JSON")
        } catch {
            fatalError("Failed to decode \(file) from bundle: \(error.localizedDescription)")
        }
    }
}

2      

It looks like I've been creating a second instance of UserData() all along.

2      

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.