-1

I have a custom struct called myObjectHolder and it has an array of a custom struct (called myObject) called myArray.

I try to fetch the data I store in Firebase-Firesrtore and to append it to the array (using a function that converts the Firebase document to myObject).

I try to do it like this:

struct myObjectHolder {
    var myArray = [myObject]()


    private func fetchMyObjects() {
        let db = Firestore.firestore().collection("myObjects").getDocuments { (querySnapshot, err) in
        for document in querySnapshot!.documents {
            self.myArray.append(self.myObjectFromDocument(document: document)) //Error
        }
    }
}

For some reason, when I try to append the new myObject to myArray, I receive this error message:

Cannot use mutating member on immutable value: 'self' is immutable

Does anybody know how can I resolve it? (I am using SwiftUI it matters)

Thank you!

Asperi
  • 228,894
  • 20
  • 464
  • 690
F.SO7
  • 695
  • 1
  • 13
  • 37

3 Answers3

0

Use class instead of struct is one solution - and if you think struct suits your design then use

struct myObjectHolder {
    var myArray = [myObject]()


    private mutating func fetchMyObjects() {
        let db = Firestore.firestore().collection("myObjects").getDocuments { (querySnapshot, err) in
        for document in querySnapshot!.documents {
            self.myArray.append(self.myObjectFromDocument(document: document)) //Error
        }
    }
}
Peter Friese
  • 6,709
  • 31
  • 43
Jawad Ali
  • 13,556
  • 3
  • 32
  • 49
0

What other have said before is correct: you cannot modify a struct, hence you should use a class - or make the struct modifiable. For more details, check out https://chris.eidhof.nl/post/structs-and-mutation-in-swift/

Since you've mentioned Firestore specifically, let me suggest you use an MVVM architecture:

Put your data access logic into a view model:

import Foundation
import FirebaseFirestore
import FirebaseFirestoreSwift

class ThingViewModel: ObservableObject {
  @Published var things = [Thing]()

  private var db = Firestore.firestore()

  func fetchData() {
    db.collection("things").addSnapshotListener { (querySnapshot, error) in
      guard let documents = querySnapshot?.documents else {
        print("No documents")
        return
      }

      self.things = documents.map { queryDocumentSnapshot -> Thing in
        return queryDocumentSnapshot.data(as: Thing.self)
      }
    }
  }
}

In your view, instantiate the view model and observe the published things array:

struct ThingsListView: View {
  @ObservedObject var viewModel = ThingsViewModel()

  var body: some View {
    NavigationView {
      List(viewModel.things) { thing in
        VStack(alignment: .leading) {
          Text(thing.name)
            .font(.headline)
        }
      }
      .navigationBarTitle("Things")
      .onAppear() {
        self.viewModel.fetchData()
      }
    }
  }
}

Also, it's good practice to use UpperCamelCase for Swift structs and classes: https://github.com/raywenderlich/swift-style-guide#naming

Peter Friese
  • 6,709
  • 31
  • 43
-2

I've just changed it from struct to class and it worked perfectly.

F.SO7
  • 695
  • 1
  • 13
  • 37