Swift: is it possible to iterate and call an array of functions? - swift4

I am trying to simplify the following repetitive code:
var cells = [Cell]()
var modules = [Module]()
var fields = [Field]()
root.child("cells").observeSingleEvent(of: .value) { (snapshot) in
guard let dict = snapshot.value as? [String: Any],
let cell = Cell(dict: dict) else { return }
cells.append(cell)
}
root.child("modules").observeSingleEvent(of: .value) { (snapshot) in
guard let dict = snapshot.value as? [String: Any],
let module = Module(dict: dict) else { return }
modules.append(module)
}
root.child("fields").observeSingleEvent(of: .value) { (snapshot) in
guard let dict = snapshot.value as? [String: Any],
let field = Field(dict: dict) else { return }
fields.append(field)
}
Cell, Module, Field are custom struct. I am thinking whether it's possible to put their initiators init?(dict: [String: Any]) in an array and pass in dict to each of them.

I think you will need to make your structs adopt one protocol for that:
protocol InitializableWithDictionary {
init?(dict: [String : Any])
}
struct Cell: InitializableWithDictionary {
//...
init?(dict: [String : Any]){
//...
}
}
struct Module: InitializableWithDictionary {
//...
init?(dict: [String : Any]){
//...
}
}
struct Field: InitializableWithDictionary {
//...
init?(dict: [String : Any]){
//...
}
}
var cells: [InitializableWithDictionary] = [Cell]()
var modules: [InitializableWithDictionary] = [Module]()
var fields: [InitializableWithDictionary] = [Field]()
root.child("cells").observeSingleEvent(of: .value) { (snapshot) in
guard let dict = snapshot.value as? [String: Any] else { return }
let array: [InitializableWithDictionary.Type] = [Cell.self, Module.self, Field.self]
array.forEach {
if let element = $0.init(dict: dict) {
switch element {
case is Cell: cells.append(element)
case is Module: modules.append(element)
case is Field: fields.append(element)
default: break
}
} else { return }
}
}

Related

Could not cast value of type '__NSCFString' to 'NSDictionary' switft 5

I want to get list of users from Firebase database and put that in a Users array but I am unable to get data from the snapshot
This is what I have tried
class User: NSObject {
var name: String?
var email: String?
}
var users : [User] = []
func fetchUsers() {
Database.database().reference().child("users").observe(.childAdded) { (snapshot) in
for child in (snapshot.children) {
let snap = child as! DataSnapshot
let dic = snap.value as! [String: String] // erros here
//print((dic["name"]))
//WANT TO LOAD THE LIST OF USERS INTO `var users`
}
}
}
Error
Could not cast value of type '__NSCFString' to 'NSDictionary'
This is how my data looks like on firebase database
Thanks for your help
R
That's because DataSnapshot is a String to Object dictionary. This should do the trick
guard let dict = child.value as? [String : AnyObject]
else {
print("couldn't cast to dictionary")
return
}
print("successfully cast to dictionary")
print(dict["name"])
print(dict["email"])
func fetchUsers() {
Database.database().reference().child("users").observe(.childAdded) { (snapshot) in
for child in snapshot.children.allObjects as! [DataSnapshot] {
print(child.value)
if let dict = child.value as? [String : AnyObject] {
if let email = dict["email"] {
print(email)
}
}
}
}
}

Cant fetch SavedPost

My app has an option to save posts for users to watch later. The code is:
func savedPost(for cell: FirstView) {
guard let indexPath = collectionView.indexPath(for: cell) else { return }
var post = self.posts[indexPath.item]
guard let currentuserId = Auth.auth().currentUser?.uid else { return }
let targetUid = post.user.uid
guard let postId = post.id else { return }
let ref = Database.database().reference().child("save_post").child(currentuserId).child(postId)
if post.hasSaved {
ref.removeValue { (err, _) in
if let _ = err {
showErr(info: NSLocalizedString("failtoUnsave", comment: ""), subInfo: tryLater)
return
}
post.hasSaved = false
self.posts[indexPath.item] = post
self.collectionView.reloadItems(at: [indexPath])
}
} else {
let values = ["userId": targetUid]
ref.updateChildValues(values) { (err, ref) in
if let _ = err {
showErr(info: NSLocalizedString("failtoSave", comment: ""), subInfo: tryLater)
}
post.hasSaved = true
self.posts[indexPath.item] = post
self.collectionView.reloadItems(at: [indexPath])
}
}
}
With this code my firebase database in "save_post" has -> currentUseruId -> postid -> postUserId.
On ProfileController users can view saved Posts from "savedPost" Tab. The code is:
var savedPosts = [Post]()
fileprivate func fetchSaved() {
var userIds = [String]()
var postIds = [String]()
guard let uid = self.user?.uid else { return }
let getIDsRef = Database.database().reference().child("save_post").child(uid)
let query = getIDsRef.queryOrderedByKey()
query.observeSingleEvent(of: .value) { (snapshot) in
let dictionary = snapshot.value as? [String: Any]
dictionary?.forEach({ (key,value) in
guard let dic = value as? [String: String] else { return }
postIds.append(key)
userIds.append(dic["userId"] ?? "")
})
var i = 0
while i < userIds.count {
self.fetchPostsWithUserIDPostID(userID: userIds[i], postID: postIds[i])
i += 1
}
}
}
fileprivate func fetchPostsWithUserIDPostID(userID: String, postID: String) {
let getPostRef = Database.database().reference().child("video_list")
getPostRef.child(userID).child(postID).observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionary = snapshot.value as? [String: Any] else { return }
let ref = Database.database().reference().child("users").child(userID)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
guard let dict = snapshot.value as? [String: Any] else { return }
let user = User(uid: userID, dictionary: dict)
var post = Post(user: user, dictionary: dictionary)
post.id = postID
guard let currentUserUID = Auth.auth().currentUser?.uid else { return }
Database.database().reference().child("likes").child(postID).child(currentUserUID).observeSingleEvent(of: .value, with: { (snapshot) in
if let value = snapshot.value as? Int, value == 1 {
post.hasLiked = true
} else {
post.hasLiked = false
}
post.hasSaved = true
self.savedPosts.append(post)
self.savedPosts.sort(by: { (p1, p2) -> Bool in
return p1.creationDate.compare(p2.creationDate) == .orderedDescending
})
self.collectionView.reloadData()
})
})
})
}
However, when I click "savedPost" tab, there is no post shown. I don't know where my mistake is. I have all the necessary code under all override func collectionView(....). I believe the error should come from the code listed above. I am sincerely looking for help to resolve this issue. Thanks.
There could be a number of things going on here. It would be good to throw some print statements in there to make sure that 1) the data you're getting back from the database looks like what you expect, and 2) that you're properly parsing it into Post objects. Do you have your cells defined properly for your CollectionView? Also, I don't see where you are defining the data source for the CollectionView.

How to read data from firebase

This is my realtime database structure. I have only 1 item in club. In reality, I have many items.
I want to read all clubs information, and try to get the related address using club's key.
here is my code:
func loadClubs() {
ref = Database.database().reference()
let clubRef = ref.child("club")
let refHandle = clubRef.observe(DataEventType.value, with: { (snapshot) in
if let c = snapshot.value as? [String : AnyObject] {
let name = c["name"] as! String // PRINT NIL
}
// ...
})
}
How can I retrieve a club's name, courtNum, explanation,...?
try this:-
ref = Database.database().reference()
ref.child("club").observe(.value, with: { (snapshot) in
print("clubs: \(snapshot)")
if(snapshot.exists()) {
let array:NSArray = snapshot.children.allObjects as NSArray
for obj in array {
let snapshot:FIRDataSnapshot = obj as! FIRDataSnapshot
if let childSnapshot = snapshot.value as? [String : AnyObject]
{
if let clubName = childSnapshot["name"] as? String {
print(clubName)
}
}
}
}
}

facing Issue in parsing in swift3

I am trying to parse the emergency data in into emergency struct but it never statifies the condition and get into else case.Here is my code and structure.Some thing i have written woring in first line.
if let emergencyDict = snapshotValue["emergency"] as? [String:[String:Any]]{
for (emerId, emerData) in emergencyDict {
let emer = Emergency.init(emergency: emerData as NSDictionary)
emergency.append(emer)
}
}
else{
let emer = Emergency.init(emerg: "" as AnyObject)
emergency.append(emer)
}
struct Emergency{
var emer_id: String
var emer_name: String
var emer_phoneNo: String
init(emergency: NSDictionary) {
if emergency.object(forKey: "id") != nil {
emer_id = emergency.object(forKey: "id") as! String
}
else{
emer_id = ""
}
}
}
The problem you are having emergency as Array with type [Any] and if you remove the first object then you get Array of type [[String:Any]]. So try like this way.
if let array = snapshotValue["emergency"] as? [Any],
let emergencyArrar = Array(array.dropFirst()) as? [[String:Any]] {
print(emergencyArray)
for emergency in emergencyArray {
print(emergency)
}
}
You have written wrong in this line:
if let emergencyDict = snapshotValue["emergency"] as? [String:[String:Any]]{
It should be:
if let emergencyDict = snapshotValue["emergency"] as? [[String:Any]]{
This question should belong to query from firebase database.
// you have to get the children in emergency,
// then get the value(dictionary) of each child
ref.child("emergency").observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let keys = value?.allKeys // [1, 2, 3 ....]
for key in keys {
ref.child("emergency").child(key)..observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
// Here is your dictionary
}
}
}) { (error) in
print(error.localizedDescription)
}

Firebase 2, accesing child values in snapshot produces nulls

I've been attempting to utilize firebase's snapshots, but when I try to access specific children, the output is a null.
var ref = FIRDatabaseReference.init()
func loadData {
ref = FIRDatabase.database().reference(
ref.child("Posts").child(postId).observeSingleEventOfType(.Value, withBlock: { snapshot in
print(snapshot.value?["PostText"] as! String) // Optional(<null>)
print(snapshot)
for child in snapshot.children {
if child.childSnapshotForPath("PostText").value == nil {
self.postText.text = ""
} else {
self.postText.text = child.childSnapshotForPath("PostText").value as? String // Optional(<null>)
print(child.childSnapshotForPath("PostText").value)
}
}
})
}
Output of print(snapshot)
Snap (84844) {
Author = lGAV1KUhSCP8hnFiKY1N9lBPrmmst1;
CommentsCount = 1;
Group = 665555;
ImageUrl = "http://i.telegraph.co.uk/multimedia/archive/03589/Wellcome_Image_Awa_3589699k.jpg";
PostText = "I like cakeh, but theijijijijijij truth is, it's too sweet. So SOMETIMES I dont eat it, but i LIKE CAKE.";
}
It looks like your snapshot is a Dictionary. Then you have to cast it as a Dictionary:
func loadData {
ref = FIRDatabase.database().reference(
ref.child("Posts").child(postId).observeSingleEventOfType(.Value, withBlock: { snapshot in
print(snapshot.value?["PostText"] as! String) // Optional(<null>)
print(snapshot)
let dict = snapshot.value as! Dictionary<String, AnyObject>
if let author = dict["Author"] as? String, commentsCount = dict["CommentsCount"] as? Int, group = dict["Group"] as? Int {
print("author \(author) commentsCount \(commentsCount), group: \(group)")
}
})
}
Do the same for ImageUrl and PostText, they should be cast as String