'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 |