I'm currently building an iOS app that has its own database on Firebase to handle the app's basic functionality.
However I want to add more information to each user (aside from the uid, email and password) so I can validate some steps in my app. What's the best way to achieve this, hierarchy-wise?
I'm using the new Firebase btw.
There really isn't a schema. You write the values into a heirarcy you want. First you reference the UID.
In Swift it would be:
var usersRef = ref.childByAppendingPath("users")
Then you would create an object with all values you want to write. You could also write the values directly without making an object first.
let newUser = [
"provider": authData.provider,
"displayName": authData.providerData["displayName"] as? NSString as? String
]
Then write the values with:
ref.childByAppendingPath("users")
.childByAppendingPath(authData.uid).setValue(newUser)
The docs are tricky to follow. The reference for this example is https://www.firebase.com/docs/ios/guide/user-auth.html
This block of code will give you:
{
"users": {
"6d914336-d254-4fdb-8520-68b740e047e4": {
"displayName": "alanisawesome",
"provider": "password"
},
"002a448c-30c0-4b87-a16b-f70dfebe3386": {
"displayName": "gracehop",
"provider": "password"
}
}
}
Hope this helps!
Related
I am learning Flutter with Firebase. I have some dummy data in Firebase Realtime Data base as:
{
"postsnode": {
"posts": [
{
"postId": "u1",
"postName": "p1"
}
]
},
"usersnode": {
"users": [
{
"userId": "u1",
"userName": "bla bla 1"
},
{
"userId": "u2",
"userName": "bla bla 2"
}
]
}
}
The screen shot of the console database structure is:
I have successfully performed a query on my usersnode object to get the specific users:
void queryDB(BuildContext context) async {
AppUtil.showLoader(context: context);
FirebaseDatabase.instance.ref().child('usersnode/users').orderByChild('userId').equalTo("u1").get().then((snapshot) {
if (snapshot.exists) {
print("user:::" + snapshot.value.toString());
final jsonResponse = json.encode(snapshot.value);
List<dynamic> list = json.decode(jsonResponse);
List<Users> users = [];
list.forEach((element) {
Users usersModel = Users.fromJson(element);
users.add(usersModel);
});
users.forEach((element) {
debugPrint("UseX:::" + element.userName);
});
} else {
print('No data available.1');
}
AppUtil.dismissLoader(context: context);
// }).onError((error, stackTrace) {
// print('No data available.2');
// AppUtil.dismissLoader(context: context);
});
}
I just want to update my specific object suppose the users object with the userId=u1 only!
Can somebody help me getting this, update using the query! or I'm doing the wrong way!
Actually I just simple want to update the object based on some condition:
Update Users -> where userId=u1
Thanks in Advance!
Welcome to Flutter Firebase!
First, you should probably change your data structure. There's no need to have a random incremental ID for users. You can use their unique ID as the key for the object:
{
"posts": {}
...
"users": {
"u1": {
"userName": "bla bla 1"
},
"u2": {
"userName": "bla bla 2"
}
}
}
Then, you can do this:
final String userId = 'u1';
final userDoc = FirebaseDatabase.instance.reference().child('users/$userId');
If you're planning on storing a lot of data in posts or users, I highly recommend that you check Cloud Firestore. It offers better structure and more advanced data types.
You're starting with a very flimsy foundation to your database if you go down the route you're taking. A proper schema would look like this:
{
"posts": {
"$postID": {
"postName": "Some Name"
// ...
}
}
"users": {
"$userID": {
"userName": "Some Username"
}
}
}
You would be wise to avoid arrays entirely when using firebase database. This isn't a firebase standard, it's a NoSQL practice.
In short, NoSQL databases use key value pairs to structure data. Since you won't have any two users with the same ids, nor two posts with the same ids, using key-value pairs in your database let you easily accomplish what you're trying to do by design.
I just want to update my specific object suppose the users object with
the userId=u1 only! Can somebody help me getting this, update using
the query! or I'm doing the wrong way!
The way to do this in the user nodes is as simple as this:
String userID = "$userID"; // where $userID is your targeted user
String newUserName = "$newUsername"; // this is the new data you're trying to change
DatabaseReference userRef = FirebaseDatabase.instance.ref().child('users').child(userID);
userRef.update({
"userName": newUserName,
"timestamp": ServerValue.timestamp,
});
I have successfully performed a query on my usersnode object to get the specific users:
Obviously your old implementation won't work when you update your schema to be key-value paired. But that's a good thing because now you can do proper queries which you can then convert into arrays in the app. You would effectively parse through the DatabaseSnapshot value, which is a Map where the keys are the userIDS and the values are corresponding data.
The way you would load the users into your app with is as follows:
DatabaseReference usersRef = FirebaseDatabase.instance.ref().child('users');
usersRef.orderByChild("userName").once(DatabaseEventType.value).then((DatabaseEvent databaseEvent) {
// (You should update to the latest package which has breaking changes like this you need to adjust to)
// First get the snapshot from the new DatabaseEvent object
DataSnapshot snapshot = databaseEvent.snapshot;
// Convert the value into a map you can parse through
Object? snapshotValue = snapshot.value;
Map<dynamic, dynamic> valueMap = snapshotValue is Map<dynamic, dynamic> ? snapshotValue : {};
// Print the results for debugging
if(valueMap.isEmpty) {
debugPrint("No users found");
} else {
debugPrint("${valueMap.length} user${valueMap.length == 1 ? '' : 's'} found");
}
// Loop through the keys of this map (which are the userIDS)
List<Users> users = [];
for(String userID in valueMap.keys) {
Users userModel = Users.fromJson(valueMap[userID]);
users.add(usersModel);
debugPrint("UseX:::" + userModel.userName);
}
AppUtil.dismissLoader(context: context);
});
Lastly, if you allow users to create posts, you might want to consider adding the following node to your schema in addition to just "posts":
"user_posts" {
"$userID": {
"$postID": POST_OBJECT,
// ...
}
}
Then when you want to load the posts created by the user, rather than only being able to query the "posts" node for posts created by the user (which you'll have a limited querying ability), you query the posts under this specific user's posts (by timestamp for example).
If you do it this way, you'd store a copy of the same object under the user's posts node and posts' node.
You seem new to firebase and have a limited understanding of how the database works. It can get very messy very fast and the most important thing to do is understand how you're going to use the data, how it's going to be queried, and most importantly, how you want to be able to sort it (by alphabetical order, by ranking, by number of likes, by date created, etc.).
Understanding these concepts now is very important because it's directly tied to your database security rules as well (which you wouldn't be able to protect with the way you were doing it before).
My advice would be to look up a quick start guide on NoSQL databases (maybe just the firebase documentation starting guide), look at the firebase database security rules guide, understand what fanning out data is, and then start looking into cloud functions so you can do useful things when data is created, updated, or deleted.
It's very easy to neglect this stuff early on while only focusing only on the app, but this is equally important despite not being something your end user would spend a second thinking about. Without a solid database, you'll run into endless issues in the future.
Good luck :)
I want to import the data from Google Spreadsheet into Firebase Database, using Google Apps Script library access to the Firebase Database.
There is tutorial that shows how it can be accomplished, I followed these steps and my code in Google Apps Script is here:
function updateData() {
function getFirebaseService(){
return OAuth2.createService('Firebase')
// Set the endpoint URL.
.setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
// Set the private key and issuer.
.setPrivateKey(PRIVATE_KEY)
.setIssuer(CLIENT_EMAIL)
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getScriptProperties())
// Set the scopes.
.setScope('https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/firebase.database');
}
var token = getFirebaseService().getAccessToken();
Logger.log(token);
}
This is my appsscript.json↓
{
"timeZone": "Asia/Hong_Kong",
"dependencies": {
"libraries": [{
"userSymbol": "OAuth2",
"libraryId": "1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF",
"version": "33"
}, {
"userSymbol": "FirebaseApp",
"libraryId": "1hguuh4Zx72XVC1Zldm_vTtcUUKUA6iBUOoGnJUWLfqDWx5WlOJHqYkrt",
"version": "30"
}]
},
"exceptionLogging": "STACKDRIVER",
"oauthScopes": [
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/firebase.database",
"https://www.googleapis.com/auth/spreadsheets.currentonly",
"https://www.googleapis.com/auth/spreadsheets"]
}
Necessary oauthScopes has been added.
When I connected the Firebase , the result is 'Error: Access not granted or expired. 454'.
It's might be AccessToken expired, but after updating the latest PRIVATE_KEY and CLIENT_EMAIL, the error still show.
I have no idea about it , if someone could give me advices, I'll be grateful.
I'm trying to add security rules to a new Firestore project I'm working on. I have a collection named users that has all my user data in it in this format in my Firestore database:
var users = {
"userId": {
friend_requests: [],
friends: [
/users/friendId1,
/users/friendId2
],
name: "User One",
username: "user1"
},
"friendId1": {
friend_requests: [],
friends: [
/users/userId
],
name: "User Two",
username: "user2"
},
"friendId2": {
friend_requests: [],
friends: [
/users/userId
],
name: "User Three",
username: "user3"
},
"lonelyUser": {
friend_requests: [],
friends: [],
name: "Lonely User",
username: "lonely_user"
}
}
My Firestore rules are this, verbatim:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
match /users/{userId} {
allow read: if isOwner(userId) || isFriendOf(userId);
}
}
function isOwner(userId) {
return userId == currentUser().uid;
}
function isFriendOf(userId) {
return getUserPath(userId) in getUserData().friends;
}
function currentUser() {
return request.auth;
}
function getUserData() {
return get(getUserPath(currentUser().uid)).data;
}
function getUserPath(userId) {
return /databases/$(database)/documents/users/$(userId);
}
}
The keys in the map I outlined above are Firebase user ids in my actual db, so when logged in as user "userId" I'd expect to be able to read the user document for both "friendId1" and "friendId2" users.
The problem I'm having is that isFriendOf is returning false. I've tried a few variants of wrapping the comparison data in a get call and passing in other values like id and data off the resource it returns.
I've also tried wrapping the getUserPath call in isFriendOf in a get and then using the __name__ property of the document as the comparison value as well. No luck there either.
Any help would be greatly appreciated.
Edit: Including a screenshot of the actual documents for clarification.
Screenshot of Firebase Documents
Edit 2: I've made a clean firebase project with only the information in these new screenshots in it. I'm including screenshots of Firebase Authentication page, both user entries in the database, as well as a failed simulator run as one user trying to get the document of the other user.
Authentication Configuration
user1
user2
Simulated Request
So I'm making a chat app, and I need to access the user's name. But I only have the users ID. Is there any way I can search through the entire database and find a user with the same id as I have?
Firebase Tree Setup Image
this is my firebase tree setup. So I have the users id: eg. 1JIalS7s85PgucxZgGi48ao9Oaf2
However, I don't have the user's name: eg. DummyBoy
Is there a way to find the name
You can store the names of your users like this.
{
"users": {
"{uid}": {
"name": "John"
}
}
}
Now using a user's uid, you can get their name in swift like this:
let ref = FIRDatabase.database().referenceWithPath("users/\(uid)/name")
ref.observeSingleEventOfType(.Value, withBlock: { snapshot in
if let name = snapshot.value as? String {
print(name)
}
})
I'm currently trying to add new users to Firebase via the AngularFire $set() method. I'm creating new users via the $createUser method from AngularFire. My code looks like this:
$scope.createUser = function() {
$scope.auth.$createUser('dolly#gmail.com', 'password').then(function(user, err) {
if (err) {
console.log('ERROR: ' + err);
} else {
sync.$set('users', user);
}
});
}
This is a creating new user and placing the new user object inside of users:{..}, however, it is also adding an additional user child object thats just duplicate data -- this is what the code is adding to Firebase:
{
"users": {
"email": "dolly#gmail.com",
"id": "11",
"isTemporaryPassword": false,
"md5_hash": "xxxxxxxxxxxxxxx",
"provider": "password",
"sessionKey": "xxxxxxxxxxxxx",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0MDc5NDQ2NDYsInYiOjAsImQiOnsicHJvdmlkZXIiOiJwYXNzd29yZCIsImlkIjoiMTEiLCJ1aWQiOiJzaW1wbGVsb2dpbjoxMSIsImVtYWlsIjoiZG9sbHlAZ21haWwuY29tIiwibWQ1X2hhc2giOiIzsdrggfeedsaadrfcDc0ZDRhMTU5NTk2NzI1NzFmMDk2ZTZlNyIsImlzVGVtcG9yYXJ5UGFzc3dvcmQiOmZhbHNlLCJzZXNzaW9uS2V5IjoiM2MwMDNjODkxMDEzOWE5MjhlZTZjNWI1NjU5ZTRiZjMifSwiaWF0IjoxNDA3ODU4MjQ2fQ.p7-9GDtaNpBn1ICTLIUSwlPytaUGi-jyBgcO-LKHUys",
"uid": "simplelogin:11",
"user": {
"email": "dolly#gmail.com",
"id": "11",
"isTemporaryPassword": false,
"md5_hash": "xxxxxxxxxxxxxxx",
"provider": "dfrggrssxxxxxxx",
"sessionKey": "xxxxxxxxxxxxxxx",
"uid": "simplelogin:11"
}
}
I ideally want my users object to look like the example in firebase, except with each user key to be whatever is inside user.uid
users: {
user1: {
name: "Alice"
},
user2: {
name: "Bob"
}
}
Where each new user will be added to the users: {...} key without the duplicate user child tacked on?
If you create a reference to users/ and then call $set on that path, then whatever data you include will replace anything at that path. This is AngularFire 101. You should begin by reading at least that section if not the entire guide.
Choose the path you want to set data at when creating your sync object.
var ref = new Firebase('.../users/'+user.uid);
var sync = $firebase(ref);
sync.$set({ email: user.email, provider: user.provider });
Or, better yet, just set it on the reference since you aren't utilizing this for client-side bindings.
var ref = new Firebase('.../users/'+user.uid);
ref.set({ email: user.email, provider: user.provider });
Creating profiles is explained in the Firebase docs and covered in the AngularFire-Seed repo's createProfile service, nearly verbatim to your example.