'How can I display my user custom data in String format?
I'm new to Swift UI and MongoDB Realms. I'm trying to display a user's email (from their respective custom user data) in a text box but I'm getting a weird result that I don't know how to fix. I've been semi-blindly trying all sorts of things hoping something would work but no luck so far. I'm using Email/Password authorization so I know that the email/password wouldn't be stored in the user custom data (I think) but its just an example for what I'm trying to do.
My current code so far below.
struct HomeView: View {
let user = app.currentUser!
@State var isLoggingOut = false
var body: some View {
let userEmail = user.customData["email"] ?? "no email"
let userPassword = user.customData["password"] ?? "no password"
let userDisplay = user.customData["display"] ?? "no display"
let userName = user.customData["fullName"] ?? "no name"
ZStack {
Rectangle().foregroundColor(.yellow)
VStack {
Spacer()
Text("Home")
HStack {
Text("Email").frame(width: 100)
Spacer()
Text(String(reflecting: userEmail))
}.padding(.vertical)
HStack {
Text("Password").frame(width: 100)
Spacer()
Text(String(describing: userPassword))
}.padding(.vertical)
HStack {
Text("Display").frame(width: 100)
Spacer()
Text(String(describing: userDisplay))
}.padding(.vertical)
HStack {
Text("Full name").frame(width: 100)
Spacer()
Text(String(describing: userName))
}.padding(.vertical)
Spacer()
Button("Log Out") {tryLogOut()}
}.padding(40)
if isLoggingOut {LoginView()}
}
}
func tryLogOut() {
app.currentUser?.logOut {error in}
self.isLoggingOut = true
}
}
After logging in with a test user, this is what I'm getting in the right HStack text boxes (for example, the top email text box):
Email Optional(RealmSwift.AnyBSON.string("[email protected]"))
Obviously what I'm trying to end up with is:
Email [email protected]
What am I doing wrong? Everything else works as intended but this problem is giving me a headache. Any help would be appreciated.
Also FYI - Everything I am trying to display in the text boxes is stored in the database as Strings according to Atlas so I don't see the problem. However in my NewUserRegistrationView, when I create the new user document, I use the following code, I'm not sure if there is anything conflicting with the AnyBSON types before inserting the document.
struct NewUserRegistrationView: View {
// Email, password, displayName, and fullName obtained from TextFields in the body ...
// createUserDocument() is called after registering and confirming the user
func createUserDocument() {
let credentials = Credentials.emailPassword(
email: self.email,
password: self.password)
app.login(credentials: credentials) {result in
switch result {
case .failure:
self.statustext = "Document creation failed, try again"
case .success(let user):
let client = user.mongoClient("mongodb-atlas")
let database = client.database(named: "AppDatabase")
let collection = database.collection(withName: "userDocuments")
collection.insertOne([
"userID": AnyBSON(user.id),
"email": AnyBSON(self.email),
"password": AnyBSON(self.password),
"display": AnyBSON(self.display),
"fullName": AnyBSON(self.fullName)
]) { result in
switch result {
case .failure:
self.statustext = "Could not add document"
case .success(let newObjectId):
self.statustext = "Inserted document with objID: \(newObjectId)"
self.isDone = true
}
}
}
}
}
Solution 1:[1]
It is because you are using String(...) change it to 'userEmail.description ?? ""`
Solution 2:[2]
The best way to display user data is to have a function in your class/struct that converts user data in whatever form to a string and then displays it. This would allow you to just use one function to convert data
Solution 3:[3]
I managed to figure it out but I have absolutely no idea why it works or what is happening. Lucky enough, I got it by brute force trial and error. Other answers didn't work unfortunately, but thanks to those for the suggestions.
I changed:
let userEmail = user.customData["email"] ?? "no email"
let userPassword = user.customData["password"] ?? "no password"
let userDisplay = user.customData["display"] ?? "no display"
let userName = user.customData["fullName"] ?? "no name"
To this:
let userEmail = ((user.customData["email"] ?? "email")?.stringValue)!
let userPassword = ((user.customData["password"] ?? "password")?.stringValue)!
let userDisplay = ((user.customData["display"] ?? "display")?.stringValue)!
let userName = ((user.customData["fullName"] ?? "fullName")?.stringValue)!
Text(userEmail)
Text(userPassword)
Text(userDisplay)
Text(userName)
Can anyone breakdown how this works? Like what do the question/exclamation marks do? And what is a simpler way to do this (if any)?
Solution 4:[4]
First of all conditionally downcast all dictionary values to AnyBSON to get the String value
let userEmail = (user.customData["email"] as? AnyBSON)?.stringValue ?? "no email"
let userPassword = (user.customData["password"] as? AnyBSON)?.stringValue ?? "no password"
let userDisplay = (user.customData["display"] as? AnyBSON)?.stringValue ?? "no display"
let userName = (user.customData["fullName"] as? AnyBSON)?.stringValue ?? "no name"
Without the cast you get Any which prints the weird output using String(reflecting
Then simply write
Text(userEmail)
...
Text(userPassword)
...
Text(userDisplay)
...
Text(userName)
Solution 5:[5]
This solves the problem of decoding any AnyBSON. Not, user.customData...which in fact always returns nil!
I'm upset that it took so long to basically guess at the solution. And even this solution needs a refactor as I'm force unwrapping which means its a ticking bomb.
So I used the Realm call gist:
collection.findOneDocument(filter: ["userId": AnyBSON(user.id)])
Using the result:
case .success(let document):
let email = document?["email"] ?? "no email"
print("Email: \(String(describing: email!.stringValue!))")
So this returns the email string by itself. The fact I had to double-unwrap is insane.
Naturally, you want to guard and or if let as necessary. The document in this case is a Realm Document which is: Dictionary <String : AnyBSON?>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | lorem ipsum |
| Solution 2 | a1cd |
| Solution 3 | cresendez744 |
| Solution 4 | |
| Solution 5 | kerryatkanjo |
