I have two models: a User
model and a book Review
model. My username
in the Review is of type User
so that I can retrieve a given review and the user who created it.
I can now retrieve the Review but not the User object with my code. What am I doing wrong?
User Class
class User: NSObject {
var id: String?
var name: String?
var email: String?
var profileImageUrl: String?
init(dictionary: [String: AnyObject]) {
self.id = dictionary["id"] as? String
self.name = dictionary["name"] as? String
self.email = dictionary["email"] as? String
self.profileImageUrl = dictionary["profileImageUrl"] as? String
}
}
Review Struct
struct Review {
let id: String
let bookName: String
let author: String
let review: String
let username: User
let createdAt: Date
init(id: String, bookName: String, author: String, review: String, username: User, timestamp: Double) {
self.id = id
self.bookName = bookName
self.author = author
self.review = review
self.username = username
self.createdAt = Date(timeIntervalSince1970: timestamp / 1000)
}
}
Review Controller
private func fetchReviews() {
let reviewRef = Database.database().reference().child("reviews")
let queryRef = reviewRef.queryOrdered(byChild: "timestamp").queryLimited(toLast: 5)
queryRef.observe(.value, with: { snapshot in
var temporaryReviews = [Review]()
for child in snapshot.children {
if let childSnapshot = child as? DataSnapshot,
let dictionary = childSnapshot.value as? [String:Any],
let user = dictionary["user"] as? [String:Any],
let id = user["id"] as? String,
let name = user["name"] as? String,
let profileImageUrl = user["profileImageUrl"] as? String,
let url = URL(string: profileImageUrl),
let bookName = dictionary["bookName"] as? String,
let author = dictionary["author"] as? String,
let review = dictionary["review"] as? String,
let timestamp = dictionary["timestamp"] as? Double {
let user = User(dictionary: <#T##[String : AnyObject]#> ??? // Cannot use a user object
let review = Review(id: childSnapshot.key, bookName: bookName, author: author, review: review, username: user, timestamp: timestamp)
DispatchQueue.main.async {
temporaryReviews.insert(review, at: 0)
self.reviews = temporaryReviews
self.tableView.reloadData()
}
}
}
})
}
If I remove the user and its associating values from the loop, I get the data in the UI but no username
or photoImageURL
which I am updating in my view class. The data is already in Firebase. How can I read the data and the user?
It's not clear what is expected of this line
let user = User(dictionary: <#T##[String : AnyObject]#> ???
and also not sure why class User: NSObject is an NSObject but this will work with Swift 5.
class User { //using pure Swift, remove NSObject
var id: String?
var name: String?
var email: String?
var profileImageUrl: String?
init(dictionary: [String: Any]) { //change AnyObject to Any
self.id = dictionary["id"] as? String
self.name = dictionary["name"] as? String
self.email = dictionary["email"] as? String
self.profileImageUrl = dictionary["profileImageUrl"] as? String
}
}
then when reading the Firebase data in the closure create a Dictionary of the items you want to populate the user class with
let id = user["id"] as? String,
let name = user["name"] as? String,
let profileImageUrl = user["profileImageUrl"] as? String,
let url = URL(string: profileImageUrl),
let dict: [String: Any] = [
"id": id,
"name": name,
"email": email,
"profileImageUrl": profileImageUrl
]
let aUser = User(dictionary: dict)
Or you could just pass the snapshot to the User class and let it get whatever it needs from it
class User {
var user_id = ""
var name = ""
var email = ""
var profileImageUrl = ""
init(withSnap: DataSnapshot) {
self.user_id = withSnap.childSnapshot(forPath: "id").value as? String ?? "No Id"
self.name = withSnap.childSnapshot(forPath: "name").value as? String ?? "No name"
self.email = withSnap.childSnapshot(forPath: "email").value as? String ?? "No email"
self.profileImageUrl = withSnap.childSnapshot(forPath: "profileImageUrl").value as? String ?? "No url"
}
}
and then
let aUser = User(withSnap: snapshot)
Also, in your code, the tableView is being reloaded each loop iteration and that may cause flicker. Best bet is to move the self.tableView.reloadData()
outside the for loop so it only executes once.