I know this has been asked on stack overflow several times but I cannot seem to find the answer I am looking for. I am trying to store data from a firebase database (using the observeSingleEvent(snapshot)) method in a global variable. See below code for details.
I've tried adding a completion handler and followed steps online, but doing so, the observeSingleEvent request stops working.
What I had before:
class ItemsTableViewController: UITableViewController {
let listToUsers = "ListToUsers"
var user: User!
let ref = Database.database().reference()
override func viewDidLoad() {
super.viewDidLoad()
let defaultUser = User()
ref.child("users").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
defaultUser.uid = uid
defaultUser.email = value?["email"] as? String ?? ""
defaultUser.name = value?["name"] as? String ?? ""
defaultUser.grad = value?["grad"] as? Int ?? 0
defaultUser.number = value?["number"] as? String ?? ""
defaultUser.image = UIImage(named: value?["image"] as? String ?? "")!
completion(defaultUser)
}) { (error) in
print("hello")
print(error.localizedDescription)
}
self.user = defaultUser
}
}
What I tried after and still did not work:
class ItemsTableViewController: UITableViewController {
// MARK: Constants
let listToUsers = "ListToUsers"
// MARK: Properties
var user: User!
let ref = Database.database().reference()
override func viewDidLoad() {
super.viewDidLoad()
let use = Auth.auth().currentUser?.uid
self.getUserData(uid:use!) { (user) -> () in
self.user = user
}
}
func getUserData(uid:String , completion: #escaping (User) -> ()) {
ref.child("users").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let defaultUser = User()
defaultUser.uid = uid
defaultUser.email = value?["email"] as? String ?? ""
defaultUser.name = value?["name"] as? String ?? ""
defaultUser.grad = value?["grad"] as? Int ?? 0
defaultUser.number = value?["number"] as? String ?? ""
defaultUser.image = UIImage(named: value?["image"] as? String ?? "")!
completion(defaultUser)
}){ (error) in
print("hello")
print(error.localizedDescription)
}
}
Firebase is asynchronous and the self.user = defaultUser in the first code block is going to be called way before Firebase has a chance to return data.
Data is ONLY valid within the closure following the Firebase call. So here's how to fix it. Oh, and remove that error code as it isn't needed.
Assume a user structure like this
users
uid_0
name: "some name"
email: "some email"
and the code to read in uid_0 and populate a global (i.e. class var) user
class MyUser {
var uid = ""
var name = ""
var email = ""
init(aUid: String, aName: String, aEmail: String) {
self.uid = aUid
self.name = aName
self.email = aEmail
}
}
var user: MyUser!
func readOneUser() {
let uid = "uid_0"
let thisUserRef = self.ref.child("users").child(uid)
thisUserRef.observeSingleEvent(of: .value, with: { snapshot in
//let uid = snapshot.key if you don't know the uid, it will be the key to the node
let name = snapshot.childSnapshot(forPath: "name").value as? String ?? "No Name"
let email = snapshot.childSnapshot(forPath: "email").value as? String ?? "No Email"
self.user = MyUser(aUid: uid, aName: name, aEmail: email)
print(self.user.name) //do something with the user here
})
}
and this line
defaultUser.image = UIImage(named: value?["image"] as? String ?? "")!
needs to be addressed but I don't know what the intention is. It could end up being nil if this happens
defaultUser.image = UIImage(named: "")!
so you may want to populate it with a default image of some kind or provide error handling in case it is nil.
Related
enter image description here
I have a userProfile file:
class UserProfile {
var uid:String
var email: String
var username:String
var photoURL:URL
init(uid:String, email:String, username:String, photoURL:URL) {
self.uid = uid
self.email = email
self.username = username
self.photoURL = photoURL
}
}
and a Post file
class Post {
var id:String
var author:UserProfile
var text:String
var timestamp:Date
init(id:String, author:UserProfile, text:String,timestamp:Double) {
self.id = id
self.author = author
self.text = text
self.timestamp = Date(timeIntervalSince1970: timestamp / 1000)//divided by 1000 because firebase stores dates as milliseconds
}
}
This is the way it shows in firebaseenter image description here
So what I'm trying to do is reuse the references(username, date, and urlimage) that the two files before uses.
Here the code thats used for the post file:
func oberseverRoomatePostFeed(){
let postRef = Database.database().reference().child("posts")
postRef.observe(.value, with: { snapshot in
var currentUserRoomatePose = [Post]()//temporary array
//array****************************
for child in snapshot.children {
if let roommateSnapshot = child as? DataSnapshot,
let dict = roommateSnapshot.value as? [String:Any],
let author = dict["author"] as? [String:Any],
let uid = author["uid"] as? String,
let photoURL = author["photoURL"] as? String,
let email = author["email"] as? String,
let username = author["username"] as? String,
let url = URL(string:photoURL),
let text = dict["text"] as? String,
let timeStamp = dict["timestamp"] as? Double{
let userP = UserProfile(uid: uid, email: email, username: username, photoURL: url)
let post = Post(id: roommateSnapshot.key, author: userP, text: text, timestamp: timeStamp)
currentUserRoomatePose.append(post)
}
}
self.posts = currentUserRoomatePose
self.tableView.reloadData()
})
}
And here is what I have so far
class User: NSObject {
var name: String?
var currentUser: UserProfile
var currentPost: Post
init(dictionary: [String: Any], currentUser:UserProfile, currentPost:Post) {
self.name = dictionary["name"] as? String ?? ""
self.currentUser = currentUser
self.currentPost = currentPost
}
}
and
func fetchUser() {
let postRef = Database.database().reference().child("users")
postRef.observe(.value, with: { snapshot in
var currentUsers = [User]() // temp array
for child in snapshot.children {
if let userSnapshot = child as? DataSnapshot,
let dict = userSnapshot.value as? [String: Any],
let author = dict["author"] as? [String:Any],
let uid = author["uid"] as? String,
let photoURL = author["photoURL"] as? String,
let email = author["email"] as? String,
let username = author["username"] as? String,
let url = URL(string:photoURL),
let text = dict["text"] as? String,
let timeStamp = dict["timestamp"] as? Double {
let userP = UserProfile(uid: uid, email: email, username: username, photoURL: url)
let user = User(dictionary: [String : Any], currentUser: userP, currentPost: Post)
}
}
})
}
enter image description here
enter image description here
func checkLogin() {
if Auth.auth().currentUser?.uid == nil {
perform(#selector(backButton), with:nil, afterDelay:0)
} else {
let uid = Auth.auth().currentUser?.uid
Database.database().reference().child("Users/profile/").child(uid!).observeSingleEvent(of: .value, with: {(snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
self.navigationItem.title = dictionary["username"] as? String
}
}, withCancel: nil)
}
}
"Users" : {
"kFjK5Kcrk7dLCnd3fQOnhBcPQHz1" : {
"Email" : "cmhughes95#gmail.com",
"Full Name" : "Cameron Hughes",
"Google UID" : "112185374105612274429",
"provider" : "Google"
},
"profile" : {
"0Ef8GJch5PPZ8yE9jLSXAS7fVoK2" : {
"email" : "teclarke#aggies.ncat.edu",
"password" : "Tecl6013",
"photoURL" : "https://firebasestorage.googleapis.com/v0/b/aggie-wallet.appspot.com/o/user%2F0Ef8GJch5PPZ8yE9jLSXAS7fVoK2?alt=media&token=62827fc7-38ec-47ae-9972-c078ef1d486e",
"username" : "tec95"
},
"EsqtPIFUWQbXh0ItLWK0W3qxOdI2" : {
"email" : "teclarke#aggies.ncat.edu",
"password" : "Tecl6013",
"photoURL" : "https://firebasestorage.googleapis.com/v0/b/aggie-wallet.appspot.com/o/user%2FEsqtPIFUWQbXh0ItLWK0W3qxOdI2?alt=media&token=40c82e6e-cc4d-4320-a0ab-434cc297567a",
"username" : "tyrek95"
},
Right off, there appears to be an issue with the users node. You've got what looks like a uid at the same level as a node called 'profile' which then contains other user id's. That's not going to work. The uid's should all be at the same level. The nodes also contain different child nodes so it's unclear what the purpose is. This would be a better structure:
users
uid_0
email: "test#test.com"
photoURL: "https://www.xxxxxx"
username: "tyrek95"
uid_1
email: "yipee#yipee.com"
photoURL: "https://www.yyyyy"
username: "someusername"
Based on comments, it appears you're trying to get a single user name to put in a titlebar - there's a whole lot of extraneous code in the question if that's the task. The checkLogin and fetchUser functions aren't called and while denormalization in the posts node is fine, it's unnecessary duplicate data - you don't need to have the email, photoURL duplicated as you know the uid and can get that from the users node
A better structure is
posts
post_0
author: "uid_0"
text: "Hello, World"
timestamp: "some time"
post_1
author: "uid_1"
text: "What's happening?"
timestamp: "some time"
To keep it simple, let's get one post and the associated user and print out what that user said in their post.
let usersRef = self.ref.child("users")
let postsRef = self.ref.child("posts")
let postNum = "post_0"
let postToGetRef = postsRef.child(postNum)
postToGetRef.observeSingleEvent(of: .value, with: { postSnap in
let postDict = postSnap.value as! [String: Any]
let uid = postDict["author"] as! String
let postText = postDict["text"] as! String
let userToGetRef = usersRef.child(uid)
userToGetRef.observeSingleEvent(of: .value, with: { userSnap in
let userDict = userSnap.value as! [String: Any]
let userName = userDict["username"] as! String
print("\(userName) said \(postText)") //here you put the name in the title bar
})
})
and the output is
tyrek95 said Hello, World
I did this for a single post but it could be easily expanded by using .value on the posts node, which will read in all of the posts, and then iterate over them in a for..loop to get the post information and the user for each post.
Note there's no error checking here for brevity.
I have three functions getNewOrder(),storeOrderDetails(_ details:[String:String]) and getUserInfo(_ userID:String).
Function getNewOrder() is called first. It fetches new orders ( .childAdded values) and sends the dictionary to storeOrderDetails(_ details:[String:String]).
storeOrderDetails(_ details:[String:String])then segregate all the values and callsgetUserInfo(_ userID:String)` by passing it userID which was present in its details.
getUserInfo(_ userID:String) then fetches users details and returns user's
information.
However, the problem is [ userInfo = getUserInfo(_ userID:String) in storeOrderDetails(_ details:[String:String]) ] userInfo is always empty. Apparently func getUserInfo(_ userID:String) goes into a completion block after it has returned empty value.
I want these three functions to execute in sequential way.
Any advice is highly appreciated.
Please follow the below Links to review my code.
https://imgur.com/hNjvyDk
https://imgur.com/J0LMXMg
func childAdded(){
let ref = Database.database().reference().child("Orders").child(todaysDate)
ref.observe(.childAdded) { (snapshot) in
var details = [String:String]()
if let orderID = snapshot.key as? String {
ref.child(orderID).observeSingleEvent(of: .value, with: { (snap) in
self.newOrderTextView.text = ""
self.customerNameLabel.text = ""
self.customerPhoneLabel.text = ""
self.orderNumberLabel.text = ""
let enumerator = snap.children
while let rest = enumerator.nextObject() as? DataSnapshot {
details[rest.key as? String ?? ""] = rest.value as? String ?? ""
}
self.storeUserDetails(details)
})
}
}
}
func storeUserDetails(_ details:[String:String]){
if details["CustomerID"] != nil {
userInfo = getUserDetails(details["CustomerID"]!)
print(userInfo)
}
if !userInfo.isEmpty{
let order = OrderDatabase()
order.customerEmail = userInfo["Email"]!
order.customerName = userInfo["Name"]!
order.orderAcceptStatus = details["OrderStatus"]!
order.customerOrderNumber = details["orderNumber"]!
order.orderID = details["orderID"]!
order.time = details["Time"]!
order.customerFirebaseID = details["CustomerID"]!
self.orderDatabase[details["orderNumber"]!] = order
self.orderTable.reloadData()
}
}
func getUserDetails(_ userID:String) -> [String:String]{
var details = [String:String]()
let userDetailsReference = Database.database().reference().child("Users")
userDetailsReference.child(userID).observeSingleEvent(of: DataEventType.value, with: { (snapshot) in
if let dictionary = snapshot.value as? NSDictionary {
self.customerNameLabel.text = dictionary.value(forKey: "Name") as? String
self.customerPhoneLabel.text = dictionary.value(forKey: "Email") as? String
details["Name"] = dictionary.value(forKey: "Name") as? String
details["Email"] = dictionary.value(forKey: "Email") as? String
}
})
return details
}
From what I can see here, I am betting that the issue you are facing has to do with the fact that the methods are asynchronous. So one thing is not completely finished and some other method gets fired too soon. There are a few ways to deal with this issue. One is completion handlers, and the other is adding observers. Below is an example of doing both for Firebase. Here I'm asking a getLocationPhotos method to get all the photos from Firebase. Notice the observers and completion handler
func getLocationPhotos(coordinate:CLLocationCoordinate2D){
dbHandler.getImageFileNames(coordinateIn: coordinate) { (filenames) in
if filenames.isEmpty {
log.debug(String.warningGet + "filenames is empty")
return
}//if filenames.isEmpty
self.imageFiles = filenames.filter { $0 != "none" }
if self.imageFiles.isEmpty {
log.error(String.errorGet + "imageFiles array is empty")
return
}//if imageFiles.isEmpty
for file in self.imageFiles {
let reference = self.storageHandler.imageReference.child(file)
let download = self.imageView.sd_setImage(with: reference)
if let i = self.imageView.image {
self.imageArray.append(i)
self.collectionView.reloadData()
}//let i
download?.observe(.progress, handler: { (snapshot) in
guard let p = snapshot.progress else {
return
}//let p
self.progressView.progress = Float(p.fractionCompleted)
if self.progressView.progress == Float(1) {
self.progressView.isHidden = true
}
})//progress
download?.observe(.success, handler: { (snapshot) in
self.progressView.progress = 1
self.progressView.isHidden = true
self.collectionView.setNeedsLayout()
})//success
download?.observe(.failure, handler: { (snapshot) in
log.error(String.errorGet + "Error occured getting data from snapshot")
})//failure
}//for file
}//dbHandler
In order to populate my tableView, I append items (created from a struct) to a local array:
func loadList() {
var newAnnotations: [AnnotationListItem] = []
if let uid = Auth.auth().currentUser?.uid {
uidRef.child(uid).child("annotations").queryOrderedByKey().observeSingleEvent(of: .value, with: {snapshot in
for item in snapshot.children {
let annotationItem = AnnotationListItem(snapshot: item as! DataSnapshot)
newAnnotations.append(annotationItem)
}
annotationList = newAnnotations
self.tableView.reloadSections([0], with: .fade)
})
}
}
When I click a specific row, I am taken to a DetailViewController where it is only a large UITextView (named notes). The UITextView.text displayed is based on the selected indexPath.row and the "notes" value is retrieved from the array. Now the user is able to type some text and when they are done, the textViewDidEndEditing function is called:
func textViewDidEndEditing(_ textView: UITextView) {
notes.resignFirstResponder()
navigationItem.rightBarButtonItem = nil
let newNotes = self.notes.text
print(newNotes!)
}
Now I'd like to updateChildValues to newNotes to the child node "notes" in my JSON:
"users" : {
"gI5dKGOX7NZ5UBqeTdtu30Ze9wG3" : {
"annotations" : {
"-KuWIRBARv7osWr3XDZz" : {
"annotationSubtitle" : "1 Cupertino CA",
"annotationTitle" : "Apple Infinite Loop",
"notes" : "Does it work?!",
}
How can I access the selected autoID so I can update the specific notes node. So far the best I have is:
guard let uid = Auth.auth().currentUser?.uid else { return }
uidRef.child(uid).child("annotations").(somehow access the specific childID).updateChildValues(["notes": newNotes])
Any help will be greatly appreciated. Thanks in advance
UPDATE
The annotationListItem struct is created:
struct AnnotationListItem {
let key: String?
var annotationTitle: String?
let annotationSubtitle: String?
let notes: String?
let ref: DatabaseReference?
init(key: String = "", annotationTitle: String, annotationSubtitle: String, notes: String) {
self.key = key
self.annotationTitle = annotationTitle
self.annotationSubtitle = annotationSubtitle
self.notes = notes
self.ref = nil
}
init(snapshot: DataSnapshot) {
key = snapshot.key
let snapshotValue = snapshot.value as! [String: AnyObject]
annotationTitle = snapshotValue["annotationTitle"] as? String
annotationSubtitle = snapshotValue["annotationSubtitle"] as? String
notes = snapshotValue["notes"] as? String
ref = snapshot.ref
}
init(Dictionary: [String: AnyObject]) {
self.key = Dictionary["key"] as? String
self.annotationTitle = Dictionary["annotationTitle"] as? String
self.annotationSubtitle = Dictionary["annotationSubtitle"] as? String
self.notes = Dictionary["notes"] as? String
self.ref = nil
}
func toAnyObject() -> Any {
return [
"annotationTitle": annotationTitle as Any,
"annotationSubtitle": annotationSubtitle as Any,
"notes": notes as Any
]
}
}
UPDATE
This is how the annotationListItem is created to be stored in Firebase:
// Using the current user’s data, create a new AnnotationListItem that is not completed by default
let uid = Auth.auth().currentUser?.uid
guard let email = Auth.auth().currentUser?.email else { return }
let title = placemark.name
let subtitle = annotation.subtitle
let notes = ""
// declare variables
let annotationListItem = AnnotationListItem(
annotationTitle: title!,
annotationSubtitle: subtitle!,
notes: notes)
// Add the annotation under their UID
let userAnnotationItemRef = uidRef.child(uid!).child("annotations").childByAutoId()
userAnnotationItemRef.setValue(annotationListItem.toAnyObject())
I think you only need to do this:(since you have declared the note as global)
guard let uid = Auth.auth().currentUser?.uid else { return }
uidRef.child(uid).child("annotations").(note.key).updateChildValues(["notes": newNotes])
inside the method where you change the notes
If I am not mistaken you are creating an array of a custom object?
var newAnnotations: [AnnotationListItem] = []
You could do something like: var newAnnotations: [(key: String, value: [String : Any])] = [] (Any only if you are going to have Strings, Integers, ect. If it'll only be String then specify it as a String.
Accessing the key would be: newAnnotations[indexPath.row].key in your cellForRowAtIndex of your tableView. Accessing values would be: newAnnotations[indexPath.row].value["NAME"].
You can have a separate array that holds the key and just append it at the same time as your population:
for item in snapshot.children {
guard let itemSnapshot = task as? FDataSnapshot else {
continue
}
let id = task.key //This is the ID
let annotationItem = AnnotationListItem(snapshot: item as! DataSnapshot)
newAnnotations.append(annotationItem)
}
Another thing you could do is go up one more level in your firebase call:
if let uid = Auth.auth().currentUser?.uid {
uidRef.child(uid).child("annotations").observeSingleEvent(of: .value, with: {snapshot in
if snapshot is NSNull{
//Handles error
} else{
if let value = snapshot.value as? NSDictionary{ //(or [String: String]
//set localDictionary equal to value
}
}
self.tableView.reloadSections([0], with: .fade)
})
}
And then when you select a row: let selectedItem = localDictionary.allKeys[indexPath.row] as! String //This is the ID you pass to your viewController.
I have an issue with my least favourite part in Firebase. I want to pull a post from user's following list (every user has one and only one post). First, I created a completion handler to get a list of all followers from Firebase and store it in userArray array of strings:
func GetUsersInFollowing(completion: #escaping (Bool) -> ()) {
ref.child("following").queryOrdered(byChild: FIRAuth.auth()!.currentUser!.uid).observeSingleEvent(of: .value, with: { (snapshot) in
for group in snapshot.children {
self.userArray.append((group as AnyObject).key)
}
completion(true)
})
}
Now the plan is to pull a post from every element of userArray.
Here is where the problem starts. I call CreatePosts() immediately after GetUsersInFollowing() completes.
func CreatePosts() {
for x in userArray {
var thePost = Post()
print("1")
self.ref.child("users").child(x).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
thePost.fullName = value?["fullname"] as? String ?? ""
thePost.username = value?["username"] as? String ?? ""
thePost.profileImageURL = value?["photourl"] as? String ?? ""
print("2")
})
self.ref.child("posts").child(x).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
thePost.description = value?["description"] as? String ?? ""
thePost.info = value?["location"] as? String ?? ""
thePost.postImageURL = value?["photoURL"] as? String ?? ""
thePost.timePost = value?["timestamp"] as? NSDate
thePost.upVotes = value?["upvotes"] as? Int ?? 0
})
self.postArray.append(thePost)
self.tableView.reloadData()
}
}
Everything looks ok to me, but it surely isn't. Here's how I create cells:
func configureCell(post: Post) {
self.post = post
self.username.text = post.username
self.profileImage = post.profileImageURL
print("3")
self.fullname.text = post.fullName
self.timestamp.text = post.timePost
self.upvotes.text = post.upVotes
self.location.text = post.location
self.descriptionText.text = post.description
}
The output in the console varies, but usually I get:
1
1
3
3
2
2
The idea is to first retrieve all data from Firebase, add it to post object, append the object to the array and then create cell for that object with data downloaded. Cell is already created even though data is not retrieved. I think that is the problem. Thank you, every suggestion is appreciated.
You need to inner query for combining both user profile data and post data.
Like this -
func CreatePosts() {
//Using userPostArrayObjFetched as a counter to check the number of data fetched.
//Remove this code, if you don't want to wait till all the user data is fetched.
var userPostArrayObjFetched = 0
for (index,userID) in userArray.enumerated() {
print("1" + userID)
self.ref.child("users").child(userID).observeSingleEvent(of: .value, with: { (snapshot) in
var thePost = Post()
let value = snapshot.value as? NSDictionary
thePost.fullName = value?["fullname"] as? String ?? ""
thePost.username = value?["username"] as? String ?? ""
thePost.profileImageURL = value?["photourl"] as? String ?? ""
print("2" + userID)
self.ref.child("posts").child(userID).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
thePost.description = value?["description"] as? String ?? ""
thePost.info = value?["location"] as? String ?? ""
thePost.postImageURL = value?["photoURL"] as? String ?? ""
thePost.timePost = value?["timestamp"] as? NSDate
thePost.upVotes = value?["upvotes"] as? Int ?? 0
print("3" + userID)
self.postArray.append(thePost)
// Uncomment if you want to reload data as fetched from Firebase without waiting for all the data to be fetched.
// self.tableView.reloadData()
userPostArrayObjFetched += 1
if userPostArrayObjFetched == userArray.count{
self.tableView.reloadData()
}
})
})
}
}
I am trying to display the user data in screen. But I always get an empty value. I don't know why.
var profileData = Profile(usrObj: [String:String]())
#IBOutlet weak var userName: UILabel!
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(true)
self.userName.text = profileData.FirstName
print(profileData.FirstName)
}
My print statement and my label value are empty. Please help me out with any mistake I am making.
My model class :
class Profile {
var FirstName: String
init(usrObj : [String: AnyObject]) {
self.FirstName = (usrObj["FirstName"] ?? "") as! String
}
var ProfileObject: [String:AnyObject] {
return ["FirstName" : self.FirstName]
}
In your LoginViewController save your data in NSUserDefaults
#IBAction func loginWithUserNamePassword(){
KRProgressHUD.show(progressHUDStyle: .White, message: "Loading...")
loginWithMailAndPassword((username.text?.trimWhiteSpace)!, password: (password.text?.trimWhiteSpace)!) { (user, error) in
if error != nil{
KRProgressHUD.dismiss()
SCLAlertView().showError("Login Error", subTitle: error!.localizedDescription)
}
else {
if user!.emailVerified
{
currentUser = user
fireBaseRef.child("Users").child(currentUser!.uid).child("UserProfile").observeSingleEventOfType(.Value, withBlock: { (snapshot) in
if let data: [String : AnyObject] = snapshot.value as? [String : AnyObject] {
let userDefaults = NSUserDefaults.standardUserDefaults()
userDefaults.setObject(data, forKey: "userdata")
userDefaults.synchronize()
enableSync()
self.navigateToNextScreen()
}
else{
}
})
}
else
{
SCLAlertView().showError("Login Error", subTitle: "This email is has not been verified yet")
}
}
}
}
and use that data in UserStaticDataViewController
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(true)
self.profileDetailsExists = true
let userdata : NSDictionary = NSUserDefaults.standardUserDefaults().valueForKey("userdata") as! NSDictionary
print(userdata["City"] as? String)
self.userName.text = userdata["FirstName"] as? String
self.userCity!.text = userdata["City"] as? String
self.userCountry!.text = userdata.valueForKey("Country") as? String
self.userState.text = userdata.valueForKey("State") as? String
self.userMobileNo.text = userdata.valueForKey("Mobile") as? String
self.userGmail.text = userdata.valueForKey("Email") as? String
self.userDob.text = userdata.valueForKey("DateOfBirth") as? String
}
Output:
Just approve my answer and give vote.
Happy coding.
From the fact that your print statement is empty, it means that the userObject you pass into the following equation
var profileData = Profile(usrObj: [String:String]())
probably does not have a string value for the key "firstName".
You can try to verify this by changing your code
self.FirstName = (usrObj["FirstName"] ?? "") as! String
to
self.FirstName = (usrObj["FirstName"] ?? "Hello world") as! String
and see if "Hello world" is printed out in your console.
If yes, then you just have to make sure that the "user" object you pass into the init function of Profile class should be a dictionary where a value is stored for the key "FirstName"