Home > front end >  Swift Firebase Processing A Custom Object
Swift Firebase Processing A Custom Object

Time:07-01

I am trying to store a struct called 'UnlockingCharacters' in the users document on firebase. I have a struct called 'Character'. When a user taps "unlock" on a character, the 'Character' is added to 'UnlockingCharacters'. I need to store this on firebase in the users document but am struggling to do this.

I have managed to add a 'Character' to 'UnlockingCharacters' and display them in the users profile however it is not stored in firebase so when the app is closed, the 'Character' is no longer in 'UnlockingCharacters'

Here are my structs & classes:

struct Character: Identifiable, Codable {
    
@DocumentID var id: String?
    var character_name: String
    var character_type: String
    var character_image: String
    var character_details: String
    var character_usersUnlocking: Int
    var character_totalPoints: Int
    var user: UserModel?
    var didUnlock: Bool? = false
      
    // To identify whether it is being unlocked...
    var isUnlocking: Bool = false
    
}


struct UnlockingCharacters: Identifiable, Codable {
    
    var id = UUID().uuidString
    var character: Character
}


class SharedDataModel: ObservableObject {
   
    // Unlocking Characters...
    @Published var unlockingCharacters: [Character] = [] 
}

My functions:

func isUnlocked() -> Bool {
        
        return sharedData.unlockingCharacters.contains { characterData in
            return self.characterData.id == characterData.id
        }
    }

    func addToUnlocking() {

        if let index = sharedData.unlockingCharacters.firstIndex(where: {

            characterData in
            return self.characterData.id == characterData.id
        }){
            // Remove from unlocking...
            sharedData.unlockingCharacters.remove(at: index)
        }
        else {
            // Add to unlocking...
            sharedData.unlockingCharacters.append(characterData)
        }
    }

And my UserModel:

struct UserModel: Identifiable, Codable {
    var username : String
    var pic : String
    var bio: String
    var uid : String
    var id: String { uid }  

    var activeUnlockingCharacters: [UnlockingCharacters]
}

When trying to process the custom object I get errors:

let ref = Firestore.firestore()

func fetchUser(uid: String,completion: @escaping (UserModel) -> ()){
    
    let db = Firestore.firestore()
    
    ref.collection("Users").document(uid).getDocument { (doc, err) in
        guard let user = doc else{return}
        
        let username = user.data()?["username"] as? String ?? "No Username"
        let pic = user.data()?["imageurl"] as? String ?? "No image URL"
        let bio = user.data()?["bio"] as? String ?? "No bio"
        let uid = user.data()?["uid"] as? String ?? ""
 
        do {
            try db.collection("Users").document("\(uid)").setData(from: UnlockingCharacters)
        } catch let error {
            print("Error writing object to Firestore: \(error)")
        }
        
        DispatchQueue.main.async {
            completion(UserModel(username: username, pic: pic, bio: bio, uid: uid, activeUnlockingCharacters: UnlockingCharacters))
        }
    }
}

I also get errors in the following line inside my ProfileViewModel:

@Published var userInfo = UserModel(username: "", pic: "", bio: "", uid: "", activeSupportingCharities: [SupportingCharities])

The errors:

Missing argument for parameter 'activeUnlockingCharacters' in call

Cannot convert value of type '[UnlockingCharacters].Type' to expected argument type '[UnlockingCharacters]'

Here is my data structure in the firebase console:

Character Data

User Data

I want there to be a field called UnlockingCharacters in the users data model on firebase when a character is added to the UnlockingCharacters struct.

CodePudding user response:

I think the issue is that your code for writing back to the User document doesn't refer to an instance of UnlockingCharacters , but instead to the type UnlockingCharacters.

So this line:

try db.collection("Users").document("\(uid)").setData(from: UnlockingCharacters)

should probably(*) become

let userModel = UserModel(username: username, pic: pic, bio: bio, uid: uid, activeUnlockingCharacters: unlockedCharacters)
try db.collection("Users").document("\(uid)").setData(from: userModel)

*: probably, because I wasn't sure about your data structure. You might want to post a screenshot of your Firestore data model (in the console) to make it easier to understand how you're intending to store this data.

Also, two other notes:

  1. You probably want to use Codable to replace the manual mapping (let username = user.data()?["username"] as? String ?? "No Username" etc.)
  2. no need to wrap the UI update in DispatchQueue.main.async - Firestore calls back on the main thread already - see https://twitter.com/peterfriese/status/1489683949014196226 .

CodePudding user response:

Instead of using UnlockingCharacters as the data (that is a type) you need to use unlockingCharacters (Value) from the SharedDataModel

  • Related