Swift 3 "updateChildValues" method fails with: Invalid key in object. Keys must be non-empty and cannot contain '.' '#' '$' '[' or ']'' - ios

I'm trying to update Firebase Database with email as value.
But Swift 3 "updateChildValues" method fails with an error: Invalid key in object. Keys must be non-empty and cannot contain '.' '#' '$' '[' or ']''
The code is:
let key = emailQueueRef.childByAutoId()
if user.email != nil {
inviteUpdate["\(key)"] = user.email!
}
emailQueueRef.updateChildValues(inviteUpdate)
Possibly, it fail because of dots in email. In other hand, encoding it into Base64 didn't fix it.
In the same time using "setValue" method works:
emailQueueRef.childByAutoId().setValue(user.email!)
But this approach is not quite good for me as I make it with several paths simultaneously.
I didn't find anything about that case in Firebase guides and docs
Is it a Firebase bug, or I misunderstood something?

The issue is the inviteUpdate string.
The string contains illegal characters but it's not the period . causing the problem. It's the ["(key)"] portion.
the var key is a Firebase DatabaseReference
The key you are trying to write to Firebase looks like this
["https://your_app.firebaseio.com/email_node/-KorK9Io5Y2XejgLInvj": "dude#someemail.com"]
It's not clear from the question what the actual Firebase structure should be but I think what you want is this
let key = emailQueueRef.childByAutoId()
if user.email != nil {
inviteUpdate[key.key] = user.email! //note key.key
}
emailQueueRef.updateChildValues(inviteUpdate)
which then writes this to Firebase
"-KorOLfUa9HvgDIcpIyg": "dude#thing.com"
I am going to urge you to not use email addresses as keys as it can lead to a lot of work later on.
For example, if a user changes their email address. You then need to totally delete the user node that uses it as a key, and re-write it. At the same time, anywhere else that references that key will also have to be changed.
It's usually much better to include the email as a child node
uid_0
email: "dude#someemail.com"
and then other areas can simply refer to uid_0.

After some attempt I've found my main error. I got DatabaseReference with childByAutoId() and I just missed one additional step in the guide, to get the key itself.
The resulting code is:
let keyRef = emailQueueRef.childByAutoId()
let key = keyRef.key
if user.email != nil {
inviteUpdate[key] = user.email!
}
emailQueueRef.updateChildValues(inviteUpdate)
Finally that helped.

Related

Flutter IOS App crashing for some specific users [duplicate]

I have the following code and getting an error :
Invalid collection reference. Collection references must have an odd number of segments
And the code :
private void setAdapter() {
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("app/users/" + uid + "/notifications").get().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
for (DocumentSnapshot document : task.getResult()) {
Log.d("FragmentNotifications", document.getId() + " => " + document.getData());
}
} else {
Log.w("FragmentNotifications", "Error getting notifications.", task.getException());
}
});
}
Then you need replace it:
db.collection("app/users/" + uid + "/notifications")...
with it:
db.collection("app").document("users").collection(uid).document("notifications")
You're welcome ;)
Hierarchical data structures and subcollections are described in the documentation. A collection contains documents and a document may contain a subcollection. The structure is always an alternating pattern of collections and documents. The documentation contains this description of an example:
Notice the alternating pattern of collections and documents. Your
collections and documents must always follow this pattern. You cannot
reference a collection in a collection or a document in a document.
Thus, a valid path to a collection will always have an odd number of segments; a valid path to a document, an even number. Since your code is trying to query a collection, the path length of four is invalid.
You are missing collection reference.
i.e db.collection(** This is getting null **).
I've encountered this issue when I provided a wrong entity_Id.
Instead of dojo/default/datasets/fe67ec58-6208-4234-a4ee-98c5dce4665f,
I've provided fe67ec58-6208-4234-a4ee-98c5dce4665fand now is working fine.
In my case collection name is empty was causing the crash
FirebaseFirestore.getInstance().collection("").add(taravih)
I've encountered this issue when I provided an entity_Id that contains the "/" character ( my value was N/A ) when i was trying to read documentReference (DocumentReference docRef2 = fireStoreDb.Collection("Ass").Document(ass.Tel.ToString())
.Collection("vehicules").Document(ve.Immatriculation);).
Here, the value ve.Immatriculation equals to N/A, and that was the problem.

Dart compare two strings return false

Im new to dart and have a problem during building my Flutter application.
I have a firestore database as a backend and im getting data from there.
When i want to compare part of the data called status with the text 'CREATED', using == comparator, dart will return false.
Can someone explain why and how to check it properly?
rideObject is a Map
Update:
Here is the function that has the condition in it:
Widget _getPage() {
if (rideObject == null) {
return OrderRidePage(
address: address,
ridesReference: reference,
setRideReference: this._setRideReference);
} else {
print(rideObject['status']);
if (rideObject['status'] == "CREATED") {
return LoadingPage(
removeRideReference: this._removeRideReference,
rideReference: rideReference);
} else {
return RidePage(
address: address,
ridesReference: reference,
setRideReference: _setRideReference);
}
}
}
The print statement returns to output:
I/flutter (15469): CREATED
Here you can see the structure of the rideObject
Funnily enough, the rideObject["status"] is String type as shown in here in console:
rideObject["status"] is String
true
"CREATED" is String
true
rideObject["status"]
"CREATED"
rideObject["status"] == "CREATED"
false
The String you got from your server is probably encoded and contains special character which you can't see, try to compare the hex values of both of the strings, and then replace all the special characters from the String returned by the server.
Using this, you can see the actual non visible difference between the two strings:
var text1 = utf8.encode(hardcodedText).toString();
var text2 = utf8.encode(textFromServer).toString();
If both are really strings, you can use "compareTo" which will return 0 if both are equal.
if(str1.compareTo(str2)==0){
}
It is explained here:
https://www.tutorialspoint.com/dart_programming/dart_programming_string_compareto_method.htm
I don't have a particular solution to this, but I updated to latest Flutter version that came up today, moved the "CREATED" string into constant and resolved an unrelated warning for another part of the application, and it suddenly started to work.
The answer for this problem is in the documentation of flutter:
https://api.flutter.dev/flutter/dart-core/String/compareTo.html
you can do:
(var.compareTo('WORD') == 0)
are equivalent
.compareTo()
Returns a negative value if is ordered before, a positive value if is ordered after, or zero if and are equivalent.thisother
Building off #yonez's answer, the encoding may be different after a string has been passed through a server.
Instead of: String.fromCharCodes(data)
Try using: utf8.decode(data)

firestore allow write for only one field

I have the following security rule set up in Firestore:
match /ads/{adId} {
allow read: if true;
allow write: if (request.auth.uid == request.resource.data.ownerId) ||
(request.auth != null &&
request.resource.data.size() == 1 &&
request.resource.data.keys().hasAll(['markedFavoriteBy']));
}
The owner of the document has all write permissions but other users can only write in one field - which is called 'markedFavoriteBy'. It seems to work in the Firestore simulator but not in Xcode (with iPhone simulator).
Important to note that the path does not go down to the document field - just to the document. I know that it is not possible to attach a security rule to a single field. However, with this workaround it should still work.
The iOS client code to update the field is the following:
let adRef = Firestore.firestore().collection("ads").document(ad.key!)
let value: FieldValue = ad.isFavorite ? FieldValue.arrayUnion([uid]) : FieldValue.arrayRemove([uid])
adRef.updateData([DatabaseKeys.markedFavoriteBy : value]) { (error) in
if let error = error {
print(error.localizedDescription)
}
}
The error printed to the console is of course: "Missing or insufficient permissions."
I can't find an error in the code - is this a bug that is somehow related to the very recent iOS Firestore SDK update? the functions arrayUnion and arrayRemove have been added only in that recent update.
Sanity check: I have changed the write rule to
allow write: if true;
Result: The array 'markedFavoriteBy' can be changed without any problem.
thanks for any hints in the right direction.
I'm not able to reproduce the entire scenario as I'm not sure of the actual contents of your database. However, what I suspect is the problem is that it doesn't grant permission because request.resource.data represents the new version of the entire document (which will be stored after the update is approved).
You probably want to use request.writeFields doc
Bear in mind that request.writeFields is not available in the simulator.

How do I set the name a child in Firebase to user input from a text field?

Here's how I want my data tree to look in Firebase:
users:
--Travis:
------travisData:
--Stephanie:
------stephanieData:
My problem is that I can't seem to name a child using user input from a textfield.
Here's the code I have so far:
ref = Database.database().reference()
let username = String(emailTextField.text!)
//ref.child("users").setValue(["username": username])
ref.child("users").setValue(["\(username)": calendar])
The commented out line creates a child named "username" with the textfield.text as its content. But how can I create a child whose name is the textfield.text, with other data as its content? I've tried multiple combinations of using .child and .setValue, but everything keeps throwing me an error when I try to use the username variable instead of a plain string.
Here's the error I get:
Your problem is that Firebase doesn't accept the "." symbol in their user names. You would get the same problem even if you typed a string of letters with a "." such as "adfojnajsdnfjs.fjknasd" instead of "username".
I think you're looking for:
ref.child("users").child(username).setValue(calendar)
Although that depends on the value of calendar.
At the very least this should work:
ref.child("users").child(username).setValue(true)
And give you the following JSON:
"users": {
"Travis": true
}
To write a more complex structure, you can for example do:
ref.child("users").child(username).child("name").setValue(username)
Which results in:
"users": {
"Travis": {
name: "Travis"
}
}
As your require, I think this code will help you
ref.child("users").child("\(inputUsername)").updateChildValues(["travidDataKey": userData])
And
result image
users:
Travid2:
travidDataKey:"Travid Data"

Search Firebase Query with array of unique keys

I have my schema set as follows.
Now i want to send the a string chinohills7leavescafe101191514.19284 and want to check if there is any string in the Chino Hills or not.
I am confused to make any search query because i have not stored above string in fixed childKey
I know the schema should be like this but i cannot change the schema.
leaves-cafe
codes
Chino Hills
-Kw0ZtwrPjyNh1_HJrkf
codeValue: "chinohills7leavescafe101191514.19284"
You're looking for queryOrderedByValue. It works the same ways as queryOrderedByChild and allows you to use queryEqualToValue to achieve the result you need since you can't alter your current schema.
Here's an example
// warning: untested code - just illustrating queryOrderedByValue
let ref = Database.database().reference().child("leaves-cafe").child("codes").child("Chino Hills")
let queryRef = ref.queryOrderedByValue().queryEqual(toValue: "chinohills7leavescafe101191514.19284")
queryRef.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
print("value does exists")
} else {
print("value doesn't exist")
}
})
Your alternative option is to iterate over all nodes and check if the value exists manually as 3stud1ant3 suggested. However, note that this approach is both costy and a security risk. You would be downloading potentially a lot of data, and generally you shouldn't load unneeded data (especially if they're sensitive information, not sure if that's your case) on device; it's the equivalent of downloading all passwords off a database to check if the entered one matches a given user's.

Resources