I am attempting to create a completion block while pulling a users profile from a firebase table. I need it to complete before I allow it to pass back a value.
Here is what I have so far:
func getProf(email: String, pass: String, completionBlock: #escaping (_ success: Bool) -> (Int)) {
let ref = Database.database().reference()
let userID = Auth.auth().currentUser?.uid
ref.child("users").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
self.zDaily = value?["qday"] as? Int ?? 0
}) {
if let error = error {
completionBlock(false)
} else {
completionBlock(true)
return zDaily
}
}
}
I'm getting the following error:
Cannot convert value of type '() -> _' to expected argument type '((Error) -> Void)?'
I'm not sure how to fix this, any suggestion would be appreciated.
I'm not sure if that'll fix the error. If it doesn't, then I think I know the issue and it would be with your error block and your observeEvent.
Edit: Just made a change to return an error object from the observeEvent.
func getProf(email: String, pass: String, completionBlock: #escaping (Bool, Int) -> ()) {
let ref = Database.database().reference()
let userID = Auth.auth().currentUser?.uid
ref.child("users").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
self.zDaily = value?["qday"] as? Int ?? 0
}) { (error) in //Added return error value - this may fix your error
if let error = error {
completionBlock(false, 0) //Add default 0 to return if error
} else {
completionBlock(true, zDaily) //Added zDaily as Int to return
//return zDaily - return will not return in async function so you can return the value in completionBlock above.
}
}
}
Related
I'm new to swift language, but now I'm developing app with swift and firebase.
I'm using realtime database and while making two identical functions for getting data one is working and one is not.
This is the working one:
func getRoom(admin: String) -> AnyPublisher<Room, Error> {
Deferred {
Future { promise in
Database.database().reference().child("rooms/\(admin)")
.getData { [self] error, snapshot in
guard error == nil else {
promise(.failure(error!))
print(error!.localizedDescription)
return;
}
let value = snapshot.value as? [String:[String:Any]]
for (key, room) in value ?? [:] {
if (key == admin){
let admin = room["admin"] as? [String:Any]
let subjects = room["subjects"] as? [String]
let difficulties = room["difficulties"] as? [String]
let users = room["users"] as? [[String:Any]]
let questions = room["questions"] as? [[String:Any]]
let isGameStarted = room["is_game_started"] as? String
let room = Room(admin: dictionaryToUser(userDict: admin!), subjects: subjects!, difficutlies: difficulties!, users: dictionaryToUsersArr(usersArrDict: users!), questions: dictionaryToQuestionArr(questionsArrDict: questions!), is_game_started: isGameStarted!)
promise(.success(room))
}
}
}
}
}
.eraseToAnyPublisher()
}
And this is not working:
func getRoomUsersFromRoom(admin: String) -> AnyPublisher<[RoomUser], Error> {
var roomUsers: [RoomUser] = []
Deferred {
Future { promise in
Database.database().reference()
.child("rooms/\(admin)")
.getData { error, snapshot in
guard error == nil else {
promise(.failure(error!))
print(error!.localizedDescription)
return;
}
let value = snapshot.value as? [String:[String:Any]]
for (key, room) in value ?? [:] {
if (key == admin){
let users = room["users"] as? [[String:Any]]
for i in 0..<users!.count {
roomUsers.append(RoomUser(username: users![i]["username"] as! String, gamePoints: users![i]["room_points"] as! Int))
}
promise(.success(roomUsers))
}
}
promise(.success(roomUsers))
}
}
}
.eraseToAnyPublisher()
}
The errors in the second one are on the line with the Future, telling:
"Generic parameter 'Failure' could not be inferred"
"Generic parameter 'Output' could not be inferred"
It suggests me to put Future<Any, Error> and the error is gone but then I have warning on "eraseToAnyPublisher()", which I think is not good.
What is difference between the functions and any ideas how to solve this?
Thanks in advance
Im trying to call data from firebase. The problem is, the data is deeply nested and I don't think I can change that.
So I'm attempting to call values from firebase, which I can then use to reference new values.
The problem arises when my for loop is not finished before the next stage is called, meaning my dictionary count for the next stage is 0, so my next function is not called?
Is there a way to do this sufficiently?
Please help?
Heres my code:
func fetchBuyer(search: String, user: String, completion: #escaping ([Post]) -> (), withCancel cancel: ((Error) -> ())?) {
let ref = Database.database().reference().child("posts").child(user).child(search).child("purchases")
ref.observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionaries = snapshot.value as? [String: Any] else {
completion([])
return
}
let keys: [String] = dictionaries.map({ $0.key })
var newdictionaries = [String: String]()
for i in keys {
let newref = Database.database().reference().child("posts").child(user).child(search).child("purchases").child(i).child("purchaser")
newref.observeSingleEvent(of: .value, with: { (snapshot) in
newdictionaries[i] = snapshot.value as? String
print("THESE ARE MY PURCHASES ID-->", newdictionaries.values)///prints out ["-M0pTHtZXYUVQT7DCLj-", "-M0pU79uQCCnBunAEkJN"]
})
}
var buyerPosts = [Post]()
print("newdictionaries.count--->", newdictionaries.count)//this print is 0
newdictionaries.forEach({ (postId, value) in
Database.database().fetchPost(withUID: user, postId: postId, completion: { (post) in
buyerPosts.append(post)
if buyerPosts.count == newdictionaries.count{
completion(buyerPosts)
}
})
})
}) { (err) in
print("Failed to fetch posts for buyers:", err)
cancel?(err)
}
}
Attempted answer:
let g = DispatchGroup() //// 1
for i in keys{
g.enter() //// 2
let newref = Database.database().reference().child("posts").child(user).child(search).child("purchases").child(i).child("purchaser")
print("now")
newref.observeSingleEvent(of: .value, with: { (snapshot)
newdictionaries[i] = snapshot.value as? String
print("print new dictionaries-->", newdictionaries)
// complete here
Database.database().fetchPost(withUID: user, postId: newdictionaries[i]!, completion: { (post) in
buyerPosts.append(post)
g.leave() //////// 3
})
})
}
g.notify(queue: DispatchQueue.main) {
print("finished!!!")
completion(buyerPosts)
}
You need a dispatch group and nest the calls
let g = DispatchGroup() //// 1
for i in keys{
g.enter() //// 2
let newref = Database.database().reference().child("posts").child(user).child(search).child("purchases").child(i).child("purchaser")
newref.observeSingleEvent(of: .value, with: { (snapshot)
newdictionaries[i] = snapshot.value as? String
// complete here
Database.database().fetchPost(withUID: user, postId: postId, completion: { (post) in
buyerPosts.append(post)
g.leave() //////// 3
})
})
}
/// 4
g.notfiy(queue.main) {
completion(buyerPosts)
}
What is the solution to this error? I tried what I found, but I could not sort out my problem.
Error is here:
if var post = currentData.value as? [String : Any], let uid = Auth.auth().currentUser!.uid {
Full Code:
func incrementLikes(postId: String, onSucess: #escaping (Post) -> Void, onError: #escaping (_ errorMessage: String?) -> Void) {
let postRef = Api.Post.REF_POSTS.child(postId)
postRef.runTransactionBlock({ (currentData: MutableData) -> TransactionResult in
if var post = currentData.value as? [String : Any], let uid = Auth.auth().currentUser!.uid {
var likes: Dictionary<String, Bool>
likes = post["likes"] as? [String : Bool] ?? [:]
var likeCount = post["likeCount"] as? Int ?? 0
if let _ = likes[uid] {
likeCount -= 1
likes.removeValue(forKey: uid)
} else {
likeCount += 1
likes[uid] = true
}
post["likeCount"] = likeCount as AnyObject?
post["likes"] = likes as AnyObject?
currentData.value = post
return TransactionResult.success(withValue: currentData)
}
return TransactionResult.success(withValue: currentData)
}) { (error, committed, snapshot) in
if let error = error {
onError(error.localizedDescription)
}
if let dict = snapshot?.value as? [String: Any] {
let post = Post.transformPostPhoto(dict: dict, key: snapshot!.key)
onSucess(post)
}
}
}
enter image description here
I do not know how to add a picture directly. Sorry
Because Auth.auth().currentUser!.uid returns a String, not String?. If you want to perform a conditional check, you should use
if let uid = Auth.auth().currentUser?.uid {
//Logic here
}
If you want to assign value of Auth.auth().currentUser!.uid to uid, you should do it elsewhere, not inside an if statement
The let uid = Auth.auth().currentUser!.uid part of your if-let/var statement returns a String instead of an String?. You're force unwrapping currentUser Change it to currentUser?.uid
I'm trying to fetch data from web and trying paging using AZTableView library. I'm facing the above error. Here's my code
My Modal class
class JobsNearBy: NSObject {
var jobId: Int?
var title: String?
var companyName: String? }
Fetch data code
I fetch 10 rows from the web first time put them in object and append on array and return.
func jobsNearByFetch(pageNumber: Int, success:#escaping (_ status:Bool, _ jobsNearByArray:Any) -> (), failure:#escaping (_ message: Error) -> ()) {
let headers: HTTPHeaders = ["Accept": "application/json",
"Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhb"]
let url = "http://thedemo.net/demo/stdinaus/api/jobs-near-me?page=\(pageNumber)&latitude=27.6947033&longitude=85.3310636"
Alamofire.request(url, headers: headers).responseJSON { response in
guard let jobsResponse = response.result.value as? [String:Any] else{
print("Error: \(String(describing: response.result.error))")
failure((response.result.error! as Error))
return
}
// print("response: \(jobsResponse)")
let jobsNearByObj:JobsNearBy = JobsNearBy()
var jobsNearByArray:Array = [JobsNearBy]()
let dict = jobsResponse as NSDictionary
let status = dict["status"] as? Int
let meta = dict["meta"] as! NSDictionary
let lastPage = meta["last_page"] as? Int
let dataArray = dict["data"] as! NSArray
for dataDict in dataArray{
let dataCompanyName = dataDict as! NSDictionary
let jobId = dataDict as! NSDictionary
let title = dataDict as! NSDictionary
if let companyName = dataCompanyName["company_name"],
let jobId = jobId["jobId"],
let title = title["title"]{
jobsNearByObj.companyName = companyName as? String
jobsNearByObj.jobId = jobId as? Int
jobsNearByObj.title = title as? String
jobsNearByArray.append(jobsNearByObj)
}
}
success(true, jobsNearByArray)
}
}
Code in AZTableViewController
override func fetchData() {
super.fetchData()
if Util.isConnectedToInternet(){
self.showLoading(view: self.view, text: "Loading..")
APIHandler.sharedInstance.jobsNearByFetch(pageNumber: 1, success: { (status, jobsArray) in
self.stopLoading(fromView: self.view)
self.arrayOfJobs.removeAll()
self.arrayOfJobs.append(jobsArray as! JobsNearBy)
self.didfetchData(resultCount: self.arrayOfJobs.count, haveMoreData: true)
}) { (failure) in
self.stopLoading(fromView: self.view)
print("Failure")
}
}else{
Util.showAlert(title:"Oops", message:"No internet connection..", view:self)
}
}
override func fetchNextData() {
super.fetchNextData()
if Util.isConnectedToInternet(){
self.showLoading(view: self.view, text: "Loading..")
APIHandler.sharedInstance.jobsNearByFetch(pageNumber: 2, success: { (status, jobsArray) in
self.stopLoading(fromView: self.view)
self.arrayOfJobs.append(jobsArray as! JobsNearBy)
if self.arrayOfJobs.count < 10{
self.didfetchData(resultCount: self.arrayOfJobs.count, haveMoreData: true)
}else{
self.didfetchData(resultCount: self.arrayOfJobs.count, haveMoreData: false)
}
}) { (failure) in
self.stopLoading(fromView: self.view)
print("Failure")
}
}else{
Util.showAlert(title:"Oops", message:"No internet connection..", view:self)
}
}
I think I've made mistake on append line but unable to solve this. Please someone help me with the above error.
Your completion handler for jobsNearByFetch returns an array of JobsNearBy, which you put into jobsArray
Then, you have a force cast of jobsArray to JobsNearBy, but it is an array, not a single instance of the object so the downcast fails and because it is a forced downcast your app crashes.
You could fix it by using as! [JobsNearBy], but it is better to change the signature of the completion closure to indicate that it returns [JobsNearBy] instead of Any; then you don't need to downcast anything:
You shouldn't use Any when you can determine what the actual type is. Also, you shouldn't use NSDictionary when working in Swift if you can avoid it. Also, avoid force downcasting and unwrapping whenever possible.
Stylistically the boolean success parameter and a separate failure closure is a bit odd too; you would typically have a single closure that returns an optional Error - If error is nil then the operation was successful.
I would have:
func jobsNearByFetch(pageNumber: Int, completion:#escaping ( _ jobsNearByArray:[JobsNearBy]?, error:Error?) -> ()) {
This way you can use a single trailing closure.
You also need to look at your jobsNearByFetch as there are some return paths that don't call a closure.
Finally, you should look at the Codeble protocol as it can eliminate the JSON parsing code altogether.
I have don't know what the problem please help me. When I get a particular message from firebase database then the value is getting but my app got a crash on one line.So please tell me what I do wrong in my code.below is my function.We also provide the screenshot of the error.
func getLatestMessageFromFirebase(token:String,completionmessage: #escaping (_ message:String) -> Swift.Void)
{
print("getModelFromFirebase")
var message:String=""
ref.child("chatmessage/devicetoken/").child(token).queryLimited(toLast: 1).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
if value?["message"] as? String != ""
{
DispatchQueue.main.async
{
message = (value?["message"] as? String)! //My app stop on this line
completionmessage(message)
}
}
})
{ (error) in
print(error.localizedDescription)
}
}
func callAPI()
{
let response = (jsonResult.object(forKey: "chatListArr") as? NSArray)!
if response.count > 0
{
for i in 0..<response.count
{
let dict = response[i] as! NSDictionary
let chatlist = ChatList(dict: dict)
self.arr_list.append(chatlist)
}
for i in 0..<self.arr_list.count
{
let chatlist = self.arr_list[i]
self.getLatestMessageFromFirebase(token: chatlist.token, completionmessage: { (message) in
self.arr_list[i].msg = message
})
}
self.table_view.reloadData()
}
}
Please help me.
Thanks in Advance.
First of all you should clean your code up a bit, you do a couple of things which would be considered anti patterns
func getLatestMessageFromFirebase(token:String,completionmessage: #escaping (_ message:String) -> Swift.Void)
{
ref.child("chatmessage/devicetoken/").child(token).queryLimited(toLast: 1).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
for snap in snapshot.children.allObjects as [DataSnapshot] {
let value = snap.value as? [String: Any] ?? [:] // A good way to unwrap optionals in a single line
if let message = value["message"] as? String {
DispatchQueue.main.async {
completionmessage(message)
}
}
}
})
{ (error) in
print(error.localizedDescription)
}
}
With the above code your app shouldnt crash. And if there is a message AND it is a string (which might have been your problem before) then your callback will fire.