I've set up some data objects in Firebase and I'm trying to retrieve them in Wwift and display them in a table view but I'm unable to get any result.
What i've tried?
var crystalsList = [Crystal]()
override func viewDidLoad() {
super.viewDidLoad()
print(crystalsList.count)
crystalRef = Database.database().reference().child("crystalList")
crystalRef?.observe(DataEventType.value, with: { (snapshot) in
if snapshot.childrenCount > 0 {
self.crystalsList.removeAll()
for myCrystal in snapshot.children.allObjects as! [DataSnapshot]{
let crystalObject = myCrystal.value as? [String : AnyObject]
let name = crystalObject?["name"]
let colors = crystalObject?["colors"]
let mainColor = crystalObject?["mainColor"]
let region = crystalObject?["region"]
let rarity = crystalObject?["rarity"]
let additionalInfo = crystalObject?["additionalInfo"]
let primaryUse = crystalObject?["primaryUse"]
let isNew = crystalObject?["isNew"]
let theCrystal = Crystal(name: (name as! String?)!, rarity: (rarity as! String?)!, region: (region as! String?)!, color: (colors as! String?)!, mainColor: (mainColor as! String?)!, isNewListing: (isNew as! Bool), additionalInfo: (additionalInfo as! String?)!, primaryUse: (primaryUse as! String?)!)
self.crystalsList.append(theCrystal)
}
self.crystalTable.reloadData()
}
})
}
Thanks to frank, i was able to resolve this issue by going into firebase and allowing the permission to read the data.
Related
I have a follow/unfollow button and am accessing it by "sender". I am changing the text when the user taps it to follow or unfollow another user. Problem is that when it should show "unfollow" it is showing the default text used in storyboard. The button changes to "follow" as it should, but not "unfollow". Also, I have to use "sender: UIButton" because I am accessing the tableview cells "tag" for the right information.
#IBAction func followButton(_ sender: UIButton) {
//self.yourFollowing.removeAll()
//self.following.removeAll()
self.followingTableView.reloadData()
let accessData = self.yourFollowing[sender.tag].dataPass
let businessUid = accessData["uid"] as! String
let businessName = accessData["businessName"] as! String
let businessStreet = accessData["businessStreet"] as! String
let businessCity = accessData["businessCity"] as! String
let businessState = accessData["businessState"] as! String
let businessZip = accessData["businessZIP"] as! String
let businessPhone = accessData["businessPhone"] as! String
let businessLatitude = accessData["businessLatitude"] as! String
let businessLongitude = accessData["businessLongitude"] as! String
let businessWebsite = accessData["businessWebsite"] as! String
let businessFacebook = accessData["facebookURL"] as! String
let businessFoursquare = accessData["foursquareURL"] as! String
let businessGoogle = accessData["googleURL"] as! String
let businessInstagram = accessData["instagramURL"] as! String
let businessSnapchat = accessData["snapchatURL"] as! String
let businessTwitter = accessData["twitterURL"] as! String
let businessYelp = accessData["yelpURL"] as! String
let userID = Auth.auth().currentUser!.uid
let ref = Database.database().reference()
let key = ref.child("Businesses").childByAutoId().key
var isFollower = false
let followersRef = "followers/\(businessUid)/\(self.loggedInUserData?["uid"] as! String)"
let followingRef = "following/" + (self.loggedInUserData?["uid"] as! String) + "/" + (businessUid)
ref.child("Businesses").child(userID).child("following").queryOrderedByKey().observeSingleEvent(of: .value, with: { snapshot in
if let following = snapshot.value as? [String : AnyObject] {
for (item, value) in following {
if value as! String == businessUid {
isFollower = true
let followersRef = "followers/\(businessUid)/\(self.loggedInUserData?["uid"] as! String)"
let followingRef = "following/" + (self.loggedInUserData?["uid"] as! String) + "/" + (businessUid)
let childUpdates = [followingRef:NSNull(),followersRef:NSNull()]
self.databaseRef.updateChildValues(childUpdates)
ref.child("Businesses").child(userID).child("following/\(item)").removeValue()
ref.child("Businesses").child(businessUid).child("followers/\(item)").removeValue()
sender.titleLabel?.text = "follow"
//self.yourFollowing.removeAll()
self.following.removeAll()
self.followingTableView.reloadData()
}
}
}
// Follow
if !isFollower {
sender.titleLabel?.text = "unfollow"
let followersData = ["email":self.loggedInUserData?["email"] as! String, "businessName":self.loggedInUserData?["businessName"] as! String]
let followingData = ["businessName":businessName, "businessStreet":businessStreet, "businessCity":businessCity, "businessState":businessState, "businessZIP":businessZip, "businessPhone":businessPhone, "businessWebsite":businessWebsite,"businessLatitude":businessLatitude, "businessLongitude":businessLongitude, "facebookURL":businessFacebook, "twitterURL":businessTwitter, "instagramURL":businessInstagram, "googleURL":businessGoogle, "yelpURL":businessYelp, "foursquareURL":businessFoursquare, "snapchatURL":businessSnapchat, "uid":businessUid]
let childUpdates = [followersRef:followersData, followingRef:followingData]
self.databaseRef.updateChildValues(childUpdates)
let following = ["following/\(key)" : businessUid]
let followers = ["followers/\(key)" : userID]
ref.child("Businesses").child(userID).updateChildValues(following as Any as! [AnyHashable : Any])
ref.child("Businesses").child(businessUid).updateChildValues(followers)
self.yourFollowing.removeAll()
self.following.removeAll()
self.followingTableView.reloadData()
}
})
}
Your problem is this line in your button action
#IBAction func followButton(_ sender: UIButton) {
.
.
var isFollower = false
.
.
}
You are declaring the variable isFollow inside the button action. This means that every time regardless of follow or unfollow, your isFollower is false which is why the condition for follow will work. But the change to true which is inside the completion of follow won't reflect next time you click the button because you are resetting isFollower to false.
Solution: Move the variable isFollow outside the button action.
var isFollow = false
#IBAction func followButton(_ sender: UIButton) {
// Your logic
}
Also your logic inside the completion seems wrong. Something like the code below should be there to change it to false.
if value as! String == businessUid {
isFollower = !isFollower
if isFollower {
// Follow logic
sender.setTitle("unfollow", for: .normal)
} else {
// Unfollowed logic
sender.setTitle("follow", for: .normal)
}
// Reload table
}
I'm retrieving mapView annotations posted in Firebase to show them on map, but while converting String values for latitude and longitude to recombine them into CLLocationCoordinates2D I get the error. I don't understand why, because in another function I use the same method but getting the values from arrays but I don't get the error. Also on retrieving the data I would like to also use the key value from firebase as initialiser for my annotations. But I get two more errors Use of unresolved identifier 'firebaseKey' and Use of unresolved identifier 'recombinedCoordinate' for initialisers. Here're the function:
func displayAlerts() {
// FIREBASE: Reference
ref = Database.database().reference()
// FIREBASE:Retrieve posts and listen for changes
databaseHandle = ref?.child("Community").child("Alert Notifications").observe(.childAdded, with: { (snapshot) in
let data = snapshot.value as? [String:String]
if let actualData = data {
let dataLatitude = data!["Latitude"]
let dataLongitude = data!["Longitude"]
self.alertIconToDisplay = data!["Description"]
let doubledLatitude = Double(dataLatitude)
let doubledLongitude = Double(dataLongitude)
var recombinedCoordinate = CLLocationCoordinate2D(latitude: doubledLatitude!, longitude: doubledLongitude!)
print("Firebase post retrieved !")
self.dummyFunctionToFoolFirebaseObservers()
}
let dataKey = snapshot.key as? String
if let firebaseKey = dataKey {
print("Longitude DataKey is \(String(describing: dataKey))")
print("Longitude Actual DataKey is \(String(describing: firebaseKey))")
self.dummyFunctionToFoolFirebaseObservers()
}
print("fir long \((snapshot.value!, snapshot.key))")
userAlertAnnotation = UserAlert(type: self.alertIconToDisplay, coordinate: recombinedCoordinate, firebaseKey: firebaseKey)
self.mapView.addAnnotation(self.userAlertAnnotation)
})
}
Here's the annotation model :
class UserAlert: NSObject , MKAnnotation {
var type: String?
var firebaseKey: String?
var coordinate:CLLocationCoordinate2D
init(type:String, coordinate:CLLocationCoordinate2D, firebaseKey: String) {
self.type = type
self.firebaseKey = firebaseKey
self.coordinate = coordinate
}
}
What am I doing wrong here? I understand that the error on the initialisers are because initialisation occurs in key closures, but how then I incorporate all data into initialiser ?
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annotationView = MKAnnotationView(annotation: userAlertAnnotation, reuseIdentifier: "") // CHANGE FOR NEW ANNOTATION : FULL DATA
//added if statement for displaying user location blue dot
if annotation is MKUserLocation{
return nil
} else {
annotationView.image = UIImage(named: alertIconToDisplay!) // choose the image to load
let transform = CGAffineTransform(scaleX: 0.27, y: 0.27)
annotationView.transform = transform
return annotationView
}
}
func postAlertNotification() {
// to set next notification id as the position it will have in array ( because first position is 0 ) we use the array.count as value
let latitude = alertNotificationLatitude
let longitude = alertNotificationLongitude
let alertType = alertNotificationType
let post: [String:String] = [//"Date" : date as! String,
//"Time" : time as! String,
"Latitude" : latitude as! String,
"Longitude" : longitude as! String,
"Description" : alertType as! String]
var ref: DatabaseReference!
ref = Database.database().reference()
ref.child("Community").child("Alert Notifications").childByAutoId().setValue(post)
}
The error in the topic says that you can't create a Double from an optional String which is true.
To solve it force unwrap the values for Latitude and Longitude.
But the main issue is a scope issue, all variables used in the initializer must be in the same scope. You can flatten the scope with guard statements:
...
databaseHandle = ref?.child("Community").child("Alert Notifications").observe(.childAdded, with: { (snapshot) in
defer { self.dummyFunctionToFoolFirebaseObservers() }
guard let data = snapshot.value as? [String:String] else { return }
guard let firebaseKey = snapshot.key as? String else { return }
// let date = data!["Date"]
// let time = data!["Time"]
let dataLatitude = data["Latitude"]!
let dataLongitude = data["Longitude"]!
self.alertIconToDisplay = data["Description"]!
let doubledLatitude = Double(dataLatitude)
let doubledLongitude = Double(dataLongitude)
let recombinedCoordinate = CLLocationCoordinate2D(latitude: doubledLatitude!, longitude: doubledLongitude!)
print("Firebase post retrieved !")
// self .keyaLon = dataKey
// self.keyaLonArray.append(firebaseKey)
print("Longitude Actual DataKey is \(String(describing: firebaseKey))")
print("fir long \((snapshot.value!, snapshot.key))")
self.userAlertAnnotation = UserAlert(type: self.alertIconToDisplay, coordinate: recombinedCoordinate, firebaseKey: firebaseKey)
self.mapView.addAnnotation(self.userAlertAnnotation)
})
I have two custom cells in one table view.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Configure the cell...
if (indexPath.row == 0) {
let cell = tableView.dequeueReusableCell(withIdentifier: "Main", for: indexPath) as! PostTableViewCell
//Configure the cell
cell.PostView.layer.cornerRadius = 5
cell.PostView.layer.masksToBounds = false
cell.PostView.layer.shadowColor = UIColor.black.withAlphaComponent(0.4).cgColor
cell.PostView.layer.shadowOffset = CGSize(width: 0, height: 0)
cell.PostView.layer.shadowOpacity = 0.9
let post = Comments[indexPath.row] as! [String: AnyObject]
let commentname = post["author"] as? String
sendAuthor = post["author"] as? String
cell.CommentersName.setTitle(commentname, for: .normal)
if let seconds = post["pub_time"] as? Double {
let timeStampDate = NSDate(timeIntervalSince1970: seconds/1000)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM d, yyyy"
let formating = timeStampDate as Date
cell.CommentTime.text = dateFormatter.string(from: formating)
}
cell.comment.text = post["content"] as? String
textViewDidChange(cell.comment)
cell.comment.frame.size.width = 344
cell.comment.sizeToFit()
cell.comment.clipsToBounds = true
cell.REply.frame.origin.y = cell.comment.frame.maxY + 10
cell.PostView.frame.size.height = cell.comment.frame.maxY + 50
TableView.rowHeight = cell.PostView.frame.size.height + 20
cell.LikesNumber.text = post["num_likes"] as? String
replyId = post["id"] as? String
cell.checkfornightmode()
return cell
}
else{
let cell = tableView.dequeueReusableCell(withIdentifier: "Reply", for: indexPath) as! RepliesTableViewCell
cell.ReplyCustomCell.layer.cornerRadius = 5
cell.ReplyCustomCell.layer.masksToBounds = false
cell.ReplyCustomCell.layer.shadowColor = UIColor.black.withAlphaComponent(0.4).cgColor
cell.ReplyCustomCell.layer.shadowOffset = CGSize(width: 0, height: 0)
cell.ReplyCustomCell.layer.shadowOpacity = 0.9
let post = Comments[indexPath.row] as! [String: AnyObject]
let posttest = post["id"] as? String
let replyRef = Database.database().reference().child("main").child("posts").child(postID!).child("comments").child(posttest!).child("comments")
replyRef.observeSingleEvent(of: .value, with: { (snapshot:DataSnapshot) in
if let postsDictionary = snapshot .value as? [String: AnyObject] {
for testingkey in postsDictionary.keys {
Database.database().reference().child("main").child("posts").child(self.postID!).child("comments").child(posttest!).child("comments").child(testingkey).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let content : String? = value?["content"] as? String ?? ""
cell.ReplyText.text = content!
})
}
}
})
TableView.rowHeight = 150.0
return cell
}
}
The first cell with the identifier main is supposed to print out all the intial comments for a certain post. The second cell with the identifier is supposed to print out the comments to main comments. Based on this code, I am only getting the last comment of the main post and the last comment to the main posts.
This is what the json looks like
First thing is to separate the firebase call into a separate function and in that function populate a dictionary with posts as key and comments as value
for example like this
var postComments: [String: [String]] = [:] // post as key and string array as comments
In firebase database call populate this with snapshot.
After the data call but within the firebase Database call back use this function to reload data
DispatchQueue.main.async{
tableView.reloadData()
}
Set the cell with postComments array.
This is my second version me trying to retrieve code from Firebase and do stuff with it. This is how I done it the second way:
channelRef?.observe(.childChanged, with: { (snapshot) -> Void in
let update = snapshot.value as! Dictionary<String, AnyObject>
var readyToGoValue: Bool?
var userID: String?
var amountOfPlayers: Int?
var changedCreator: String?
if let updatedReadyToGo = update["readyToGo"] as! Bool!{
if updatedReadyToGo == true
{
readyToGoValue = true
}
else
{
readyToGoValue = false
}
}
if let updateduserID = update["userID"] as! String!{
userID = updateduserID
}
if let updatedAmountOfPlayers = update["currentPlayers"] as! Int!{
amountOfPlayers = updatedAmountOfPlayers
}
if let updateduserID = update["userID"] as! String!{
userID = updateduserID
}
if let updatedCreator = update["creator"] as! String!{
changedCreator = updatedCreator
}
let currentUser = FIRAuth.auth()?.currentUser?.uid
if changedCreator != nil
{
print("changed creator")
self.creator = changedCreator!
}
This crashed with an error code:
Could not cast value of type '__NSCFString' (0x10a77f4a0) to 'NSDictionary' (0x10a780288). at the line " update". This was my first attempt:
channelRef?.observe(.childChanged, with: { (snapshot) -> Void in
let value = snapshot.value as? NSDictionary
let readyToGoValue = value?["readyToGo"] as? Bool ?? false
let userID = value?["userID"] as? String ?? ""
var amountOfPlayers = value?["currentPlayers"] as? Int ?? 0
let changedCreator = value?["creator"] as? String ?? ""
print(snapshot)
let currentUser = FIRAuth.auth()?.currentUser?.uid
print(changedCreator)
print(amountOfPlayers)
if changedCreator != ""
{
print("changed creator")
self.creator = changedCreator
}
This doesn't work swell. When changing the creator (just a string) in Firebase, I get this as a print when adding print(snapshot):
Snap (creator) hi
However the print("changed creator") never is executed. Why is this?
edit: This is how I got channelRef?:
super.prepare(for: segue, sender: sender)
if let channel = sender as? Channel {
let chatVc = segue.destination as! channelMultiplayerViewController
chatVc.channel = channel
chatVc.channelRef = channelRef.child(channel.id)
chatVc.usersKey = userKey
}
Printing more data:
print("path channel ref: " + "\(self.channelRef)")
print("snapshot: " + "\(snapshot)")
print("value: " + "\(value)")
-path channel ref: Optional(https://X.com/channels/-KeGKaJavH6uPYaSa7k4)
-snapshot: Snap (creator) new Creator
-value: nil
Update:
data structure:
This will work for now, but isn't there a better approach?:
if snapshot.key == "creator"
{
changedCreator = snapshot.value as! String
}
Another problem, exactly the same as above but with the solution for the first problem, this problem won't get solved. When I try to get the first child node, so the first user, and trying to get their userID, nothing works. I use this code:
let firstChild = UInt(1)
self.channelRef?.queryLimited(toFirst: firstChild).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
print(snapshot)
print(value)
let newCreator = value?.value(forKey: "userID") as? String
if newCreator != nil{
print("Got the userID")
}
if snapshot.key == "userID"
{
print("Got the userID")
}
})
Snap (-KeJWMiXaL-FGp0J7b3u) {
"-KeJWO0V9kxgGnrACAtP" = {
PictureVersion = 2;
readyToGo = 0;
userID = SZlQ76RLCJQpFa0CDhrgFJoYzrs2;
username = pietje;
};
}
Optional({
"-KeJWO0V9kxgGnrACAtP" = {
PictureVersion = 2;
readyToGo = 0;
userID = SZlQ76RLCJQpFa0CDhrgFJoYzrs2;
username = pietje;
};
})
And this prints out, so no userID is given. Why is this? The userID is right there! I read the docs but it should work...
Try replacing chatVc.channelRef = channelRef.child(channel.id) with chatVc.channelRef = channelRef
For some reason this code works perfectly in xcode6.4, but when switching to xcode7 it freezes the app.
What I am trying to do is pull the post information on a user's feed and display it on a tableview. I am able to pull the information from Firebase, but the app freezes before it displays on the tableview.
EDIT: The tableview works when I do not have any constraints or autolayout. It seems to not work when I try to have dynamic cell heights.
func getRadarData() {
let url = "https://(insert appname).firebaseio.com/users/" + currentUser + "/postsReceived/"
let targetRef = Firebase(url: url)
targetRef.observeEventType(.ChildAdded, withBlock: {
snapshot in
print("child")
if let found = self.posts.map({ $0.key }).indexOf(snapshot.key) {
let obj = self.posts[found]
print(obj)
print(found)
self.posts.removeAtIndex(found)
}
let postsUrl = "https://(insert appname).firebaseio.com/posts/" + snapshot.key
let postsRef = Firebase(url: postsUrl)
var updatedAt = snapshot.value["updatedAt"] as? NSTimeInterval
var endAt = snapshot.value["endAt"] as? NSTimeInterval
if updatedAt == nil {
updatedAt = 0
}
if endAt == nil {
endAt = 0
}
postsRef.observeSingleEventOfType(.Value, withBlock: { snapshot in
if let key = snapshot.key
{if let content = snapshot.value["content"] as? String {
if let creator = snapshot.value["creator"] as? String {
if let createdAt = snapshot.value["createdAt"] as? NSTimeInterval {
let userurl = "https://(insert appname).firebaseio.com/users/" + (creator)
let userRef = Firebase(url: userurl)
userRef.observeSingleEventOfType(.Value, withBlock: { snapshot in
if let username = snapshot.value["username"] as? String {
let updatedDate = NSDate(timeIntervalSince1970: (updatedAt!/1000))
let createdDate = NSDate(timeIntervalSince1970: (createdAt/1000))
let endedDate = NSDate(timeIntervalSince1970: (endAt!))
let post = Post(content: content, creator: creator, key: key, createdAt: updatedDate, name: username, joined: true, messageCount: 0, endAt: endedDate)
self.posts.append(post)
// Sort posts in descending order
self.posts.sortInPlace({ $0.createdAt.compare($1.createdAt) == .OrderedDescending })
self.tableView.reloadData()
}
})
}
}
}
}
})
})
}
Here is my code for my tableview where I used autolayout on the textView and nameLabel
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: RadarTableViewCell = tableView.dequeueReusableCellWithIdentifier("radarCell", forIndexPath: indexPath) as! RadarTableViewCell
let creator: (String) = posts[indexPath.row].creator
let key = posts[indexPath.row].key
let radarContent: (AnyObject) = posts[indexPath.row].content
cell.textView.selectable = false
cell.textView.text = radarContent as? String
cell.textView.userInteractionEnabled = false
cell.textView.selectable = true
let radarCreator: (AnyObject) = posts[indexPath.row].name
cell.nameLabel.text = radarCreator as? String
return cell
The issue was that I had initial text in my textView. I deleted it on my Storyboard and my app works now.
Found the solution here: Why does a previously working Xcode project hang up in Xcode 7 when presenting a new UITableviewController Subclass?