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

SOLVED: Trouble with updating Content view in day 77

Forums > 100 Days of SwiftUI

@Paul  

So I took a large break in between day 77 and everything else so I expect I am missing something easy.

When I append to the people Array in my content view model it crashs. I'm, assuming there is a better way to handle this but honestly I've spent hours on this going in circles and just can't work out what I've messed up (Because of this some of the code is quite a mess).

I know in other laungages appending to collections whilst they are being enumerated on in the view can cause issues but I'm not sure of the swiftui way to handle this.

Any help would be greatly appreciated. Code is available here for convenience: https://github.com/PaulLocksley/100Days-of-SwiftUI-Day77-Help-Link

Content View

struct ContentView: View {
    @StateObject private var viewModel = ViewModel()
    var body: some View {
        ZStack{
            VStack {
                Section{
                    ForEach(viewModel.people.sorted(), id: \.id){ person in
                        HStack{

                            Text("Person")
                                .padding()
//                            if let pic = person.picture {
//                                Image(uiImage: UIImage(data: pic)!)
//                                    .scaledToFit()
//                                    .padding()
//                            }
                            padding()
                        }
                    }
                }
                Spacer()
                HStack{
                    Spacer()
                    Button(action: {
                        viewModel.AddPerson()
                        viewModel.updateView()
                        }) {
                        Image(  systemName: "plus")
                            .padding()
                            .background(.blue.opacity(0.90))
                            .foregroundColor(.white)
                            .font(.title)
                            .clipShape(Circle())
                            .padding(.trailing)          
                    }
                }
            }.sheet(isPresented: $viewModel.addingPerson) {
                AddPerson(viewModel: viewModel){ np in
                    viewModel.insertPerson(person: np)

                }

Content view model


import Foundation
import UIKit

extension ContentView {
    @MainActor class ViewModel: ObservableObject   {

       @Published  var addingPerson: Bool
        @Published private(set) var people: [Person]

        let savePath = FileManager.documentsDirectory.appendingPathComponent("PeopleList")
        init() {
            self.addingPerson = false
            do {
                let data = try Data(contentsOf: savePath)
                people = try JSONDecoder().decode([Person].self, from: data)
            }catch{
                people = []
                print("Unable to load")
            }
        }

        func AddPerson(){
            addingPerson = true
        }

        func closeSheet() {
            Task { @MainActor in

                addingPerson = false
            }
        }

        func insertPerson(person: Person){
            people.append(person)
            //save()
            //updateView()

        }

        func updatePesron(_ person: Person){
            save()
        }

        func updateView() {
            self.objectWillChange.send()

        }

        func save() {}

Add person view:

struct AddPerson: View {
    @Environment(\.dismiss) var dismiss
    @ObservedObject var viewModel: ContentView.ViewModel
    @State var name: String = ""
    @State var desc: String = ""
    @State var  picture: UIImage?
    var onSave: (Person) -> Void
    @State var addingPicture = false
    @State var im: Image?

    var body: some View {
        VStack{
            Form{
                Section{
                    if let picture {
                        Image(uiImage: picture)
                            .scaledToFill()
                    }else{
                        Button{
                            addingPicture = true
                        }label: {
                            HStack{
                                padding()
                                Text("Add Picture")
                                Image(systemName: "camera")
                                    .font(.title)
                                padding()
                            }
                        }
                    }
                }

                Section{
                    TextField("Name", text: $name)
                    TextField("Description", text: $desc)
                }
                HStack{
                    Button("Add New Person"){
                        let newPerson = Person(Picture: picture,name: name, desc: desc)
                        onSave(newPerson)
                        dismiss()

                    }

                    .font(.title)
                    .fontWeight(.bold)
                    .padding(.leading, 20)
                    .padding(.trailing, 20)
                    .background(.blue)
                    .foregroundColor(.white)
                    .clipShape(Capsule())
                    .padding(.all, 2)
                    .frame(width: 500, alignment: .center)
                }
            }

        }
        .sheet(isPresented: $addingPicture){
                ImagePicker(image: $picture)
            }
    }

}

2      

@Bnerd  

My guess without testing is the below

Change your AddPerson code as below:

@ObservedObject var viewModel: ViewModel

2      

@Paul  

Thanks for the help @Bnerd, Unfortunatly that doesn't work as AddPerson doesn't know what ViewModel is wihtout the context of ContentView.

Is there any particular reason you think this is the path to go down?

2      

@Bnerd  

My mistake I don't know why but I thought your model was an extension of View ...I know silly.. I just noticed that in your model the people array is private(set), private set means you can read but not write, does it work if you remove the(set) ?

@Published private var people: [Person]

2      

@Paul  

Lookign back over it the viewmodel in addperson was a relic of a different attempt to fix this so I removed it.

Setting private(set) should be fine because its being set insite the view model via a function call inside of ContentView.

I have removed it for now but it did not change the behaviour, when I press "Add Person" in the add person sheet the program crashes with Thread 1: EXC_BAD_ACCESS (code=2, address=0x16f57bff8)

2      

@Paul  

The Content view model does extend content view. I have added a git repo to the main post.

2      

@Bnerd  

Ok found it... Change 01 that makes the most mess :P

Line 27 of your ContentView

//                            }
                            padding() // Remove this, it breaks all your code..

Change 02, remove the Challenge3App.swift

I tried , now it works.

2      

@Paul  

Thanks so much...

I don't even know what to think about that solution though.

2      

@Bnerd  

actually the padding() is obvious, read your code carefully, you have

Text("Person")
                                .padding()                          
                            padding()

I was surprised that even compiled..

2      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

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.