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)*/
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)
.