Home > Blockchain >  Function with a parameter inside init() not updating onAppear{}
Function with a parameter inside init() not updating onAppear{}

Time:02-05

Attempting to call a function within init(). I have a function that takes a parameter which I call in the initializer. I set the parameter to a blank string at first until I pass in the value in another view when it appears.

My issue is the function isn't updating immediately when the view first appears. My objective is to just have the function run immediately once the view is generated and have the view update immediately (where I display in my view values from the function).

My guess is since I'm passing in the initial blank string during the init(), my function isn't firing with the updated variable. I don't want to set the @State to @Binding as I don't want to have to pass in a value everytime I call the observedObject. Any advice is greatly appreciated.

To summarize my issue, when I call the grabItems recipe when my view first appears, it doesn't initially get called with the correct parameter (it gets called with a blank string).

class testClass: ObservableObject {
    @State var uidNonUser: String
    @Published var itemsNonUser = [Item]() // items saved
    init(){
        self.uidNonUser = ""
        grabItems(userUID: uidNonUser) // << grabs things
    }
} 

struct testStruct: View {
  @ObservedObject var rm = testClass()
  @State var userUID: String // << variable passing to grabItems

 var body: some View {
 Text(rm.itemsNonUser.count)
   .onAppear{
      rm.grabItems(userUID: userUID)
     }
  }
}

FYI - pasting my actual grabItems recipe below just as reference in case it helps understand the issue.

func grabItems(userUID: String){
       //grab user thats not current user
        
        FirebaseManager.shared.firestore
            .collection("users")
            .document(userUID) // << passed in from view
            .collection("userRecipes")
            .addSnapshotListener { (snapshot, err) in
                guard let documents = snapshot?.documents else{
                    print("no documents present")
                    return
                }

                self.itemsNonUser = documents.map { (querySnapshot) -> RecipeItem in
                    let data = querySnapshot.data()
                    let recipeTitle = data ["recipeTitle"] as? String ?? ""
                    let recipePrepTime = data ["recipePrepTime"] as? String ?? ""
                    let recipeImage = data ["recipeImage"] as? String ?? ""
                    let createdAt = data ["createdAt"] as? String ?? ""
                    let ingredients = data ["ingredientItem"] as? [String: String] ?? ["": ""]
                    let directions = data ["directions"] as? [String] ?? [""]
                    let recipeID = data ["recipeID"] as? String ?? ""
                    let recipeCaloriesMacro = data ["recipeCaloriesMacro"] as? Int ?? 0
                    let recipeFatMacro = data ["recipeFatMacro"] as? Int ?? 0
                    let recipeCarbMacro = data ["recipeCarbMacro"] as? Int ?? 0
                    let recipeProteinMacro = data ["recipeProteinMacro"] as? Int ?? 0
                    let recipe = RecipeItem(id: recipeID, recipeTitle:recipeTitle , recipePrepTime: recipePrepTime, recipeImage: recipeImage, createdAt: createdAt, recipeCaloriesMacro: recipeCaloriesMacro, recipeFatMacro: recipeFatMacro, recipeCarbMacro:recipeCarbMacro, recipeProteinMacro: recipeProteinMacro, directions: directions, ingredientItem: ingredients)

                    return recipe

                }
            }
        }

CodePudding user response:

Try this example code to update the View when it first appears.

// for testing
struct RecipeItem {
    var recipeTitle: String
    // ...
}

class TestClass: ObservableObject {
    @Published var itemsNonUser = [RecipeItem]()  // <-- here
    
    func grabItems(userUID: String){
        //...
        // for testing
        itemsNonUser = [RecipeItem(recipeTitle: "banana cake with ID: \(userUID)")]
    }
    
}

struct TestStruct: View {
    @StateObject var rm = TestClass()  // <-- here
    @State var userUID: String
    
    var body: some View {
        VStack {
            Text(rm.itemsNonUser.first?.recipeTitle ?? "no name")
            Text("\(rm.itemsNonUser.count)")  // <-- here
        }
        .onAppear{
            rm.grabItems(userUID: userUID) // <-- here
        }
    }
}

struct ContentView: View {
    let userUID = "123456"
    var body: some View {
        TestStruct(userUID: userUID) // <-- here
    }
}

CodePudding user response:

In my instance (and could have been specific for my particular case) I updated the grabItems recipe to return an Int since my view was trying to display the count.

I then just called it directly into the view like so and it worked:

 Text(String(rm.grabItems(userUID: userUID))).bold() // << calling function directly in view
  • Related