I´m new to Swift or rather the whole programming world and I´m trying to learn how to program in Swift.
I got a table view with cells (VC1) which contain different information provided by a user. I want to click on a cell to get to a new VC by segue. This new VC should display the user information of the user who posted the information. I´m passing the userID from VC1 to VC2 by segue.
In VC2 I want to download the user information using the userID.
I´m using a UserModel
import Foundation
class UserModel {
var username: String?
var email: String?
var profilImageUrl: String
var birthdayDate: String?
var gender: String?
init(dictionary: [String: Any]) {
username = dictionary["username"] as? String
email = dictionary["email"] as? String
profilImageUrl = dictionary["profilImageURL"] as? String ?? ""
birthdayDate = dictionary["Geburtsdatum"] as? String
gender = dictionary["gender"] as? String
}
}
Passing the userID VC1 -> VC2 works, I checked it using the print command
My Code from VC2:
//MARK: - Outlets
#IBOutlet weak var nameLabel: UILabel!
//MARK: Var/ Let
var event: EventModel?
var users = [UserModel]()
var user: UserModel?{
didSet {
guard let _username = user?.username else {return}
nameLabel.text = _username
}
}
func loadUserDeatails(){
guard let userID = event?.uid else {return}
fetchUser(uid: userID)
}
func fetchUser(uid: String) {
let userRef = Database.database().reference().child("users").child(uid)
userRef.observeSingleEvent(of: .value) { (snapshot) in
guard let dic = snapshot.value as? [String: Any] else {return}
let newUser = UserModel(dictionary: dic)
self.users.append(newUser)
}
}
override func viewDidLoad() {
super.viewDidLoad()
loadUserDeatails()
print(users)
}
the nameLabel is empty as well as the array(users) which I tried to print in ViewDidLoad.
Var "dic" is containing the user details I downloaded with "guard let userID = event?.uid" so I guess that should be right.
Hopefully someone can help me :)
I'm not sure what event is doing, but one approach is:
In VC1:
In prepareforsegue, once you get the reference of VC2,
let vc2 = segue.destination as! VC2
vc2.userId = "<USER_ID>" // Pass userId to VC2
In VC2: didSet of var userId would do the fetch, then assign username to nameLabel.text directly.
var userId: String? {
didSet {
// Do the fetch user details here
guard let userId = userId else {return}
let userRef = Database.database().reference().child("users").child(userID)
userRef.observeSingleEvent(of: .value) { (snapshot) in
guard let dic = snapshot.value as? [String: Any] else {return}
let newUser = UserModel(dictionary: dic)
// Once your get user detail data, set the label text
nameLabel.text = newUser["username"] ?? ""
}
}
}
Try the following, it might work.
var user: UserModel?{
didSet {
guard let _username = user?.username else {return}
guard let userID = event?.uid else {return}
let userRef = Database.database().reference().child("users").child(userID)
userRef.observeSingleEvent(of: .value) { (snapshot) in
guard let dic = snapshot.value as? [String: Any] else {return}
let newUser = UserModel(dictionary: dic)
nameLabel.text = newUser["username"] ?? ""
}
}
}
Related
I have written a firebase query that searches for the level of a user and then changes the level-label on the view controller. However, this query is giving me level for the first user in my Firebase db instead of the level for the current user. How can I resolve this issue?
#IBOutlet weak var levelLabel: UILabel!
var refUser:DatabaseReference?
override func viewDidLoad() {
super.viewDidLoad()
refUser = Database.database().reference().child("userInfo");
let userID = Auth.auth().currentUser!.uid
let query = refUser?.queryOrdered(byChild: "userId").queryEqual(toValue: "\(userID)")
query?.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let childSnap = child as! DataSnapshot
var dict = childSnap.value as! [String: Any]
let level=dict["level"] as! String
self.levelLabel.text=level
}
})
}
Photo of Firebase DB
try in such way:
query?.observeSingleEvent(of: .value, with: { snapshot in
guard let values = snapshot.value as? [String: Any],
let level = values["level"] as? String else {
return
}
DispatchQueue.main.async {
self.levelLabel.text = level
}
})
How can I get name, quant from this structure:
Im try this:
var handle: DatabaseHandle?
var ref: DatabaseReference?
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
handle = ref?.child("Цитаты").child("Из Книг").child("Дорогой звезд").child("Quote1").observe(.childAdded, with: { (snapshot) in
let user = snapshot.value
print(user!) //text from name + 10 + Quote1
}
When I print snapshot.value it's give me data from Quote1 , but I want to show name in textField and quant in label I try this:
ref = Database.database().reference()
handle = ref?.child("Цитаты").child("Из Книг").child("Дорогой звезд").child("Quote1").observe(.childAdded, with: { (snapshot) in
let user = snapshot.value
if let us = user!["name"] as? String {
if let quant = user!["quant"] as? String{
self.quoteTextField.text = us
self.quotelabel.text = quant
}
}
And have error. How can I add data from snapshot.value to label and textField?
As I was checking your screen, quant is an Int not String, so try this
ref = Database.database().reference()
handle = ref?.child("Цитаты").child("Из Книг").child("Дорогой звезд").child("Quote1").observe(.childAdded, with: { (snapshot) in
//let username = snapshot.value as? NSDictionary
let user = snapshot.value as? [String: Any]
if let us = user!["name"] as? String {
if let quant = user!["quant"] as? Int {
self.quoteTextField.text = us
self.quotelabel.text = quant.description
}
}
}
You need to cast the snapshot.value as a dictionary -
let user = snapshot.value as? NSDictionary
You should also consider safely unwrapping your optionals -
let name = user?["name"] as? String ?? ""
let quant = user?["quant"] as? Int ?? 0
Please find my code below. How can we append filter data on array from Firebase?
var childrenList = [DatabaseList]()
let ref = Database.database().reference(withPath: "Messages")
let query = ref.queryOrdered(byChild: "VideoID").queryEqual(toValue: "12345").observe(.value, with: { (snapshot) in
for childSnapshot in snapshot.children{
print(childSnapshot)
self.childrenList.append(snapshot)
}
})
DispatchQueue.main.async {
self.tableView.reloadData()
}
let ref = Database.database().reference(withPath: "Messages")
let query = ref.queryOrdered(byChild: "VideoID").queryEqual(toValue: "12345").observe(.value, with: { (snapshot) in
print(snapshot)
for (childSnapshotId, childSnapshotValue) in snapshot {
if let dataListDict = childSnapshotValue as? [String: AnyObject] {
//Init you newModel with the dataListDict here
let newModel = DatabaseList(dict: dataListDict)
print(childSnapshot)
self.childrenList.append(newModel)
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
class DatabaseList : NSObject {
var messageBody : String?
var name : String?
var videoID : String?
init(dict: [String: AnyObject]) {
messageBody = dict["MessageBody"]
name = dict["Name"]
videoID = dict["videoID"]
}
}
Your query is correct but there are few mistakes in finishing block.
self.childrenList.append(snapshot) snapshot is an instance of DataSnapshot not a DatabaseList so you can not append it like this.
for childSnapshot in snapshot.children {
/// childSnapshot is an instance of DataSnapshot not a dictionary but its value will be
guard let data = (childSnapshot as! DataSnapshot).value else {continue}
let dataDict = data as! Dictionary<String, Any>
/// Initializing the new object of DatabaseList and passing the values from data
let list: DatabaseList = DatabaseList()
list.messageBody = dataDict["MessageBody"] as? String
list.name = dataDict["Name"] as? String
list.videoID = dataDict["VideoID"] as? String
/// This is correct, and now you can append it to your array.
childrenList.append(list)
}
Apart from this you will have to reload the tableView inside the finishing block not below the block because this is an asynchronous request and data will come later.
Also its always better to check the data existence. snapshot.exists().
One more suggestion if you want to fetch the data just once then do not use .observe use .observeSingleEvent instead. .observe will fire the block every time there is any change at this node.
Here is the full code snippet.
let query = ref.queryOrdered(byChild: "VideoID").queryEqual(toValue: "12345").observe(.value, with: { (snapshot) in
if !snapshot.exists() {
// Data doesn't exist
return
}
for childSnapshot in snapshot.children {
guard let data = (childSnapshot as! DataSnapshot).value else {continue}
let dataDict = data as! Dictionary<String, Any>
let list: DatabaseList = DatabaseList()
list.messageBody = dataDict["MessageBody"] as? String
list.name = dataDict["Name"] as? String
list.videoID = dataDict["VideoID"] as? String
childrenList.append(list)
}
/// Reload your tableView here
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
And expecting the class model like below:
class DatabaseList: NSObject {
var messageBody: String?
var name: String?
var videoID: String?
}
I want to use variable_a in another function. Actually, I want to load these data into tableviewcell.
func readFIRData() {
var credentials:[String]
let userID = Auth.auth().currentUser?.uid
ref = Database.database().reference().child("usr").child(userID!)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let Name = value?["firstName"] as? String ?? ""
let PhoneNo = value?["mobile"] as? String ?? ""
var variable_a = [Name,PhoneNo]
self.tableView.reloadData()
}) { (error) in
}
}
You should have a callback (completion handler) in your readFIRData function, and pass variable_a as a parameter in that callback. Parse it into object which you use in table view, and reload tableView in the callback.
Function should look like this:
func readFIRData(_ completion: ([Name,PhoneNo]) -> ()) {
var credentials:[String]
let userID = Auth.auth().currentUser?.uid
ref = Database.database().reference().child("usr").child(userID!)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let Name = value?["firstName"] as? String ?? ""
let PhoneNo = value?["mobile"] as? String ?? ""
var variable_a = [Name,PhoneNo]
completion(variable_a)
}) { (error) in
}
}
and then have another function which is going to call readFIRData function:
func requestData() {
readFIRData() { [weak self] data in
guard let `self` = self else { return }
self.data = data
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
Mention how you get back to main thread to reload tableView. By self.data I assumed data which you will use in table view to instantiate cells.
Solved this way
func GetData(completion: #escaping(_ credentials: [String]) -> Void) {
let userID = Auth.auth().currentUser?.uid
ref = Database.database().reference().child("usr").child(userID!)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let Name = value?["firstName"] as? String ?? ""
let PhoneNo = value?["mobile"] as? String ?? ""
var variable_a = [Name,PhoneNo]
completion(variable_a)
self.tableView.reloadData()
}) { (error) in
}
}
Now, assign StringArray to some variable. Use below code to assign. Likewise variable StringArrayVariable can be used to populate tableview cells.
GetData { (StringArray) in
self.StringArrayVariable= StringArray
}
I would like to get some data in user profile, I've got some info but I got stuck at this one.
Firebase database:
Here is some of my code:
self.ref.child("users").child("profile").observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String:Any] {
let user = User()
let brands = dictionary["status"] as! NSDictionary
user.displayname = dictionary["displayname"] as? String
user.isconnected = brands["isconnected"] as? String
print(user.isconnected) //fatal error: unexpectedly found nil while unwrapping an Optional value
class User: NSObject {
var displayname: String?
var isconnected: String?
}
Try something like that :
guard let dictionary = snapshot.value as? [String: Any] else {
return
}
guard let statusDictionary = dictionary["status"] as? [String: Any] else {
return
}
guard let deviceStatusDictionary = statusDictionary["DEVICE_KEY"] as? [String: Any] else {
return
}
let user = User()
user.displayname = dictionary["displayname"] as? String
user.isconnected = deviceStatusDictionary["isconnected"] as? Bool
You need to know your device key.
And :
class User: NSObject {
var displayname: String?
var isconnected: Bool?
}