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