Swift Ambiguous use of 'subscript' - swift

I had recently added new pods to my project and updated the previous ones, this code had all worked before and now I am getting "Ambiguous use of 'subscript'" I have been checking other answers and it seems like the way I have it currently set up is the correct way. I am not sure why all of a sudden these lines of code would get flagged as errors.
databaseRef.child("following").queryOrderedByKey().observeSingleEvent(of: .value, with: { (postsSnapshot) in
let posts = postsSnapshot.value as! [String: AnyObject]
for (_, post) in posts {
for (_, postInfo) in post as! [String: AnyObject] {
if let followingID = postInfo["uid"] as? String {
for each in self.following {
if each == followingID {
guard let uid = postInfo["uid"] as! String? else {return}
guard let name = postInfo["businessName"] as! String? else {return}
guard let address = postInfo["businessStreet"] as! String? else {return}
guard let state = postInfo["businessState"] as! String? else {return}
guard let city = postInfo["businessCity"] as! String? else {return}
let data = ["uid":postInfo["uid"] as! String, "businessName":postInfo["businessName"] as! String, "businessStreet":postInfo["businessStreet"] as! String, "businessState":postInfo["businessState"] as! String, "businessCity":postInfo["businessCity"] as! String,"businessZIP":postInfo["businessZIP"] as! String, "businessPhone":postInfo["businessPhone"] as! String, "businessLatitude":postInfo["businessLatitude"] as! String, "businessLongitude":postInfo["businessLongitude"] as! String, "businessWebsite":postInfo["businessWebsite"] as! String, "facebookURL":postInfo["facebookURL"] as! String, "foursquareURL":postInfo["foursquareURL"] as! String, "googleURL":postInfo["googleURL"] as! String, "instagramURL":postInfo["instagramURL"] as! String, "snapchatURL":postInfo["snapchatURL"] as! String, "twitterURL":postInfo["twitterURL"] as! String, "yelpURL":postInfo["yelpURL"] as! String]

The problem is your use of AnyObject. postInfo is being declared as AnyObject but then you attempt to use it like a dictionary, hence the error.
If post is a dictionary of dictionaries, then declare is as such:
Change:
for (_, postInfo) in post as! [String: AnyObject] {
to:
for (_, postInfo) in post as! [String: [String: AnyObject]] {
This will change postInfo to be [String: AnyObject] instead of AnyObject.

Related

Reloading tableView after liking a button in cell

My tableView shows some user comments loaded via Firebase Database. The tableView contains also a prototype cell. First of all loadPosts() gets executed. It loads every post and also goes to the user and takes the profile picture which is up to date and at last it sorts them after time.
Now the user has the option to like a post. In the extension UITableViewDelegate I created a cell.buttonAction. Here it opens into the users account and looks if he already liked the post with a specific id. If not it adds +1. If it did already it takes one -1.
The code itself works. But I have huge performance issues. Sometime the tableView gets updated immidiately. Sometimes I have to manually refresh it. As you can see right now I use observeSingleEvent. Should I better use observe instead? But then it crashes sometimes if someone posts and I have the same tableView opened. I also don't want it to reload every time the whole tableView if someone liked a post.
I also created a Video. Maybe it helps. I really do not know how I can solve that problem.
func loadPosts() {
let placeIdFromSearch = ViewController.placeidUebertragen
ref = Database.database().reference().child("placeID/\(placeIdFromSearch)")
ref.queryOrdered(byChild: "userTime").queryLimited(toLast: 10).observeSingleEvent(of: DataEventType.value, with: {(snapshot) in
self.table.removeAll()
for video in snapshot.children.allObjects as! [DataSnapshot] {
let Object = video.value as? [String: AnyObject]
let timestampDate = NSDate(timeIntervalSince1970: Double(Object?["userTime"] as! NSNumber)/1000)
...
let time = dateFormatter.string(from: timestampDate as Date)
...
let userTimeForArranging = Object?["userTime"]
ViewComments.commentIDNew = commentId as! String
getProfilePicture()
func getProfilePicture(){
self.ref = Database.database().reference()
self.ref.child("user/\(userID!)").observe (.value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let userImage = value?["picture"] as? String ?? ""
let userName = value?["firstname"] as? String ?? ""
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 as! String, userTimeForArranging: userTimeForArranging as! Int)
self.table.insert(video, at: 0)
self.table = self.table.sorted(by: { $0.userTimeForArranging! > $1.userTimeForArranging! })
self.tableView.reloadData()
})
}
}
})
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableViewCell
let video: importComment
video = table[indexPath.row]
cell.layoutMargins = UIEdgeInsets.zero
...
cell.userImage.image = UIImage(url: URL(string: video.userImage!))
cell.buttonAction = {
getClickedRow(arg: true, completion: { (success) -> Void in
if success {
self.loadPosts()
} else {
}
})
func getClickedRow(arg: Bool, completion: #escaping (Bool) -> ()) {
let selectedIndexPath = self.table[indexPath.row].commentId!
ViewSubComments.subCommentIdNew = selectedIndexPath
completion(arg)
}
}
return cell
}
From Prototype cell:
#IBAction func buttonTapped(_ sender: UIButton) {
buttonAction?()
let ref = Database.database().reference()
let userID = Auth.auth().currentUser!.uid
ref.child("user/\(userID)/places/\(ViewController.placeidUebertragen)/\(ViewComments.commentIDNew)").observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists(){
getLikeAndMinusOne ()
let database = Database.database().reference()
database.child("user/\(userID)/places/\(ViewController.placeidUebertragen)/\(ViewComments.commentIDNew)").removeValue()
}else{
getLikeAndAddOne ()
let database = Database.database().reference()
let object: [String: Any] = [
"like": "true",]
database.child("user/\(userID)/places/\(ViewController.placeidUebertragen)/\(ViewComments.commentIDNew)").setValue(object)
}
})
func getLikeAndAddOne () {
let database = Database.database().reference()
database.child("placeID/\(ViewController.placeidUebertragen)/\(ViewComments.commentIDNew)").observeSingleEvent(of: .value, with: { (snapshot) in
let userDict = snapshot.value as! [String: Any]
let userLikes = userDict["userLikes"] as! Int
var userLikesNeu = Int(userLikes)
userLikesNeu = Int(userLikes) + 1
database.child("placeID/\(ViewController.placeidUebertragen)/\(ViewComments.commentIDNew)").updateChildValues(["userLikes": userLikesNeu])
})}
func getLikeAndMinusOne () {
let database = Database.database().reference()
database.child("placeID/\(ViewController.placeidUebertragen)/\(ViewComments.commentIDNew)").observeSingleEvent(of: .value, with: { (snapshot) in
let userDict = snapshot.value as! [String: Any]
let userLikes = userDict["userLikes"] as! Int
var userLikesNeu = Int(userLikes)
userLikesNeu = Int(userLikes) - 1
database.child("placeID/\(ViewController.placeidUebertragen)/\(ViewComments.commentIDNew)").updateChildValues(["userLikes": userLikesNeu])
})}
}

Not able to read data from Firebase realtime database

I have stored the comments under a post in firebase realtime database. The problem i have is that when i try to parse out the data from firebase i get an error that says Unexpectedly found nil while unwrapping an Optional value. So for example if i try to pront the data stored under degree, i get this nil error. But when i print "comments" instead of the "degree" i successfully fetch the data. My database structure looks like this.
func obeserveComments() {
// get auto-id of post
let commentKey = self.keyFound
let postRef = Database.database().reference().child("posts").child(commentKey)
var tempComments = [Comments]()
postRef.observe(.value, with: {(snapshot) in
if let dict = snapshot.value as? [String:Any] {
if let comments = dict["comments"] as? [String:Any] {
let degree = comments["reply degree"] as! String
// let name = comments["reply name"] as! String
// let text = comments["reply text"] as! String
// let university = comments["reply university"] as! String
// let photoURL = comments["reply url"] as! String
// let url = URL(string: photoURL)
// let timestamp = comments["timestamp"] as! Double
print(degree)
}
}
})
}
The answer by #aytroncb is a good answer, I prefer to leave Firebase data 'Firebasy' as long as possible. In other words coverting to dictionaries looses ordering and and find code like this
[String: [String: [String: Any]]]
To be very hard to read.
I prefer
let snap = snapshot.childSnapshot("comments") //snap becomes a DataSnapshot
So my solution maintains the order and leverages .childSnapshot to leave data in it's DataSnapshot form.
func readPostComments() {
let postRef = self.ref.child("posts") //self.ref points to my firebase
postRef.observeSingleEvent(of: .value, with: { snapshot in
let allPosts = snapshot.children.allObjects as! [DataSnapshot]
for postSnap in allPosts {
print("postId: \(postSnap.key)")
let commentsSnap = postSnap.childSnapshot(forPath: "comments") //will be a DataSnapshot
let allComments = commentsSnap.children.allObjects as! [DataSnapshot]
for commentSnap in allComments {
print(" commentId: \(commentSnap.key)")
let replyDegree = commentSnap.childSnapshot(forPath: "reply_degree").value as? String ?? "No Degree"
let replyName = commentSnap.childSnapshot(forPath: "reply_name").value as? String ?? "No Name"
print(" degree: \(replyDegree) by: \(replyName)")
}
}
})
}
EDIT
For a single post, remove the top part of the code that reads in and iterates over all posts.
func readCommentsForOnePost() {
let postRef = self.ref.child("posts")
let postCommentRef = postRef.child("post_0")
postCommentRef.observeSingleEvent(of: .value, with: { snapshot in
print("postId: \(snapshot.key)")
let commentsSnap = snapshot.childSnapshot(forPath: "comments") //will be a DataSnapshot
let allComments = commentsSnap.children.allObjects as! [DataSnapshot]
for commentSnap in allComments {
print(" commentId: \(commentSnap.key)")
let replyDegree = commentSnap.childSnapshot(forPath: "reply_degree").value as? String ?? "No Degree"
let replyName = commentSnap.childSnapshot(forPath: "reply_name").value as? String ?? "No Name"
print(" degree: \(replyDegree) by: \(replyName)")
}
})
}
Its because firebase is returning your data like this
{
"MAKFW244kdL)Cw;1": [Array of data],
"LOPSw!35pa3flAL4": [Array of data],
"ALV34VR4_A6Vn1a": [Array of data]
}
So change your initial casting of snapshot.value to this:
if let dict = snapshot.value as? [String: [String: Any]]
then loop through that new dictionary like this:
for objectJson in dict.values {
if let comments = objectJson["comments"] as? [String: [String: Any]] {
for commentJson in comments.values {
let degree = commentJson["reply_degree"] as? String
}
}
}
Update
Just read through your post again and noticed your trying to access the comments directly with a key, your first going to need to provide the PostId. Then you can use the above code to loop through the objects
let postRef = Database.database().reference().child("posts").child(postID)
alternatively I believe you can have the comments returned as a normal list by doing something like this:
let postRef = Database.database().reference().child("posts").child("\(postID)/{id}")

Swift: Sort list object by property

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)
}

Can't get node of firebase children

Hi there i'm newest in swift. I am working with a firebase database with at 2 layer of hierarchy as well as many children for each node. I got 1st layer (descript, enddata and other), but i stll can't get the news node. Is in 3 to 5 random keys. I sow many issues but still not have issue for me.
I'm understand i'm doing some wrong but what?
The Firebase is:
i need retreat the news child
struct is
struct ICONews {
let ICOId: String
let news1: String
let news2: String
let news3: String
init?(ICOId: String, dict: [String: Any] ) {
self.ICOId=ICOId
guard let news1 = dict[""] as? String,
let news2 = dict[""] as? String,
let news3 = dict[""] as? String
else { return nil }
self.news1 = news1
self.news2 = news2
self.news3 = news3
}
}
struct NewsSnapShot {
let posts: [ICONews]
init?(with snapshot: DataSnapshot) {
var posts = [ICONews] ()
guard let snapDict = snapshot.value as? [String: [String: Any]] else { return nil }
for snap in snapDict {
guard let post = ICONews (ICOId: snap.key, dict: snap.value) else {continue}
posts.append(post)
}
self.posts=posts
}
}
class of DataBase
class DatabaseService {
static let shared = DatabaseService()
private init(){}
let ICOReference = Database.database().reference()
}
and retreat method
DatabaseService.shared.ICOReference.child("news").observe(DataEventType.value, with: { (snapshot) in
guard let postsSnapShot = ICOSnapShot(with: snapshot) else {return}
})
done
Database.database().reference().observeSingleEvent(of: .value, with: {(snapshot) in
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? DataSnapshot {
let values = (rest as! DataSnapshot).value as? NSDictionary
let enumeratorMap1 = (rest as! DataSnapshot).children
while let rest2 = enumeratorMap1.nextObject() as? DataSnapshot {
let valuesMap1 = (rest2 as! DataSnapshot).value as? NSDictionary
if (rest2 as! DataSnapshot).key == "news" {
print(rest2.value)
}
}
}
})
Make the the Firebase Api call like
Database.database().reference().child("users").child(userID).observe(.childAdded, with: { (snapshot) in
if snapshot.exists() {
let receivedMessage = snapshot.value as! [String: Any]
let name = receivedMessage["name"] as? String ?? ""
let id = receivedMessage["id"] as? Double ?? 0.0
let profileurl = receivedMessage["url"] as? String ?? ""
completion(User(name: name, id: id, url: url))
} else {
failure()
}
})

reading data on firebase

This is my data structure.
This is how I load club data and its address.
func loadClubs() {
ref = Database.database().reference()
ref.child("club").observe(DataEventType.childAdded, with: { (clubSnapshot) in
if let clubDict = clubSnapshot.value as? [String : AnyObject] {
let name = clubDict["name"] as! String
let explanation = clubDict["explanation"] as! String
let courtNum = clubDict["courtNum"] as! Int
let membershipFee = clubDict["membershipFee"] as! Int
let visitorFee = clubDict["visitorFee"] as! Int
let hasCarParking = clubDict["hasCarParking"] as! Bool
let club2 = Club2(name: name, explanation: explanation, courtNum: courtNum, membershipFee: membershipFee, visitorFee: visitorFee, hasCarParking: hasCarParking)
self.club2Array.append(club2) // Add to clubArray
print(self.club2Array)
self.tableView.reloadData()
}
let addressRef = Database.database().reference()
addressRef.child("address").child(clubSnapshot.key).observe(DataEventType.childAdded, with: { (addressSnapshot) in
if let addressDict = addressSnapshot.value as? [String: AnyObject] {
let clubAddress = ClubAddress(postCode: addressDict["postcode"] as! String, cityName: addressDict["city"] as! String, ward: addressDict["ward"] as! String, address1: addressDict["address1"] as! String, address2: addressDict["address2"] as! String)
self.addressArray.append(clubAddress)
print(self.addressArray)
}
})
})
}
basically, after retrieving each snapshot of club, I get club's key (-KsJB9TkoGNIkiZFGg7), then use that key to retrieve address.
However, print(self.addressArray) doesn't not print anything.
I add a debug breakpoint at if let addressDict = addressSnapshot.value as? [String: AnyObject] { , it does not stop the debug process.
Why is it not calling?
What do I miss here?
Ah! Your code is very close.
Keep in mind that .childAdded iterates over all of the child nodes and loads each one.
In your case, you don't want to iterate over all of the address nodes, you just want one, and don't want to leave an observer.
To do that, we load the specific node child data of the address node, by observeSingleEvent(of: .value. Here's a snippet of the important part.
let addressRef = Database.database().reference()
addressRef.child("address").child(clubSnapshot.key)
.observeSingleEvent(of: .value, with: { (addressSnapshot) in
let dict = addressSnapshot.value as! [String: Any]
let address = dict["address1"] as! String
print(address)