Search code examples
swiftuiios14

How to Transmit a View Entry Count to a Class Method


I'm having trouble with usage of a count of the number of entries in a view. I especially need to know when there are no entries in the view. I have placed debug code in the view below and the view count currants.curItem.countis updating as expected. The count status in checkForUpdates() doesn't follow the view above.

If I recall correctly I should be using @EnvironmentObject or @ObservedObject only in a view. I really need some kind of global variable that I can pass to the method checkForUpdates. It is crashing when count in checkForUpdates() is nonzero when in the view it is actually zero. It also crashes in checkForUpdates() with the error Fatal error: No ObservableObject of type Currencies found. A View.environmentObject(_:) for Currencies may be missing as an ancestor of this view.

struct manCurView: View {
    
    @EnvironmentObject var currants: Currants
    
    var body: some View {
        List {       
            ForEach(currants.curItem, id: \.id) { item in
                HStack {
                    Text(item.curCode)
                        .frame(width: 100, alignment: .center)
                    
                    Text(item.cunName)
                }
                .font(.subheadline)
            }
            .onDelete(perform: removeItems)
        }
        .navigationBarTitle(Text("Manage Working Blocks"), displayMode: .inline)
        HStack {
            NavigationLink(destination: addCurView()) {Text("Add Working Blocks").fontWeight(.bold)}
                .font(.title2)
                .disabled(currants.curItem.count > 7)

Here is how the data is stored for the view above

struct CurItem: Codable, Identifiable {
    var id = UUID()
    var cunName: String 
    var curName: String
    var curCode: String
    var curSymbol: String 
    var curRate: Double
}

class Currants: ObservableObject {
    
    @Published var curItem: [CurItem]   
}

And here is the class and method where I would like to use count from the view manCurView

class BlockStatus:  ObservableObject {
    
    @EnvironmentObject var globalCur : Currants
    @ObservedObject var netStatus : TestNetStatus = TestNetStatus()
    
    func checkForUpdates() -> (Bool) {
        
        if netStatus.connected == true {
            if globalCur.curItem.count > 0 { 

Solution

  • Without a minimal reproducible example it is very difficult to give you exact code but you can try something like the code below in your manCurView

    @StateObject var blockStatus: BlockStatus = BlockStatus()
    
    .onChange(of: currants.curItem.count, perform: { value in
                print("send value from here")
                blockStatus.arrayCount = value
            })
    

    And adding the code below to BlockStatus

    @Published var arrayCount: Int = 0{
        didSet{
            //Call your method here
        }
    }
    

    Look at the code below.

    import SwiftUI
    import Combine
    struct CurItem: Codable, Identifiable {
        var id = UUID()
        
    }
    class Currants: ObservableObject {
        
        @Published var curItem: [CurItem] = [CurItem(), CurItem(), CurItem(), CurItem()]
    }
    class TestNetStatus: ObservableObject {
        static let sharedInstance = TestNetStatus()
        @Published var connected: Bool = false
        
        init() {
            //Simulate changes in connection
            Timer.scheduledTimer(withTimeInterval: 10, repeats: true){ timer in
                self.connected.toggle()
            }
        }
    }
    class BlockStatus:  ObservableObject {
        @Published var arrayCount: Int = 0{
            didSet{
                checkForUpdates()
            }
        }
        @Published var checkedForUpdates: Bool = false
        var netStatus : TestNetStatus = TestNetStatus.sharedInstance
        //private var cancellable: AnyCancellable?
        init() {
            //Maybe? if you want to check upon init.
            //checkForUpdates()
            
            //Something like the code below is also possible but with 2 observed objects the other variable could be outdated
            
            //        cancellable = netStatus.objectWillChange.sink { [weak self] in
            //            self?.checkForUpdates()
            //        }
        }
        
        func checkForUpdates() {
            if netStatus.connected == true {
                if arrayCount > 0 {
                    checkedForUpdates = true
                }else{
                    checkedForUpdates = false
                }
            }else{
                checkedForUpdates = false
            }
        }
    }
    struct ManCurView: View {
        @StateObject var currants: Currants = Currants()
        @StateObject var blockStatus: BlockStatus = BlockStatus()
        @StateObject var testNetStatus: TestNetStatus = TestNetStatus.sharedInstance
        var body: some View {
            List {
                Text("checkedForUpdates = " + blockStatus.checkedForUpdates.description).foregroundColor(blockStatus.checkedForUpdates ? Color.green : Color.red)
                Text("connected = " + blockStatus.netStatus.connected.description).foregroundColor(blockStatus.netStatus.connected ? Color.green : Color.red)
                
                ForEach(currants.curItem, id: \.id) { item in
                    HStack {
                        Text(item.id.uuidString)
                            .frame(width: 100, alignment: .center)
                        
                        Text(item.id.uuidString)
                    }
                    .font(.subheadline)
                }
                //Replaced with toolbar button for sample
                //.onDelete(perform: removeItems)
                //When the array count changes
                .onChange(of: currants.curItem.count, perform: { value in
                    blockStatus.arrayCount = value
                })
                //Check when the networkStatus changes
                .onChange(of: testNetStatus.connected, perform: { value in
                    //Check arrayCount
                    if blockStatus.arrayCount != currants.curItem.count{
                        blockStatus.arrayCount = currants.curItem.count
                    }else{
                        blockStatus.checkForUpdates()
                    }
                })
                
            }
            .navigationBarTitle(Text("Manage Working Blocks"), displayMode: .inline)
            //Replaced addCurView call with toolbar button for sample
            .toolbar(content: {
                ToolbarItem(placement: .navigationBarTrailing, content: {
                    Button("add-currant", action: {
                        currants.curItem.append(CurItem())
                    })
                })
                ToolbarItem(placement: .navigationBarLeading, content: {
                    Button("delete-currant", action: {
                        if currants.curItem.count > 0{
                            currants.curItem.removeFirst()
                        }
                    })
                })
            })
        }
    }