How can I pass the string userImage which I get from a firebase realtime database request? I can not just use return userImage. It says Unexpected non-void return value in void function. I want the userImage as result and paste it to XXXXXX.
...
func datenBankAbfrage() {
print("datenbankabfrage getriggert")
dic = [:]
for name in myFeed.myArray1[0..<myFeed.myArray1.count] {
ref = Database.database().reference().child("placeID/\(name)")
ref.observe(.value) { (snapshot) in
self.dic[name] = []
var specificNameVideos = [importComment]()
for video in snapshot.children.allObjects as! [DataSnapshot] {
let Object1 = video.value as? [String: AnyObject]
let timestampDate = NSDate(timeIntervalSince1970: Double(Object1?["userTime"] as! NSNumber)/1000)
let dateFormatter = DateFormatter()
dateFormatter.timeZone = NSTimeZone.local
dateFormatter.dateFormat = "dd.MM.YY hh:mm"
let time = dateFormatter.string(from: timestampDate as Date)
print(time + "Uhr")
let userName = Object1?["userName"]
...
let kommentarCount = Object1?["kommentarCount"]
let userID = Object1?["userID"]
//print(userID!)
ViewComments.commentIDNew = commentId as! String
func wertBerechnen(completion: #escaping (String) -> Void) {
self.ref = Database.database().reference()
self.ref.child("user/\(userID!)").observeSingleEvent (of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let userImage = value?["picture"] as? String ?? ""
completion(userImage)
})
}
wertBerechnen { userImage in
let video = importComment(
userName: userName as! String, userGroup: userGroup as! String,
userComment: userComment as! String, userTime: userTime as! String,
userLikes: userLikes as! Int, commentId: commentId as! String, placeID: placeID as! String, kommentarCount: kommentarCount as? Int, userImage: userImage)
specificNameVideos.append(video)
}
}
self.dic[name] = specificNameVideos
let tem = Array(self.dic.values).flatMap { $0 } // You can also sort allComments with userTime property
self.table = tem.sorted { $0.userTime! > $1.userTime! }
self.tableView.reloadData()
}
myFeed.myArray1 = []
}
...
You can use a completion handler to return the userImage string:
func wertBerechnen(completion: #escaping (String) -> Void) {
self.ref = Database.database().reference()
self.ref.child("user/\(userID!)").observeSingleEvent (of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let userImage = value?["picture"] as? String ?? ""
completion(userImage)
})
}
Then you can call it like this:
wertBerechnen { userImage in
let video = importComment(
userName: userName as! String, userGroup: userGroup as! String,
userComment: userComment as! String, userTime: userTime as! String,
userLikes: userLikes as! Int, commentId: commentId as! String, placeID: placeID as! String, kommentarCount: kommentarCount as? Int, userImage: userImage)
specificNameVideos.append(video)
}
self.dic[name] = specificNameVideos
let tem = Array(self.dic.values).flatMap { $0 }
self.table = tem.sorted { $0.userTime! > $1.userTime! }
self.tableView.reloadData()
}
}
You need to reload the tableView data inside the wertBerechnen completion handler:
func wertBerechnen(completion: #escaping (String) -> Void) {
self.ref = Database.database().reference()
self.ref.child("user/\(userID!)").observeSingleEvent (of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let userImage = value?["picture"] as? String ?? ""
completion(userImage)
})
}
Then you can call it like this:
wertBerechnen { userImage in
let video = importComment(
userName: userName as! String, userGroup: userGroup as! String,
userComment: userComment as! String, userTime: userTime as! String,
userLikes: userLikes as! Int, commentId: commentId as! String, placeID: placeID as! String, kommentarCount: kommentarCount as? Int, userImage: userImage)
specificNameVideos.append(video)
self.tableView.reloadData()
}
self.dic[name] = specificNameVideos
let tem = Array(self.dic.values).flatMap { $0 }
self.table = tem.sorted { $0.userTime! > $1.userTime! }
}
}
Related
In my App I want to show a feed which generates a tableView depending on which groups the user subscribed. In the background two Firebase request are done and then converted to a tableView.
My Code shows the content correct. But there are two issues. The order is wrong. It should be 4->3->2->1 (based on timestamp) and right now it is random. Also if I click on a comment the didSeleceRowAt works totally wrong.
How can I manage to put the result in the same tableView correctly?
func datenBankAbfrage() {
ref = Database.database().reference().child("placeID/h77e24d95a5479ed7588")
ref.queryOrdered(byChild: "userTime").queryLimited(toLast: 10).observe(
DataEventType.value,
with: { (snapshot) in
self.ref = Database.database().reference().child(
"placeID/vh-b83b6e4475e04e3fbaa647d23b")
self.ref.queryOrdered(byChild: "userTime").queryLimited(toLast: 10).observe(
DataEventType.value,
with: { (snapshot2) in
for video in snapshot.children.allObjects as! [DataSnapshot] {
let Object1 = video.value as? [String: AnyObject]
let userName = Object1?["userName"]
let userGroup = Object1?["userGroup"]
let userComment = Object1?["userComment"]
let userTime = Object1?["userTime"]
let userLikes = Object1?["userLikes"]
let commentId = Object1?["commentId"]
ViewComments.commentIDNew = commentId as! String
let video = importComment(
userName: userName as! String, userGroup: userGroup as! String,
userComment: userComment as! String, userTime: userTime as! Int,
userLikes: userLikes as! Int, commentId: commentId as! String)
self.table.insert(video, at: 0)
// self.table.append(video)
self.tableView.reloadData()
}
for video in snapshot2.children.allObjects as! [DataSnapshot] {
let Object2 = video.value as? [String: AnyObject]
let userName = Object2?["userName"]
let userGroup = Object2?["userGroup"]
let userComment = Object2?["userComment"]
let userTime = Object2?["userTime"]
let userLikes = Object2?["userLikes"]
let commentId = Object2?["commentId"]
ViewComments.commentIDNew = commentId as! String
let video2 = importComment(
userName: userName as! String, userGroup: userGroup as! String,
userComment: userComment as! String, userTime: userTime as! Int,
userLikes: userLikes as! Int, commentId: commentId as! String)
self.table.insert(video2, at: 0)
// self.table.append(video)
self.tableView.reloadData()
}
})
})
}
class importComment {
var userName: String?
var userID: String?
var userGroup: String?
var userComment: String?
var userTime: Int?
var userLikes: Int?
var commentId: String?
init(
userName: String?, userGroup: String?, userComment: String?, userTime: Int?,
userLikes: Int?, commentId: String?
) {
self.userName = userName
self.userGroup = userGroup
self.userComment = userComment
self.userTime = userTime
self.userLikes = userLikes
self.commentId = commentId
}
}
No need to fetch data in Loops. As you're calling asynchronous functions in a loop, there is no guarantee of order.
You should use DispatchGroup. Here is how execution will go:
Create a DispatchGroup instance.
Add SnapShot-1 Fetch request to the Dispatch group.
Now add SnapShot-2 Fetch request to the Dispatch group.
Notify() will only be called after both the tasks has been completed. So you should prepare your data there.
Reload Table after data preparation.
Here is an example implementation:
func datenBankAbfrage() {
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
ref = Database.database().reference().child("placeID/h77e24d95a5479ed7588")
ref.queryOrdered(byChild: "userTime").queryLimited(toLast: 10).observe(
DataEventType.value,
with: { (snapshot) in
for video in snapshot.children.allObjects as! [DataSnapshot] {
let Object1 = video.value as? [String: AnyObject]
let userName = Object1?["userName"]
let userGroup = Object1?["userGroup"]
let userComment = Object1?["userComment"]
let userTime = Object1?["userTime"]
let userLikes = Object1?["userLikes"]
let commentId = Object1?["commentId"]
ViewComments.commentIDNew = commentId as! String
let video = importComment(
userName: userName as! String, userGroup: userGroup as! String,
userComment: userComment as! String, userTime: userTime as! Int,
userLikes: userLikes as! Int, commentId: commentId as! String)
self.table.append(video)
}
dispatchGroup.leave()
})
dispatchGroup.enter()
self.ref = Database.database().reference().child(
"placeID/vh-b83b6e4475e04e3fbaa647d23b")
self.ref.queryOrdered(byChild: "userTime").queryLimited(toLast: 10).observe(
DataEventType.value,
with: { (snapshot2) in
for video in snapshot2.children.allObjects as! [DataSnapshot] {
let Object2 = video.value as? [String: AnyObject]
let userName = Object2?["userName"]
let userGroup = Object2?["userGroup"]
let userComment = Object2?["userComment"]
let userTime = Object2?["userTime"]
let userLikes = Object2?["userLikes"]
let commentId = Object2?["commentId"]
ViewComments.commentIDNew = commentId as! String
let video2 = importComment(
userName: userName as! String, userGroup: userGroup as! String,
userComment: userComment as! String, userTime: userTime as! Int,
userLikes: userLikes as! Int, commentId: commentId as! String)
self.table.append(video)
}
dispatchGroup.leave()
})
dispatchGroup.notify(queue: .main) {
// This is invoked when all the tasks in the group is completed.
self.prepareDatasource()
}
}
func prepareDatasource() {
self.table.sort(by: { $0.userTime ?? 0 > $1.userTime ?? 1}) // Sorting data
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
I want to read all three data sourcing from "Arts & Humanities" and "Beauty & Style". Is this possible?
Let ref = Database.database().reference().child("posts")
//CODE A: Pulls 2 snapshot, but doesn't display anything
let ref = Database.database().reference().child("posts").child("Arts & Humanities")
//CODE B: only pulls up the two feeds but excludes beauty and style. Vice versa
//Below is the listener code I have. This works only works with CODE B above; but ideally id like to read the post under "Beauty & Style" as well.
postsRef.observeSingleEvent(of: .value, with: { snapshot in
var tempPosts = [PostModel]()
for child in snapshot.children {
print(snapshot.childrenCount)
if let childSnapshot = child as? DataSnapshot,
let dict = childSnapshot.value as? [String:Any],
let author = dict["author"] as? [String:Any],
let uid = author["uid"] as? String,
let username = author["username"] as? String,
let fullname = author["fullname"] as? String,
let patthToImage = author["patthToImage"] as? String,
let url = URL(string:patthToImage),
let pathToImage = dict["pathToImage"] as? String,
let likes = dict["likes"] as? Int,
let postID = dict["postID"] as? String,
let message = dict["message"] as? String,
let genre = dict["genre"] as? String,
let timestamp = dict["timestamp"] as? Double {
if childSnapshot.key != lastPost?.id {
let userProfile = UserProfile(uid: uid, fullname: fullname, username: username, patthToImage: url)
let post = PostModel(genre: genre, likes: likes, message: message, pathToImage: pathToImage, postID: postID, userID: pathToImage, timestamp: timestamp, id: childSnapshot.key, author: userProfile)
tempPosts.insert(post, at: 0)
if lastPost?.id != nil {
lastPostIdChecker = lastPost!.id
}
}
}
}
return completion(tempPosts)
})
I am fetching user information from Firebase and my goal is to fetch a batch of users and then display them on the screen one at a time in a card stack. To do this i use a completion handler. However my code inside the completion handler runs before the fetch of all users is done.
Thank you for any help.
Here is my code. I want "fetchOneUser()" to run when "fetchAllUsers" is done:
fetchAllUsers(completion: { message in
print(message)
print("FetchOneUser")
self.fetchOneUser()
})
Here is fetchAllUser function:
func fetchAllUsers(completion: #escaping (_ message: String) -> Void){
//User or advertiser?
Database.database().reference(withPath: "Advertiser").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists(){
myAdvertiserVar.advertiser = true
self.currentUserKind = "Advertiser"
self.otherUserKind = "Users"
}
else{
self.currentUserKind = "Users"
self.otherUserKind = "Advertiser"
}
// Fetch
let query = self.ref?.child(self.otherUserKind).queryOrdered(byChild: "email")
query?.observeSingleEvent(of: .value) {
(snapshot) in
for child in snapshot.children.allObjects as! [DataSnapshot] {
let id = child.key
//If Already Accepted, don't fetch
Database.database().reference(withPath: self.currentUserKind).child(self.uid).child("Accepted").child(id).observeSingleEvent(of: .value, with: {(accepted) in
if accepted.exists(){
print("\(id) är redan Accepted")
}
else{
if myAdvertiserVar.advertiser == true{
let value = child.value as? NSDictionary
let username = value?["Username"] as? String
let occupation = value?["Occupation"] as? String
let age = value?["Age"] as? String
let bio = value?["Bio"] as? String
let email = value?["email"] as? String
let user = User(id: id, username: username, occupation: occupation, age: age, bio: bio, email: email)
self.usersArray.append(user)
}
else{
let value = child.value as? NSDictionary
let username = value?["Owner"] as? String
let occupation = value?["Location"] as? String
let age = value?["Rent"] as? String
let bio = value?["About"] as? String
let email = value?["email"] as? String
let user = User(id: id, username: username, occupation: occupation, age: age, bio: bio, email: email)
self.usersArray.append(user)
}
}
})
}
print(self.usersArray.count)
completion("Users list fetched")
}
})
}
You need to use DispatchGroup as the inner calls are asynchronous
func fetchAllUsers(completion: #escaping (_ message: String) -> Void){
//User or advertiser?
Database.database().reference(withPath: "Advertiser").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists(){
myAdvertiserVar.advertiser = true
self.currentUserKind = "Advertiser"
self.otherUserKind = "Users"
}
else{
self.currentUserKind = "Users"
self.otherUserKind = "Advertiser"
}
// Fetch
let query = self.ref?.child(self.otherUserKind).queryOrdered(byChild: "email")
query?.observeSingleEvent(of: .value) {
(snapshot) in
let g = DispatchGroup()
for child in snapshot.children.allObjects as! [DataSnapshot] {
let id = child.key
//If Already Accepted, don't fetch
g.enter()
Database.database().reference(withPath: self.currentUserKind).child(self.uid).child("Accepted").child(id).observeSingleEvent(of: .value, with: {(accepted) in
if accepted.exists(){
print("\(id) är redan Accepted")
}
else{
if myAdvertiserVar.advertiser == true{
let value = child.value as? NSDictionary
let username = value?["Username"] as? String
let occupation = value?["Occupation"] as? String
let age = value?["Age"] as? String
let bio = value?["Bio"] as? String
let email = value?["email"] as? String
let user = User(id: id, username: username, occupation: occupation, age: age, bio: bio, email: email)
self.usersArray.append(user)
}
else{
let value = child.value as? NSDictionary
let username = value?["Owner"] as? String
let occupation = value?["Location"] as? String
let age = value?["Rent"] as? String
let bio = value?["About"] as? String
let email = value?["email"] as? String
let user = User(id: id, username: username, occupation: occupation, age: age, bio: bio, email: email)
self.usersArray.append(user)
}
}
g.leave()
})
}
g.notify(queue: .main, execute: {
print(self.usersArray.count)
completion("Users list fetched")
})
}
})
}
Based on Firebase documentation:
Firebase use refrence() method to get a database refrence for the root of your real time database asynchronous.
this means that result takes more time to fetch than for loop, in this situation your for loop finishes and completion block calls and takes you out of method, then result of your request will return.
your code should look like
var firebaseDatabaseRefrence: DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
Database.database().reference(withPath: self.currentUserKind)
}
func someMethod() {
self.firebaseDatabaseRefrence
.child(self.uid)
.child("Accepted")
.child(id).observeSingleEvent(of: .value, with: {(accepted) in
}
after a lot of research on the net, I tried several solutions but it does not work? I want to sort my array according to the leaguecode property here is my code:
var teams = [Team]()
dbReference.child(FBDatabaseKeys.teams).observeSingleEvent(of: .value) { (snapshot) in
for nameTeam in snapshot.children.allObjects as! [DataSnapshot] {
let value = nameTeam.value as? NSDictionary
let team = Team(
capitanId: value?[FBDatabaseKeys.Teams.captainID] as! String,
leagueCode: value?[FBDatabaseKeys.Teams.leagueCode] as? String ?? value?["leageCode"] as! String,
playersIds: [""], //todo retrieve data to array
teamName: value?[FBDatabaseKeys.Teams.teamName] as! String,
imageUrl: value?[FBDatabaseKeys.Teams.imageUrl] as! String,
groupMoto: value?[FBDatabaseKeys.Teams.teamMoto] as! String,
totalScore: String(format: "%#", value?[FBDatabaseKeys.Teams.totalScore] as? CVarArg ?? "") ,
totalTickets: String(format: "%#", value?[FBDatabaseKeys.Teams.totalTickets] as? CVarArg ?? "")
)
if team.leagueCode == self.currentPlayer.leagueCode{
teams.append(team)
}
}
teams = teams.sort {
Int($0.leagueCode)! < Int($1.leagueCode)!
} as! [Team]
//Sort array by property score
completion(teams)
}
Error:
Error:(204, 27) cannot assign value of type '()' to type '[Team]'
Here is my object:
struct Team {
let capitanId: String
let leagueCode: String
let playersIds: [String]
let teamName: String
let imageUrl: String
let groupMoto: String
let totalScore: String
let totalTickets: String
Thanks for your help
The error message
cannot assign value of type '()'
clearly says that the sort function doesn't return anything. The array is sorted in place.
And the conversions to Int are not necessary. You can sort strings numerically with localizedStandardCompare
teams.sort {
$0.leagueCode.localizedStandardCompare($1.leagueCode) == .orderedAscending
}
You need
teams = teams.sorted {
Int($0.leagueCode)! < Int($1.leagueCode)!
}
Or only mutating
teams.sort {
Int($0.leagueCode)! < Int($1.leagueCode)!
}
https://developer.apple.com/documentation/swift/array/2296815-sorted
https://developer.apple.com/documentation/swift/array/2296801-sort
//
dbReference.child(FBDatabaseKeys.teams).observeSingleEvent(of: .value) { (snapshot) in
for nameTeam in snapshot.children.allObjects as! [DataSnapshot] {
let value = nameTeam.value as? NSDictionary
let team = Team(
capitanId: value?[FBDatabaseKeys.Teams.captainID] as! String,
leagueCode: value?[FBDatabaseKeys.Teams.leagueCode] as? String ?? value?["leageCode"] as! String,
playersIds: [""], //todo retrieve data to array
teamName: value?[FBDatabaseKeys.Teams.teamName] as! String,
imageUrl: value?[FBDatabaseKeys.Teams.imageUrl] as! String,
groupMoto: value?[FBDatabaseKeys.Teams.teamMoto] as! String,
totalScore: String(format: "%#", value?[FBDatabaseKeys.Teams.totalScore] as? CVarArg ?? "") ,
totalTickets: String(format: "%#", value?[FBDatabaseKeys.Teams.totalTickets] as? CVarArg ?? "")
)
if team.leagueCode == self.currentPlayer.leagueCode{
teams.append(team)
}
}
teams.sort {
Int($0.leagueCode)! < Int($1.leagueCode)!
}
completion(teams)
}
How do I change the fetchAllPosts function in my networking file and in the homeviewController so that I only get posts from the database that match the UID of the user I’m following with the post.UID, so my feed is filled with posts of users I follow?
Here is the reference showing how I make a new follower in the database:
let followingRef = "following/" + (self.loggedInUserData?["uid"] as! String) + "/" + (self.otherUser?["uid"] as! String)
Here is the post structure in the Firebase database
posts
-Ke4gQKIbow10WdLYMTL (generated key)
postDate:
postId:
postPicUrl:
postText:
postTit:
type:
uid: looking to match this
Here is the current fetchAllPosts function in the networking file
func fetchAllPosts(completion: #escaping ([Post])->()) {
let postRef = self.dataBaseRef.child("posts")
postRef.observe(.value, with: { (posts) in
var resultsArray = [Post]()
for post in posts.children {
let post = Post(snapshot: post as! FIRDataSnapshot)
resultsArray.append(post)
}
completion(resultsArray)
}) { (error) in
print(error.localizedDescription)
}
}
here is the fetchAllPosts function in the homeViewController
private func fetchAllPosts(){
authService.fetchAllPosts {(posts) in
self.postsArray = posts
self.postsArray.sort(by: { (post1, post2) -> Bool in
Int(post1.postDate) > Int(post2.postDate)
})
self.tableView.reloadData()
}
}
Here is my post structure in my swift file:
struct Post {
var username: String!
var uid: String!
var postId: String!
var type: String
var postText: String
var postTit: String
var postPicUrl: String!
var postDate: NSNumber!
var ref: FIRDatabaseReference!
var key: String = ""
init(postId: String,postText: String, postTit:String, postDate:NSNumber, postPicUrl: String?, type:String, uid: String, key: String = ""){
self.postText = postText
self.postTit = postTit
self.postPicUrl = postPicUrl
self.type = type
self.uid = uid
self.postDate = postDate
self.postId = postId
}
init(snapshot: FIRDataSnapshot){
self.ref = snapshot.ref
self.key = snapshot.key
self.postId = (snapshot.value! as! NSDictionary)["postId"] as! String
self.type = (snapshot.value! as! NSDictionary)["type"] as! String
self.postPicUrl = (snapshot.value! as! NSDictionary)["postPicUrl"] as! String
self.postDate = (snapshot.value! as! NSDictionary)["postDate"] as! NSNumber
self.postTit = (snapshot.value! as! NSDictionary)["postTit"] as! String
self.uid = (snapshot.value! as! NSDictionary)["uid"] as! String
self.postText = (snapshot.value! as! NSDictionary)["postText"] as! String
}
func toAnyObject() -> [String: Any] {
return ["postId":self.postId,"type":self.type, "postPicUrl":self.postPicUrl,"postDate":self.postDate, "postTit":self.postTit,"uid": self.uid, "postText":self.postText,]
}
}
Did you use this;
postRef.queryOrdered(byChild: "uid").queryEqual(toValue: yourUid).observe(.value, with: {
snapshot in
if let shot = snapshot {
let post = Post(snapshot: post as! FIRDataSnapshot)
}
}
This returns data you are looking for.