Escaping closure captures mutating 'self' parameter Swiftui Firebase

Viewed 36

I am trying to set string companyName with a value fetched from firebase. It takes too long to use .onAppear as the view loads without the company name and then after a few milliseconds it appears. I tried to use init() but I'm getting A error

Escaping closure captures mutating 'self' parameter.

How can I go round this as I need the value of companyName to be fetched before the view?

struct Home: View {

@AppStorage("currentPage") var currentPage = 1
@State public var companyName: String = ""
var db = Firestore.firestore()

init() {
    let user = Auth.auth().currentUser
    let userName = user?.email ?? ""
    let docRef = db.collection("CONTACT").document(userName)
    var buffer = ""

    docRef.getDocument { (document, error) in
        if let document = document, document.exists {
            //Setting Values
            let data = document.data()
            
            _companyName = State(initialValue: data?["companyName"] as? String ?? "")
        } else {
            print("Document does not exist")
        }
    }
}
1 Answers

To have a clean architecture app, you can do something like this.

Create a HomeViewModel - this class will handle the API calls.

Home - will just handle all UI matters

In HomeViewModel class

import SwiftUI
import Combine

class HomeViewModel: ObservableObject, Identifiable {
     @Published var companyName: String = ""
     private var db = Firestore.firestore()
    
     init() {
        let user = Auth.auth().currentUser
        let userName = user?.email ?? ""
        let docRef = db.collection("CONTACT").document(userName)
 

        docRef.getDocument { (document, error) in
            if let document = document, document.exists {
                //Setting Values
                let data = document.data()
            
                self.companyName = data?["companyName"] as? String ?? ""
             } else {
                print("Document does not exist")
             }
        }   
     } 
}

Then in Home Struct

struct Home: View {
    @AppStorage("currentPage") var currentPage = 1
    @ObservedObject var viewModel: HomeViewModel

    var body: some View {
        NavigationView {
            List {
                Text(viewModel.companyName)
            }.navigationBarTitle(viewModel.companyName)
        }
        .navigationViewStyle(StackNavigationViewStyle())
        .background(Color.blue)
    }
}

When you call Home, you will need to inject HomeViewModel

let viewModel = HomeViewModel()
Home(viewModel: viewModel)

Or to make things easier you can do - This example below will be a quick appoarch that allows you to test. Injecting ViewModel is always recommended

struct Home: View {
    @AppStorage("currentPage") var currentPage = 1
    @ObservedObject var viewModel: HomeViewModel = HomeViewModel()

    var body: some View {
        NavigationView {
            List {
                Text(viewModel.companyName)
            }.navigationBarTitle(viewModel.companyName)
        }
        .navigationViewStyle(StackNavigationViewStyle())
        .background(Color.blue)
    }
}

Related