简体   繁体   中英

Escaping closure captures mutating 'self' parameter, Firebase

I have the following code, How can i accomplish this without changing struct into class. Escaping closure captures mutating 'self' parameter,

struct RegisterView:View {
    var names = [String]()

    private func LoadPerson(){
        FirebaseManager.fetchNames(success:{(person) in
        guard let name = person.name else {return}
        self.names = name //here is the error
    }){(error) in
        print("Error: \(error)")
    }

    init(){
        LoadPerson()
    }a
    
    var body:some View{
        //ui code
    }
}

Firebasemanager.swift

struct FirebaseManager {
    
    func fetchPerson(
        success: @escaping (Person) -> (),
        failure: @escaping (String) -> ()
    ) {
        Database.database().reference().child("Person")
        .observe(.value, with: { (snapshot) in
            if let dictionary = snapshot.value as? [String: Any] {
                success(Person(dictionary: dictionary))
            }
        }) { (error) in
            failure(error.localizedDescription)
        }
    }
}

SwiftUI view can be created (recreated) / copied many times during rendering cycle, so View.init is not appropriate place to load some external data. Use instead dedicated view model class and load explicitly only when needed.

Like

class RegisterViewModel: ObservableObject {
    @Published var names = [String]()

    func loadPerson() {
// probably it also worth checking if person has already loaded
//      guard names.isEmpty else { return }

        FirebaseManager.fetchNames(success:{(person) in
        guard let name = person.name else {return}
        DispatchQueue.main.async {
           self.names = [name]
        }
    }){(error) in
        print("Error: \(error)")
    }
}

struct RegisterView: View {

    // in SwiftUI 1.0 it is better to inject view model from outside
    // to avoid possible recreation of vm just on parent view refresh
    @ObservedObject var vm: RegisterViewModel

//    @StateObject var vm = RegisterViewModel()   // << only SwiftUI 2.0

    var body:some View{
       Some_Sub_View()
         .onAppear {
            self.vm.loadPerson()
         }
    }
}

Make the names property @State variable.

struct RegisterView: View {
    @State var names = [String]()

    private func LoadPerson(){
        FirebaseManager.fetchNames(success: { person in
            guard let name = person.name else { return }
            DispatchQueue.main.async {
                self.names = [name]
            }
        }){(error) in
            print("Error: \(error)")
        }
    }
    //...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM