I have successfully installed Firebase and connected the login and registration with UID. If the user saves additional data in the app, I would now like to assign this to the respective user who is logged in, what is the best way to do this? Sorry I'm a beginner in Swift and Firebase, I need a tutorial or an explanation that is not too complex.
Thank you all
Uikit
Swift 5
Firebase
All of this is assuming you have Firebase UserAuth connected with your app and setup.
All users have a UID, user identifier, that uniquely identifies them. This is easy to get.
//there must be a user signed in
let user = Auth.auth().currentUser
let uid = user.uid
Simply put, to store data that is unique to the user, store it all under the uid using Firestore. If you do not have Firestore, get started with Firestore.
All data you save to Firestore, must be structured in a dictionary format with String as the key and Any as the value. For example, if I wanted to store the top 3 favorite flavors of ice cream for a user, you would do this *note firebase will create these documents and collections for you automatically if they do not exist so do not panic *:
//First get a reference to the database.
// It is best to have db as a global variable
let db = Firestore.firestore()
let favoriteFlavors: [String: Any] = ["numberOne":flavorOne as Any, "numberTwo":flavorTwo as Any, "numberThree": flavorThree as Any]
//access the collection of users
//access the collection of the currentUser
//create a document called favoriteFlavors
//set the document's data to the dictionary
db.collection("users").collection(uid).document("favoriteFlavors").setData(favoriteFlavors) { err in
if let err = err {
print("Error writing document: \(err)")
} else {
print("Document successfully written!")
}
}
Now, when you want to retrieve this data, you do access the users collection, the collection of the logged in user, then read the favoriteFlavors document--like this:
let docRef = db.collection("users").collection(uid).document("favoriteFlavors")
docRef.getDocument { (document, error) in
if let document = document {
print("Document received")
// retrieve the data in the document (a dictionary)
let data = document.data()
} else {
print("Document does not exist")
}
}
So if you wanted to get the number one favorite flavor, you would do this:
//Remember that data is a dictionary of String:Any
if let numberOneFlavor = data["numberOne"] as? String {
print("Number one favorite flavor ",numberOneFlavor)
}
Granted, this can get more convoluted, but this is a solid foundation of what you need to know. I advice reading the add data and get data pages of the Firestore documentation.
Related
This code shows the app's main view controller. We would like increment the currently logged in user's field value by 1. In the code below we are only able to do this by manually pasting "nDcAFLPpRuPXI9AOLkln" which we copied from fire base itself.
How do we automatically refer to the currently logged in user?
snapchot of our firestore data tree
#IBAction func bidButton(_ sender: Any) {
let updateScore = db.collection("users").document("nDcAFLPpRuPXI9AOLkln")
updateScore.updateData([
"leaderboardscore": FieldValue.increment(Int64(1))
])
db.collection("users").document("nDcAFLPpRuPXI9AOLkln")
.addSnapshotListener { documentSnapshot, error in
guard let document = documentSnapshot else {
// there was an error
print("Error fetching document: \(error!)")
return
}
// no data to show
guard let data = document.data() else {
print("Document data was empty.")
return
}
self.leaderBoardScoreLabel.text = String("Current data: \(data)")
//print("Current data: \(data)")
}
If you are using Firebase Authentication, and asking how to get the currently signed in user, you should follow the instructions in the documentation:
The recommended way to get the current user is by setting a listener on the Auth object:
handle = Auth.auth().addStateDidChangeListener { (auth, user) in
// ...
}
By using a listener, you ensure that the Auth object isn't in an
intermediate state—such as initialization—when you get the current
user.
You can also get the currently signed-in user by using the currentUser
property. If a user isn't signed in, currentUser is nil:
if Auth.auth().currentUser != nil {
// User is signed in.
// ...
} else {
// No user is signed in.
// ...
}
I would strongly suggest learning how to use the listener, which will give you a callback every time the user is seen to sign in or out.
Once you have a User object, you can use its uid property to get the string you're looking for:
let uid = user.uid
i want to get document's id's from firestore and store them in a variable to use in my next page.
how can i do this with swift? i tried this method but it generates a random id not the specific document id of the collection that i have
let docRef = Firestore.firestore().collection("Shops").document()
let docId = docRef.documentID
attached are the document id's i need to retrieve in my variable.
You can use getDocuments() to fetch all documents in one collection or use .addSnapshotListener() to automatically fetch new documents.
Firestore.firestore().collection("Shops").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
print("\(document.documentID)") // Get documentID
print("\(document.data)") // Get all data
print("\(document.data()["name"] as! String)") // Get specific data & type cast it.
}
}
}
I am currently working on a project with a multi user system. The user is able to create new profiles which are saved persistently using CoreData.
My problem is: Only one profile can be the active one at a single time, so I would like to get the ObjectID of the created profile and save it to UserDefaults.
Further I was thinking that as soon as I need the data of the active profile, I can simply get the ObjectID from UserDefaults and execute a READ - Request which only gives me back the result with that specific ObjectID.
My code so far for SAVING THE DATA:
// 1. Create new profile entry to the context.
let newProfile = Profiles(context: context)
newProfile.idProfileImage = idProfileImage
newProfile.timeCreated = Date()
newProfile.gender = gender
newProfile.name = name
newProfile.age = age
newProfile.weight = weight
// 2. Save the Object ID to User Defaults for "activeUser".
// ???????????????????
// ???????????????????
// 3. Try to save the new profile by saving the context to the persistent container.
do {
try context.save()
} catch {
print("Error saving context \(error)")
}
My code so far for READING THE DATA
// 1. Creates an request that is just pulling all the data.
let request: NSFetchRequest<Profiles> = Profiles.fetchRequest()
// 2. Try to fetch the request, can throw an error.
do {
let result = try context.fetch(request)
} catch {
print("Error reading data \(error)")
}
As you can see, I haven't been able to implement Part 2 of the first code block. The new profile gets saved but the ObjectID isn't saved to UserDefaults.
Also Party 1 of the second code block is not the final goal. The request just gives you back all the data of that entity, not only the one with the ObjectID I stored in User Defaults.
I hope you guys have an idea on how to solve this problem.
Thanks for your help in advance guys!
Since NSManagedObjectID does not conform to one of the types handled by UserDefaults, you'll have to use another way to represent the object id. Luckily, NSManagedObjectID has a uriRepresentation() that returns a URL, which can be stored in UserDefaults.
Assuming you are using a NSPersistentContainer, here's an extension that will handle the storage and retrieval of a active user Profile:
extension NSPersistentContainer {
private var managedObjectIDKey: String {
return "ActiveUserObjectID"
}
var activeUser: Profile? {
get {
guard let url = UserDefaults.standard.url(forKey: managedObjectIDKey) else {
return nil
}
guard let managedObjectID = persistentStoreCoordinator.managedObjectID(forURIRepresentation: url) else {
return nil
}
return viewContext.object(with: managedObjectID) as? Profile
}
set {
guard let newValue = newValue else {
UserDefaults.standard.removeObject(forKey: managedObjectIDKey)
return
}
UserDefaults.standard.set(newValue.objectID.uriRepresentation(), forKey: managedObjectIDKey)
}
}
}
This uses a method on NSPersistentStoreCoordinator to construct a NSManagedObjectID from a URI representation.
I have users collection that has sub collection called attendedEvents like the picture below. as you can see there are 3 documents in the attendedEvents sub collection
I try to get all documents available on that sub collection by using the code below, I just want to get it all, without order, limit or anything using getDocuments
func getAttendedEventsFromBeginning(completion: #escaping (_ eventID: [String]?,QueryDocumentSnapshot?)->Void) {
FirestoreDocumentReference.users(uidUser: uid).reference().collection("attendedEvents")
.getDocuments { (snapshot, error) in
let lastDocument = snapshot?.documents.last
if let error = error {
completion(nil,lastDocument)
print("Error when fetching attended events documents in user subcollection: \(error.localizedDescription)")
} else {
print("Successfully fetching attended events documents in user subcollection from Firestore ")
guard let documentsSnapshot = snapshot else {
completion(nil,lastDocument)
return
}
let eventDocuments = documentsSnapshot.documents
print("xxxxx")
print(eventDocuments)
var attendedeventIDs = [String]()
for document in eventDocuments {
let eventDictionary = document.data()
let theEvent = eventDictionary["eventID"] as! String
attendedeventIDs.append(theEvent)
}
print(attendedeventIDs)
completion(attendedeventIDs,lastDocument)
}
}
}
but as a result, I just got 2 document snapshot, it should be 3 documents
but if I delete the app from simulator and install it again, I got all the three data. is is cached or what?
I have been in the same situation. In my case, this happens because in my document it only contains subcollection. And that will cause the document itself to not be shown in queries or snapshots.
My walkaround method is to add some random info in the document to make it exist.
Here is my data structure:
I have an ios app that is attempting to access data from Cloud Firestore. I have been successful in retrieving full documents and querying for documents. However I need to access specific fields from specific documents. How would I make a call that retrieves me just the value of one field from Firestore in swift? Any Help would be appreciated.
There is no API that fetches just a single field from a document with any of the web or mobile client SDKs. Entire documents are always fetched when you use getDocument(). This implies that there is also no way to use security rules to protect a single field in a document differently than the others.
If you are trying to minimize the amount of data that comes across the wire, you can put that lone field in its own document in a subcollection of the main doc, and you can request that one document individually.
See also this thread of discussion.
It is possible with server SDKs using methods like select(), but you would obviously need to be writing code on a backend and calling that from your client app.
This is actually quite simple and very much achievable using the built in firebase api.
let docRef = db.collection("users").document(name)
docRef.getDocument(source: .cache) { (document, error) in
if let document = document {
let property = document.get(field)
} else {
print("Document does not exist in cache")
}
}
There is actually a way, use this sample code provided by Firebase itself
let docRef = db.collection("cities").document("SF")
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let property = document.get('fieldname')
print("Document data: \(dataDescription)")
} else {
print("Document does not exist")
}
}
I guess I'm late but after some extensive research, there is a way in which we can fetch specific fields from firestore. We can use the select keyword, your query would be somthing like (I'm using a collection for a generalized approach):
const result = await firebase.database().collection('Users').select('name').get();
Where we can access the result.docs to further retrieved the returned filtered result. Thanks!
//this is code for javascript
var docRef = db.collection("users").doc("ID");
docRef.get().then(function(doc) {
if (doc.exists) {
//gives full object of user
console.log("Document data:", doc.data());
//gives specific field
var name=doc.get('name');
console.log(name);
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});