简体   繁体   中英

How Do I Add Dynamic Sub Collection Data In Firestore In SwiftUI?

I'm new to swift and firebase and am trying lots of things out. I have created a collection of "characters" in firebase which are displayed in the app. The aim is there are "characters" you unlock by earning points. Points are earned by walking (getting steps using HealthKit)

When a user taps on a character and then taps "unlock" its added to a struct called UnlockingCharacters.

UnlockingCharacters are shown in the users profile.

I need to make the UnlockingCharacters struct part of the users document on Firebase (maybe as a subcollection?)

I want to limit the number of characters in UnlockingCharacters to 2. Characters are unlocked by taking steps which are turned into points. When points are uploaded to firebase, the character receives these points and adds it to its total (a characters points total is the sum of all users that are unlocking the characters points).

Here is the Character Model:

struct Character: Identifiable, Decodable {
    
@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
    
}

Here is the UnlockingCharacters model:

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

SharedDataModel:

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)
        }
    }

I know that something needs to be added to my UserModel too however I am getting an error:

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

//I am getting error for the following variable:
(Value type 'UserModel' cannot have a stored property that recursively contains it)

    var activeUnlockingCharacters: UnlockingCharacters
}

To sum up I need UnlockingCharacters to be added to the users document on Firebase.

I'm getting an error processing a custom object. Here is my code:

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))
        }
    }
}

The error in the do is: "Type 'UnlockingCharacters.Type' cannot conform to 'Encodable'"

The error in the DispatchQueue is: "Cannot convert value of type 'UnlockingCharacters.Type' to expected argument type '[UnlockingCharacters]'"

Get rid of var user: UserModel? in Character

Then you can find the characters under activeUnlockingCharacters

or something like

UserModel/{uid}/UnlockingCharacters

You can make

var activeUnlockingCharacters: UnlockingCharacters

An Array

var activeUnlockingCharacters: [UnlockingCharacters]

Then use validation or rules to limit the count to 2

Firebase has a whole section on how to process custom objects.

https://firebase.google.com/docs/firestore/manage-data/add-data#custom_objects

if you have all your structs conform to Codable

do {
    try db.collection("UserModel").document("\(uid)").setData(from: userModelObject)
} catch let error {
    print("Error writing object to Firestore: \(error)")
}

And to read

https://firebase.google.com/docs/firestore/query-data/get-data

let docRef = db.collection("UserModel").document("\(uid)")

docRef.getDocument(as: UserModel.self) { result in
    // The Result type encapsulates deserialization errors or
    // successful deserialization, and can be handled as follows:
    //
    //      Result
    //        /\
    //   Error  UserModel
    switch result {
    case .success(let userModel):
        // A `Model` value was successfully initialized from the DocumentSnapshot.
        print("Model: \(userModel)")
    case .failure(let error):
        // A `Model` value could not be initialized from the DocumentSnapshot.
        print("Error decoding object: \(error)")
    }
}

You can also user the new @FirestoreQuery and use rules to limit the object that is visible

https://firebase.google.com/docs/reference/swift/firebasefirestoreswift/api/reference/Structs/FirestoreQuery

Set in the rules that the user is the only one that can read its own UserModel based on auth.

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