Problems Updating a View

Hey everyone! While working on an app which reads the Unique Identifiers of NFC tags I ran into a problem. When I run the code the phone is able to read the tag and add the UID into an array (I checked this by making it print the array whenever the array is updated) but I am unable to make the View update. The reason this is tricky is that when you press a button on the view, it activates the reader function reader.activate() which then updates the array in the Data class only if it successfully extracted the UID. How would I make the View reload every time a new UID is added to the array? Below is the code for the content view, Data class and then the NFC reader. Thanks! View:




struct ContentView: View {

@StateObject var data = Data()

let reader : NFCReader = NFCReader()

    var body: some View {

        NavigationView {

            List {

                Section(

                    header: Text("\(data.itemCount)" + " Items")

                ) { ForEach(data.products, id: \.self) {

                    product in Text(product)

                }

                .onDelete(perform: data.deleteItem)

                .onMove(perform: data.moveItem)

            }

            }

            .navigationTitle("My Products")

            .navigationBarItems(leading: EditButton(), trailing: AddButton)

            .navigationViewStyle(.stack)

        }

        .environmentObject(data)

}

    var AddButton: some View {

        Button(action: {

            reader.activate()

// Next, once reader.activate() is done and sends data the tagUID, i need to open a new view with text imputs for name and description of product.

        }, label: {

            Image(systemName: "iphone.radiowaves.left.and.right")

        })

    }

}

struct ContentView_Previews: PreviewProvider {

    static var previews: some View {

        ContentView()

//        Do I need EnvironmentObject here???

            .environmentObject(Data())

    }

}

Data class:


//import Foundation

class Data: ObservableObject {

var tagCode = "UID"

@Published var products: [String] = []

@Published var itemCount = 0

    

    func addItem() {

        products.append(tagCode)

        itemCount = itemCount + 1

        print(tagCode + " Works!")

        print(products)

    }

    func deleteItem(indexSet : IndexSet) {

        products.remove(atOffsets: indexSet)

        itemCount = itemCount - 1

    }

    func moveItem(indices : IndexSet, newOffset : Int) {

        products.move(fromOffsets: indices, toOffset: newOffset)

}

}

NFC Reader:

import Foundation

import CoreNFC 

import SwiftUI



class NFCReader: NSObject, ObservableObject, NFCTagReaderSessionDelegate {

    var data : Data = Data()

private var tagUID = "UID"

private var session: NFCTagReaderSession?

func activate() {

    self.session = NFCTagReaderSession(pollingOption: .iso14443, delegate: self)

    self.session?.alertMessage = "Hold Your Phone Near NFC Tag"

    self.session?.begin()

    }

func tagReaderSessionDidBecomeActive(_ session: NFCTagReaderSession) {

    print("Session Begun!")

}

func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) {

    print("Error with Launching Session")

}

func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {

 print("Connecting To Tag")

    if tags.count > 1{

        session.alertMessage = "More Than One Tag Detected, Please try again"

        session.invalidate()

    }

    let tag = tags.first!

    session.connect(to: tag) { [self] (error) in

        if nil != error{

            session.invalidate(errorMessage: "Connection Failed")

        }

        if case let .miFare(sTag) = tag{

            let UID = sTag.identifier.map{ String(format: "%.2hhx", $0)}.joined()

            print("UID:", UID)

            print(sTag.identifier)

            session.alertMessage = "UID Captured"

            session.invalidate()

            DispatchQueue.main.async {

                self.tagUID = "\(UID)"

                data.tagCode = "\(UID)"

                data.addItem()

//                add inputview change here

                

            }

            

        }

        

    }



}

   

}

In NFCReader, the "data" that you are adding the new item to is not the same as the "data" in your ContentView.
They are two separate objects.
So the addition will never show up in your UI.

You need to use a single "data", and pass it to wherever it is needed.

For example...
In ContentView, you could remove data, make NFCReader the StateObject, publish NFCReader's data, and refer to that data in your View.

I tried that and it still does not seem to work. It is as if ContentView only takes a snapshot of the Data class when the app boots up and that's it. I there a function I could create to update the view once something is added to the array? ContentView just doesn't know when Data is updated.

I tried that and it still does not seem to work

Can you show your code?

Here is the Updated ContentView. I set the StateObject to be the reader and routed the data class instructions through the reader class by adding reader. in from of data. Am I interpreting what you said wrong?




struct ContentView: View {

//@StateObject var data = Data()

@StateObject var reader : NFCReader = NFCReader()

    var body: some View {

        NavigationView {

            List {

                Section(

                    header: Text("\(reader.data.itemCount)" + " Items")

                ) { ForEach(reader.data.products, id: \.self) {

                    product in Text(product)

                }

                .onDelete(perform: reader.data.deleteItem)

                .onMove(perform: reader.data.moveItem)

            }

            }

            .navigationTitle("My Products")

            .navigationBarItems(leading: EditButton(), trailing: AddButton)

            .navigationViewStyle(.stack)

        }

        .environmentObject(reader.data)

}

    var AddButton: some View {

        Button(action: {

            reader.activate()

//            data.addItem()

// Next, once reader.activate() is done and sends data the tagUID, i need to open a new view with text imputs for name and description of product.

        }, label: {

            Image(systemName: "iphone.radiowaves.left.and.right")

        })

    }

}

struct ContentView_Previews: PreviewProvider {

    static var previews: some View {

        ContentView()

//        Do I need EnvironmentObject here???

            .environmentObject(Data())

    }

}

As a test, in NFCReader, after addItem(), try forcing the update by adding:

objectWillChange.send()

That (kinda) Worked!

I think it did work, but you now move on to different questions!

Also, why can't I just put objectWillChange.send() in the Data class?

That will send objectWillChange for the data object, not for the NFCReader object (which is what ContentView is observing).

Problems Updating a View
 
 
Q