swiftuiswift-playgroundenvironmentobject

I can’t get the array to update across all views despite using SWIFT playgrounds own code


I’m new so please forgive me.

I’m following Swift Playgrounds tutorials on an iPad. They’re good but a little buggy.

I’ve followed the tutorial. I should end up with code that shows 5 creatures, their name and emoji. I can press add to go to another view and add more to the list. If I put a ForEach code into the add view I can see the list update. But it doesn’t seem to update in the other views. And when I navigate away and back, the update is gone.

The tutorial gets us to use append to add the item to the list. The list is called using EnvironmentObject wrapper. I’ve searched the internet and can’t find what is wrong.

Can anyone help please?

Thank you

The code walkthrough comments in the code seem to be added automatically when copying from the app.

First file : CreatureZoo


import SwiftUI

//#-learning-task(creatureZoo)

/*#-code-walkthrough(creatureZoo.observableObject)*/
class CreatureZoo : ObservableObject {
    /*#-code-walkthrough(creatureZoo.observableObject)*/
    /*#-code-walkthrough(creatureZoo.creatures)*/
    /*#-code-walkthrough(creatureZoo.published)*/ @Published /*#-code-walkthrough(creatureZoo.published)*/var creatures = [
        /*#-code-walkthrough(creatureZoo.creature)*/
        Creature(name: "Gorilla", emoji: "🦍"),
        /*#-code-walkthrough(creatureZoo.creature)*/
        Creature(name: "Peacock", emoji: "🦚"),
        Creature(name: "Squid", emoji: "🦑"),
        Creature(name: "Owl", emoji: "🦉"),
        Creature(name: "Unicorn", emoji: "🦄")
        //#-learning-task(addCreatures)
        
    ]
    /*#-code-walkthrough(creatureZoo.creatures)*/
}

/*#-code-walkthrough(creatureZoo.creatureStruct)*/
struct Creature : Identifiable {
    var name : String
    var emoji : String
    
    var id = UUID()
    var offset = CGSize.zero
    var rotation : Angle = Angle(degrees: 0)
}
/*#-code-walkthrough(creatureZoo.creatureStruct)*/


// Should be hidden probably
extension CreatureZoo {
    func randomizeOffsets() {
        for index in creatures.indices {
            creatures[index].offset = CGSize(width: CGFloat.random(in: -200...200), height: CGFloat.random(in: -200...200))
            creatures[index].rotation = Angle(degrees: Double.random(in: 0...720))
        }
    }
    
    func synchronizeOffsets() {
        let randomOffset = CGSize(width: CGFloat.random(in: -200...200), height: CGFloat.random(in: -200...200))
        for index in creatures.indices {
            creatures[index].offset = randomOffset
        }
    }
    
    func indexFor(_ creature: Creature) ->  Double {
        if let index = creatures.firstIndex(where: { $0.id == creature.id }) {
            return Double(index)
        }
        return 0.0
    }
}

2nd file CreatureRow


import SwiftUI

struct CreatureRow: View {
    var creature : Creature
    
    var body: some View {
        HStack {
            Text(creature.name)
                .font(.title)
            
            Spacer()
            
            Text(creature.emoji)
                .resizableFont()
                .frame(minWidth: 125)
        }
        
        
    }
}

struct CreatureRow_Previews: PreviewProvider {
    static var previews: some View {
        CreatureRow(creature: Creature(name: "Dodo Bird", emoji: "🦤"))
    }
}

3rd file CreatureEditor


import SwiftUI
import Guide

struct CreatureEditor: View {
    //#-learning-task(defineVariablesCreatureEditor)
    @Environment(\.dismiss) var dismiss
    
    @State var newCreature : Creature = Creature(name: "", emoji: "")
    @EnvironmentObject var data : CreatureZoo
    var body: some View {
        SPCAssessableGroup(view: self) {
            
            
            VStack(alignment: .leading) {
                
                
                Form {
                    Section("Name") {
                        //#-learning-task(addACreatureEditorTextField)
                        TextField("Creature Name", text: $newCreature.name)
                    }   
                    
                    Section("Emoji") {
                        TextField("Emoji", text: $newCreature.emoji)
                        
                    }
                    
                    Section("Creature Preview") {
                        CreatureRow(creature: newCreature)
                    }
                    
                    Section("Current Creatures") {
                        ForEach(data.creatures) { creature in 
                            
                            CreatureRow(creature: creature)
                            /*#-code-walkthrough(forEach.id)*/
                            
                        }
                        
                    }
                }
                
                
            }
            
            
            .toolbar { 
                ToolbarItem { 
                    Button("Add") { 
                        data.creatures.append(newCreature)
                        dismiss()
                        
                        
                    }
                }
            }
            
        }.environmentObject(CreatureZoo()) // I added this myself to try to make things work 
    }
    
}

struct CreatureEditor_Previews: PreviewProvider {
    static var previews: some View {
        NavigationView() {
            CreatureEditor().environmentObject(CreatureZoo())
        }
    }
}


4th file contentView


import SwiftUI
import Guide

struct ContentView: View {
    
    @EnvironmentObject var data : CreatureZoo
    
    //#-learning-task(cleanUpApp)
    //#-learning-task(createYourOwn)

    var body: some View {
        SPCAssessableGroup(view: self) {
            List {
                
                Text("ContentView")
                
                Section("Dance") {
                    NavigationLink("make the creatures dance") {
                        DancingCreatures()
                            .navigationTitle("Dancing Creatures")
                            .environmentObject(CreatureZoo())
                    }
                    //#-learning-task(addNavLinkCreatureDance)
                    
                }
                /*#-code-walkthrough(forEach.id)*/
                Section("Add new creature") {
                    NavigationLink(" Add new") {
                        
                        CreatureEditor()
                            .navigationTitle("Add new")
                            .environmentObject(CreatureZoo())
                        
                    }
                    
                }
                ForEach(data.creatures) { creature in 
                    
                    CreatureRow(creature: creature)
                    /*#-code-walkthrough(forEach.id)*/
                    
                }
                
                .onDelete { indexSet in 
                    data.creatures.remove(atOffsets: indexSet)
                }
                
                                
            }
            .toolbar { 
                ToolbarItem { 
                    NavigationLink("Add") { 
                        CreatureEditor()
                            .navigationTitle("Add new creature")
                            .environmentObject(CreatureZoo())
                    }
                }
            }
            
        }
    }
}


5th file myApp


import SwiftUI
import Guide
//#-learning-task(myApp)

@main
/*#-code-walkthrough(myApp.appProtocol)*/
struct MyApp: App {
    /*#-code-walkthrough(myApp.appProtocol)*/
    @StateObject var data = CreatureZoo()
    /*#-code-walkthrough(myApp.body)*/
    var body: some Scene {
        SPCAssessableWindowGroup(app: self, assessmentCandidates: [CreatureZoo()]) {
            NavigationView { 
                
                
                ContentView()
                    .navigationTitle("My Creatures")
                    .environmentObject(data)
                
            }
            
                
            }
            

            /*#-code-walkthrough(myApp.contentView)*/
            
            /*#-code-walkthrough(myApp.contentView)*/

        }
    }
    /*#-code-walkthrough(myApp.body)*/


Solution

  • When you add .environmentObject(CreatureZoo()) you're creating a new instance which is not shared between the views. You should add .environmentObject once in your App file.

    In ContentView, this .environmentObject(CreatureZoo()) should be .environmentObject(data).