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
Related
I have this code
let dic = snapshot.value as! [String:[String:Any]]
Array(dic.values).forEach {
print($0["fcmToken"])
}
It produce these 3
Optional(c1DdtdDF1Rs:APA91bGJBUD65nidQiFDO90AVNgq0wiMjUaZmZXVJ8c_tYmFe5dkmgweOdO10jzPRlMVZF_qNyWMMsu7EhA5IMVo3jLWvBThDteR7WWUPqau-ZFAHKQPHgI5Vb48vA-_4nwkZCKrOVoT)
Optional(c1DdtdDF1Rs:APA91bGJBUD65nidQiFDO90AVNgq0wiMjUaZmZXVJ8c_tYmFe5dkmgweOdO10jzPRlMVZF_qNyWMMsu7EhA5IMVo3jLWvBThDteR7WWUPqau-ZFAHKQPHgI5Vb48vA-_4nwkZCKrOVoT)
Optional(c1DdtdDF1Rs:APA91bGJBUD65nidQiFDO90AVNgq0wiMjUaZmZXVJ8c_tYmFe5dkmgweOdO10jzPRlMVZF_qNyWMMsu7EhA5IMVo3jLWvBThDteR7WWUPqau-ZFAHKQPHgI5Vb48vA-_4nwkZCKrOVoT)
I want to loop through them and add a simple if-check.
I tried
let dic = snapshot.value as! [String:[String:Any]]
Array(dic.values).forEach {
print($0["fcmToken"])
if($0["fcmToken"] != fcmToken) {
print("token is not match detected")
}
}
I kept getting
Binary operator '!=' cannot be applied to operands of type 'Any?' and 'String?'
How would one go about debugging this further?
You have:
let dic = snapshot.value as! [String:[String:Any]]
Array(dic.values).forEach {
print($0["fcmToken"])
if($0["fcmToken"] != fcmToken) {
print("token is not match detected")
}
}
But it’s giving you a compile error.
Assuming that this line is working:
let dic = snapshot.value as! [String:[String:Any]]
I'd write the rest like this:
for v in dic.values {
if let token = v["fcmtoken"] as? String, token != fcmtoken {
print("token \(token) is not match detected")
}
}
The effect is the same and there’s no error.
You can try
let dic = snapshot.value as! [String:[String:Any]]
let tokens = Array(dic.values).map { $0["fcmToken"] as! String }
let exists = tokens.contains(fcmToken)
Your problem as $0["fcmToken"] is of type Any? that can't compared with type String? ( fcmToken )
let dic = snapshot.value as! [String:[String:Any]]
Array(dic.values).forEach {
print($0["fcmToken"])
if let token = $0["fcmToken"] as? String , token != fcmToken {
print("token is not match detected")
}
// or
if ($0["fcmToken"] as? String) != fcmToken {
print("token is not match detected")
}
}
You can savely use
guard let dic = snapshot.value as? [String: [String:Any]] else {
return
}
but it won't function when the value is nil
Hi I am trying to learn RXSwift and First time I came across these concepts like Maps and Compact Maps.
I am able to get the response, but this line always returns empty.
objects.compactMap(DummyUser.init)
fileprivate let Users = Variable<[DummyUser]>([])
fileprivate let bag = DisposeBag()
response
.filter { response, _ in
return 200..<300 ~= response.statusCode
}
.map { _, data -> [[String: Any]] in
guard (try? JSONSerialization.jsonObject(with: data, options: [])) != nil else {
return []
}
let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]
// print(json!["results"])
return json!["results"] as! [[String : Any]]
}
.filter { objects in
return objects.count > 0
}
.map { objects in
// objects.forEach{print($0["name"]!)}
let names = objects.map { $0["name"]!}
print(names)
return objects.compactMap(DummyUser.init)
}
.subscribe(onNext: { [weak self] newEvents in
self?.processEvents(newEvents)
})
.disposed(by: bag)
func processEvents(_ newEvents: [DummyUser]) {
var updatedEvents = newEvents + Users.value
if updatedEvents.count > 50 {
updatedEvents = Array<DummyUser>(updatedEvents.prefix(upTo: 50))
}
Users.value = updatedEvents
DispatchQueue.main.async {
self.MianUsertable.reloadData()
}
// refreshControl?.endRefreshing()
let eventsArray = updatedEvents.map{ $0.dictionary } as NSArray
eventsArray.write(to: userFileURL, atomically: true)
}
My Json Response is Here
https://randomuser.me/api/?results=5
DummyUser Class
import Foundation
typealias AnyDict = [String: Any]
class DummyUser {
let gender: String
let name: AnyDict
let dob: String
let picture: AnyDict
init?(dictionary: AnyDict) {
guard let Dgender = dictionary["gender"] as? String,
let Dname = dictionary["name"] as? AnyDict,
let birthdata = dictionary["dob"] as? AnyDict,
let Ddob = birthdata["dob"] as? String,
let Dpicture = dictionary["picture"] as? AnyDict
else {
return nil
}
gender = Dgender
name = Dname
dob = Ddob
picture = Dpicture
}
var dictionary: AnyDict {
return [
"user": ["name" : name, "gender": gender, "dob": dob],
"picture" : ["userImage": picture]
]
}
}
In your DummyUser model you are using failable initializer, so in case of wrong dictionary provided to init method it will return nil.
compactMap automatically automatically filters nil's and that's the reason why your output is empty.
Looking at this piece of code:
let names = objects.map { $0["name"]!}
return objects.compactMap(DummyUser.init)
I would debug this variable called names because it probably has wrong input for the DummyUser initializer. It should be dictionary containing all of your DummyUser parameters. You can also debug your failable initializer to see which of the parameter is missing.
I am having trouble with the asychronous nature of Firebase, particulary appending to an array from within an observe function.
Any suggestions or help would be much appreciated :)
The comp that is appended to the users array in the selectUsersComp function disappears when the firebase observe function is exited, even though I reload the data in the collectionView.
I have tried using Dispatch.main.async but it has not helped. I have a Firebase observe function inside another Firebase observe function. Does this change things?
fileprivate func fetchStartedComps() {
let ref = Database.database().reference().child("startedComps")
ref.queryOrdered(byChild: "creationDate").observe(.value, with: {
(snapshot) in
guard let dictionaries = snapshot.value as? [String : Any] else { return }
dictionaries.forEach({ (key, value) in
guard let compDictionary = value as? [String: Any] else { return }
let comp = StartedComp(Id: key, dictionary: compDictionary)
self.selectUsersComp(comp: comp)
})
self.filteredStartedComps = self.startedComps
self.collectionView?.reloadData()
}) { (err) in
print("Failed to fetch comps for search", err)
}
}
func selectUsersComp(comp: StartedComp) {
guard let userId = Auth.auth().currentUser?.uid else { return }
let ref = Database.database().reference().child("startedComps").child(comp.title).child("invitedUsers")
ref.observe(.value, with: { (snapshot) in
guard let dictionaries = snapshot.value as? [String : Any] else { return }
dictionaries.forEach({ (key, value) in
if key == userId {
self.startedComps.append(comp)
}
})
self.collectionView?.reloadData()
}) { (err) in
print(err)
}
}
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.
This question already has answers here:
How to return value from Alamofire
(5 answers)
Closed 5 years ago.
I am new with iOS programming. I am trying to make a piece of code in my function be synchronized, but it doesn't seem to work:
func fetchLocationsList(searchText:String)->Array<String> {
print ("searched text:\(searchText)")
let url = URL(string:"http://api.openweathermap.org/data/2.5/find?q=\(searchText)&type=like&sort=name&cnt=9&APPID=a33aa72")
//Using Alamofire to handle http requests
Alamofire.request(url!).responseJSON {response in
guard let jsonResponse = response.result.value as? [String:Any]
else { print ("error in json response")
return}
guard let list = jsonResponse["list"] as? NSArray else {return}
let lockQueue = DispatchQueue(label:"Et.My-Weather-App.queue1")
_ = lockQueue.sync{
for index in 0..<list.count {
print ("index is: \(index)")
guard let listElement = list[index] as? [String:Any] else {return}
let id = listElement["id"] as! Int
print ("id is: \(id)")
let cityName = listElement["name"] as! String
print ("cityName is: \(cityName)")
let sys = listElement["sys"] as! [String:Any]
let country = sys["country"] as! String
print ("country is: \(country)")
let element = "\(cityName), \(country), \(id)"
print ("\(element)")
self.resultsArray.append(element)
}
}
}
if self.resultsArray.count==0 {
print ("results array is also zero!")
}
return self.resultsArray
}
When I run it, I see that the line "results array is also zero!" is printed before the "for" loop fills the resultArray with elements, so the returned resultArray is always empty!
What am I doing wrong?
I suggest you do this as async tasks are a pain and this works quite well.
func fetchLocationsList(searchText:String, completion: #escaping (_ results:Array<String>)->()){
print ("searched text:\(searchText)")
let url = URL(string:"http://api.openweathermap.org/data/2.5/find?q=\(searchText)&type=like&sort=name&cnt=9&APPID=a33aa72")
//Using Alamofire to handle http requests
Alamofire.request(url!).responseJSON {response in
guard let jsonResponse = response.result.value as? [String:Any] else { print ("error in json response"); return}
guard let list = jsonResponse["list"] as? Array<Dictionary<String,Any>> else { return }
var array = Array<String>() // create an array to store results.
for item in list {
let id = item["id"] as! Int
let cityName = item["name"] as! String
let sys = item["sys"] as! Dictionary<String,Any>
let country = sys["country"] as! String
let element = "\(cityName), \(country), \(id)"
array.append(element) // add to that array.
}
completion(array) //send the array via the completions handler.
}
}
So in your viewDidLoad or whatever.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
fetchLocationsList(searchText: "Whatever this string is") { (results) in
self.resultsArray.append(contentsOf: results)
// Then do anything else you need to do after this function has completed within this closure.
}
}