In Firestore how to make Data structure and how to query them

Viewed 54

Now I'm developing the app in Firestore which can share photos in group. Teach me how to make data structure. I know only the data structure by using users and photos.

  • Photos

    • photo-1
      • owner_id
      • url
    • photo-2
      • owner_id
      • url
  • Users

    • user-1
      • user_id
      • user_name
    • user-2
      • user_id
      • user_name

When I want to query photo which match userId

Firestore.firestore().collection("Photos")
    .whereField("owner_id", isEqualTo: userId)

these above can show us.

I have questions. If I want to add groups who can see shared images, How should I make data the structure and the query. This function I want is like Google docs. It can share only users permitted. So I think that is used by group function.

Please teach me the data structure and query.

1 Answers

The Photo document can have an array of allowed user ids (or users). The following approach worked in my test environment. Within the PhotosViewModel, we have got a fetchPermittedPhotos function, it handles that the current user just gets the photos he got permission to see.

Models

User

In this example we take the uid from the current user which has been logged in by the firebase authentication (Auth.auth().currentUser!.uid). The getCurrentUser function searches for a document with a specific document id, here it is using again the uid of the current user. That means, it is necessary that a (new) users id is identical to the uid. It is similar to the permission handling when fetching photos. The array contains a list of uids which get taken from Auth.auth().currentUser!.uid. If you don't want that, you have to adjust your code accordingly.

struct User: Codable, Identifiable, Hashable {

    var id: String?
    var name: String

    init(name: String, id: String?) {
        self.id = id
        self.name = name
    }

    init?(document: DocumentSnapshot) {
        let data = document.data()
        let name = data!["name"] as? String ?? ""

        id = document.documentID
        self.name = name
    }

    enum CodingKeys: String, CodingKey {
        case id
        case name
    }
}

extension User: Comparable {

static func == (lhs: User, rhs: User) -> Bool {
    return lhs.id == rhs.id
}

static func < (lhs: User, rhs: User) -> Bool {
    return lhs.name < rhs.name
}

}

Photo

struct Photo: Codable, Identifiable, Hashable {
    
    var id: String?
    var ownerUid: String
    var imageUrl: String
    var permittedUids: [String]
    
    init(ownerUid: String, imageUrl: String, permittedUids: [String], id: String = UUID().uuidString) {
        self.id = id
        self.ownerUid = ownerUid
        self.imageUrl = imageUrl
        self.permittedUids = permittedUids
    }
    
    init?(document: DocumentSnapshot) {
        let data = document.data()
        let ownerUid = data!["ownerUid"] as? String ?? ""
        let imageUrl = data!["imageUrl"] as? String ?? ""
        let permittedUids = data!["permittedUids"] as? [String] ?? []
        
        id = document.documentID
        self.ownerUid = ownerUid
        self.imageUrl = imageUrl
        self.permittedUids = permittedUids
    }
    
    enum CodingKeys: String, CodingKey {
        case id
        case ownerUid
        case imageUrl
        case permittedUids
    }
}

extension Photo: Comparable {
    
    static func == (lhs: Photo, rhs: Photo) -> Bool {
        return lhs.id == rhs.id
    }
    
    static func < (lhs: Photo, rhs: Photo) -> Bool {
        return lhs.ownerUid < rhs.ownerUid
    }
}

ViewModels

UsersViewModel

class UsersViewModel: ObservableObject {
    
    let db = Firestore.firestore()
    
    @Published var user: User?
    
    func fetchCurrentUser(_ completion: @escaping (Bool, String) ->Void) {
        guard let uid = Auth.auth().currentUser?.uid else {
            completion(false, "Could not find firebase uid")
            return
        }
        
        let docRef = self.db.collection("users").document(uid)
        docRef.getDocument { (document, error) in
            if let document = document, document.exists {
                self.user = User(document: document)
                completion(true, "User set up from Firestore db")
            } else {
                completion(false, "User document not found")
            }
        }
    }
}

PhotosViewModel

class PhotosViewModel: ObservableObject {
    
    let db = Firestore.firestore()
    let storage = Storage.storage()
    
    @Published var allphotos = [Photo]()
    @Published var permittedphotos = [Photo]()
    
    func addNewPhoto(photo: Photo, completion: @escaping (Bool, String)->Void) {
        do {
            try db.collection("photos").document(photo.id!).setData(from: photo) { _ in
                completion(true, "Photo added to Firestore")
            }
        } catch let error {
            print("Error writing photo to Firestore: \(error)")
            completion(false, "Error writing city to Firestore: \(error)")
        }
    }
    
    func fetchAllPhotos(_ completion: @escaping (Bool, String) ->Void) {
        self.allphotos.removeAll()
        db.collection("photos").addSnapshotListener { (querySnapshot, error) in
            guard let documents = querySnapshot?.documents else {
                completion(true, "No documents")
                return
            }
            
            self.allphotos = documents.map { queryDocumentSnapshot -> Photo in
                return Photo(document: queryDocumentSnapshot)!
            }
            completion(true, "Data fetched")
        }
    }
    
    func fetchPermittedPhotos(_ completion: @escaping (Bool, String) ->Void) {
        
        guard let uid = Auth.auth().currentUser?.uid else {
            completion(false, "Could not find firebase uid")
            return
        }
        
        self.permittedphotos.removeAll()
        db.collection("photos").whereField("permittedUids", arrayContains: uid).addSnapshotListener { (querySnapshot, error) in
            guard let documents = querySnapshot?.documents else {
                completion(true, "No documents")
                return
            }
            
            self.permittedphotos = documents.map { queryDocumentSnapshot -> Photo in
                return Photo(document: queryDocumentSnapshot)!
            }
            completion(true, "Data fetched")
        }
    }
}
Related