2

I have an observable object that loads a list of banks from an onAppear action on a view. When a change is made I exit the view and then come back to the view which calls loadBankMembers but those updates are not reflected in the UI. If I exit and come back then the updates are reflected.

import SwiftUI

struct BankMembersView: View {
    @EnvironmentObject var userData: UserData

    var body: some View {
        ZStack {
            if userData.showPlaidModal {
                PlaidView()
            }

            List {
                ForEach(userData.bankMembers) { bankMember in
                    BankMemberRowView(bankMember: bankMember)
                }
            }
        }
        .onAppear(perform: { self.userData.loadBankMembers() })
        .navigationBarTitle("Bank Accounts")
        .navigationBarItems(trailing:
            Button(action: { self.userData.showPlaidModal = true }) {
                Text("Add")
            }
        )
    }
}
import SwiftUI
import Combine

final class UserData: ObservableObject  {
    let objectWillChange = ObservableObjectPublisher()

    let apollo = Apollo()

    var bankMembers: [BankMember] = [] {
        willSet { self.objectWillChange.send() }
    }

    func loadBankMembers() {
        apollo.client.fetch(query: ListBankMembersQuery(), cachePolicy: .fetchIgnoringCacheCompletely) { result in
            guard let data = try? result.get().data else { return }
            var bankMembers: [BankMember] = []

            for bankMemberData in data.bankMembers ?? [] {
                var bankAccounts: [BankAccount] = []

                for bankAccountData in bankMemberData?.bankAccounts ?? [] {
                    if let bankAccountid = bankAccountData?.id {
                        let bankAccount = BankAccount(id: bankAccountid, name: bankAccountData?.name ?? "", sync: bankAccountData?.sync ?? false)
                        bankAccounts.append(bankAccount)
                    }
                }

                if let id = bankMemberData?.id {
                    let bankMember = BankMember(id: id, name: bankMemberData?.name ?? "", status: bankMemberData?.status, bankAccounts: bankAccounts)
                    bankMembers.append(bankMember)
                }
            }

            self.bankMembers = bankMembers
        }
    }
}

I tried setting UserData.banksMembers = [] at the beginning of load but then I get a crash with this error:

2019-11-07 20:51:58.191689-0700 Spendable[5839:255244] [TableView] Warning once only: UITableView was told to layout its visible cells and other contents without being in the view hierarchy (the table view or one of its superviews has not been added to a window). This may cause bugs by forcing views inside the table view to load and perform layout without accurate information (e.g. table view bounds, trait collection, layout margins, safe area insets, etc), and will also cause unnecessary performance overhead due to extra layout passes. Make a symbolic breakpoint at UITableViewAlertForLayoutOutsideViewHierarchy to catch this in the debugger and see what caused this to occur, so you can avoid this action altogether if possible, or defer it until the table view has been added to a window. Table view: <_TtC7SwiftUIP33_BFB370BA5F1BADDC9D83021565761A4925UpdateCoalescingTableView: 0x7fe05689fa00; baseClass = UITableView; frame = (0 0; 0 0); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x600000813990>; layer = <CALayer: 0x60000064be20>; contentOffset: {0, 0}; contentSize: {0, 0}; adjustedContentInset: {0, 0, 0, 0}; dataSource: <_TtGC7SwiftUIP13$7fff2c6b223419ListCoreCoordinatorGVS_20SystemListDataSourceOs5Never_GOS_19SelectionManagerBoxS2___: 0x7fe0564235c0>>
2019-11-07 20:51:58.191948-0700 Spendable[5839:255244] *** Assertion failure in -[_TtC7SwiftUIP33_BFB370BA5F1BADDC9D83021565761A4925UpdateCoalescingTableView _endCellAnimationsWithContext:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKitCore_Sim/UIKit-3900.12.15/UITableView.m:2039
2019-11-07 20:51:58.198956-0700 Spendable[5839:255244] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to delete section 0, but there are only 0 sections before the update'

1 Answer 1

2

You missed @Published for var bankMembers. Let me show simple example (cannot use your code, so wrote something similar):

final class UserData: ObservableObject  {

    @Published var bankMembers: [String] = []

    func loadBankMembers() {
        bankMembers.append("Steve")
        bankMembers.append("Bob")
    }

    func updateBankMembers() {
        bankMembers.append("John")
        bankMembers.append("Ann")
    }
}
struct BankMembersView: View {
    @EnvironmentObject var userData: UserData

    var body: some View {

        NavigationView {
            List {
                ForEach(userData.bankMembers, id: \.self) { bankMember in
                    Text(bankMember)
                }
            }
            .onAppear(perform: { self.userData.loadBankMembers() })
            .navigationBarTitle("Bank Accounts")
            .navigationBarItems(trailing:
                Button(action: { self.userData.updateBankMembers() }) {
                    Text("Add")
                }
            )
        }
    }
}

and at the beginning you'll have: enter image description here

after button "Add" pressed: enter image description here

P.S. find good article for your situation for more information

Sign up to request clarification or add additional context in comments.

4 Comments

when I do that, nothing shows up at all the first time, and I still have the problem. It seems like the tutorials said to use this which is what I am doing willSet { self.objectWillChange.send() }
I think the difference comes from the fact that you are updating bankMembers with the view open. Where my updates both happen onAppear
@MichaelStClair, checked your code more attentively and now understand, that give the most easy variant, but you have something specific.. now trying to understand the mistake
@MichaelStClair, tried to change code with ObservableObjectPublisher() and willSet { self.objectWillChange.send() }, everything work again. There is one idea: try to change for bankMemberData in data.bankMembers ?? [] {loop for filling some hardcode data (just 1-2 members) and check does UI reflects these changes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.