TableView Read Data from Firebase - ios

This is my code
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var ref: DatabaseReference?
var postData = [String]()
var databaseHandle: DatabaseHandle?
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
ref = Database.database().reference()
databaseHandle = ref?.child("Posts").observe(DataEventType.value, with: { (snapshot)in
let post = snapshot.value as? String
if let actualPost = post {
self.postData.append(actualPost)
self.tableView.reloadData()
}
})
}
}
I want my UITable to read any changes I make in my firebase console but with this code nothing shows up on the UITable. When I change it to DataEventType.childChanged, it works but it only adds new posts and not changes to previous post. What am I doing wrong? I just want all changes to show on the UITable that I make on Firebase.
Thanks!

Related

App crashes at click with message: 'this class is not key value coding-compliant for the key postReplyButton.'

My app crashes when I click a cell in my tableView of recent posts. The click is supposed to segue me to the MainTextView which has the postReplyButton. The segue worked until I started experimenting with creating comments for the posts.
Here is the MainTextView code:
import Foundation
import UIKit
import Firebase
class MainTextView: UIViewController {
#IBOutlet weak var titleText: UILabel!
#IBOutlet weak var mainText: UILabel!
#IBOutlet weak var commentPlaceHolder: UILabel!
#IBOutlet weak var newCommentLabel: UITextView!
var delegate:NewPostVCDelegate?
#IBAction func postReplyButton() {
// Firebase code here
let postRef = Database.database().reference().child("posts").childByAutoId()
let postObject = [
"comment": newCommentLabel.text,
"timestamp": [".sv": "timestamp"]
] as [String : Any]
postRef.setValue(postObject, withCompletionBlock: { error, ref in
if error == nil {
self.delegate!.didUploadPost(withID: ref.key!)
self.dismiss(animated: true, completion: nil)
} else {
// Handle error
}
})
newCommentLabel.text = String()
commentPlaceHolder.isHidden = false
}
var post: Post?
// MARK: - View Controller LifeCycle
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.setMain()
}
override func viewDidLoad() {
super.viewDidLoad()
newCommentLabel.delegate = self as! UITextViewDelegate
}
private func setMain() {
guard let post = self.post else {
return
}
titleText.text = post.text
mainText.text = post.title
}
func textViewDidChange(_commentView: UITextView) {
commentPlaceHolder.isHidden = !newCommentLabel.text.isEmpty
}
}
For reference, here is my Post class code:
import Foundation
class Post {
var id:String
var title: String
var text:String
var createdAt:Date
var comment: [String] = []
init(id: String, title: String,text:String, timestamp:Double, comment: [String] = []) {
self.id = id
self.title = title
self.text = text
self.createdAt = Date(timeIntervalSince1970: timestamp / 1000)
}
static func parse(_ key:String, data:[String:Any]) -> Post? {
if let title = data["text"] as? String,
let text = data["title"] as? String,
let timestamp = data["timestamp"] as? Double {
return Post(id: key, title: title, text: text, timestamp:timestamp, comment: [])
}
return nil
}
}
I suspect the issue may be with the delegate, which was declared as such in my NewPostViewController:
protocol NewPostVCDelegate {
func didUploadPost(withID id:String)
}
I have tried troubleshooting the storyboard, but everything seems to be in place. Is there an issue of the reuse of the protocol or perhaps the change of adding comments to the Post class itself? Maybe the issue is that I do not in fact want to upload a new post, but really I just want to add a comment to an existing post. If this is the case, how would I change the delegate or create a new one? I can provide more detail if needed. Thank you for your help.
This usually happens if you have an IBOutlet that was created previously with the same postReplyButton name. To check if your app has any other Outlet with the same name go to the Search section in your project and search for postReplyButton and see if you get multiple results for that name. If you do then click on the one which you don't need and delete it from the properties section.
If you have any Outlet which has a bad connection you will see something like this in the properties sections when you click on any one of the search result for postReplyButton
If that does not work then try renaming the Outlet entirely and see if that fixes the problem.
EDITED:
For your issue that you mentioned in the comments try this.
Instead of casting your newCommentLabel as an optional type of UITextViewDelegate just extend your viewController to conform to UITextViewDelegate. This should solve the issue.
class MainTextView: UIViewController, UITextViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
newCommentLabel.delegate = self
}
}
Once you add UITextViewDelegate to your viewController you will no longer get the warning in viewDidLoad to cast newCommentLabel as an optional of type UITextViewDelegate.

how do i get the value of self into the normal Variable

how can i get the value of self.valve1 from function readFireBaseData() into the function viewDidload() ??? i always get the set value of "valve1" which i set at the begining of the program. Hope someone can help me.
import UIKit
import FirebaseDatabase
import Firebase
class ValveViewController: UIViewController {
var valve1 = "OFF"
#IBOutlet var valveOne: UILabel!
override func viewDidLoad() {
readFirebaseData() //run the function readFireBaseData
let toggle = view.viewWithTag(1) as! UISwitch
if valve1 == "ON"
{
toggle.setOn(true, animated: true)
}else
{
toggle.setOn(false, animated: true)
}
super.viewDidLoad()
}//END of viewDidLoad
func readFirebaseData(){
var ref: DatabaseReference!
ref = Database.database().reference()
ref.child("switchValve").observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
self.valve1 = value?["valveOne"] as? String ?? ""
})
}//End of readFireBaseData
}//End of ViewController

Accessing Array Outside Closure in Swift 3

I have this closure which I use to populate my array and dictionary. However, when I try to use it outside the function, it's empty. I understand that this closure works on an asynchronous thread, so is it right to assume that I try to access that variable before it's been populated? As a result, I get an empty array. Here is my code.
class HomeCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, UISearchBarDelegate, UIGestureRecognizerDelegate {
var entries = [String: DiaryEntry]()
var entryIDS = [String]()
var searchController: UISearchController!
override func viewDidLoad() {
super.viewDidLoad()
// Register cell classes
self.collectionView!.register(DiaryCell.self, forCellWithReuseIdentifier: "homeCell")
collectionView?.backgroundColor = UIColor.white
navigationController?.hidesBarsOnSwipe = true
if let userID = FIRAuth.auth()?.currentUser?.uid {
FirebaseService.service.getUserEntriesRef(uid: userID).observe(.value, with: { [weak weakSelf = self] (snapshot) in
let enumerator = snapshot.children
while let entry = enumerator.nextObject() as? FIRDataSnapshot {
weakSelf?.entryIDS.append(entry.key)
weakSelf?.entries[entry.key] = DiaryEntry(snapshot: entry)
}
weakSelf?.entryIDS.reverse()
weakSelf?.collectionView?.reloadData()
})
print("Entries: \(entryIDS.count) ")
}
// Do any additional setup after loading the view.
}
What's the best way to deal with such a multithreaded execution?
I follow the coding standards (for Swift) of Raywenderlich and his team. If I'm going to re-write your code to have a strong self, it would be like this:
class HomeCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, UISearchBarDelegate, UIGestureRecognizerDelegate {
var entries = [String: DiaryEntry]()
var entryIDS = [String]()
var searchController: UISearchController!
override func viewDidLoad() {
super.viewDidLoad()
// Register cell classes
self.collectionView!.register(DiaryCell.self, forCellWithReuseIdentifier: "homeCell")
collectionView?.backgroundColor = UIColor.white
navigationController?.hidesBarsOnSwipe = true
if let userID = FIRAuth.auth()?.currentUser?.uid {
FirebaseService.service.getUserEntriesRef(uid: userID).observe(.value, with: {
[weak self] (snapshot) in
guard let strongSelf = self else {
return
}
let enumerator = snapshot.children
while let entry = enumerator.nextObject() as? FIRDataSnapshot {
strongSelf.entryIDS.append(entry.key)
strongSelf.entries[entry.key] = DiaryEntry(snapshot: entry)
}
strongSelf.entryIDS.reverse()
strongSelf.collectionView?.reloadData()
})
print("Entries: \(entryIDS.count) ")
}
// Do any additional setup after loading the view.
}
I hope this works. I'm writing my code like this too when connecting to any APIs, such as Firebase and Alamofire.
Use Dispatch Groups to keep track of when you're done appending all the elements to your array, then make a notify callback that'll automatically be called when they're all added.
class HomeCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, UISearchBarDelegate, UIGestureRecognizerDelegate {
var entries = [String: DiaryEntry]()
var entryIDS = [String]()
let dispatchGroup = DispatchGroup()
var searchController: UISearchController!
override func viewDidLoad() {
super.viewDidLoad()
// Register cell classes
self.collectionView!.register(DiaryCell.self, forCellWithReuseIdentifier: "homeCell")
collectionView?.backgroundColor = UIColor.white
navigationController?.hidesBarsOnSwipe = true
self.dispatchGroup.enter()
if let userID = FIRAuth.auth()?.currentUser?.uid {
FirebaseService.service.getUserEntriesRef(uid: userID).observe(.value, with: { [weak weakSelf = self] (snapshot) in
let enumerator = snapshot.children
while let entry = enumerator.nextObject() as? FIRDataSnapshot {
weakSelf?.entryIDS.append(entry.key)
weakSelf?.entries[entry.key] = DiaryEntry(snapshot: entry)
}
self.dispatchGroup.leave()
})
self.dispatchGroup.notify(queue: DispatchQueue.main, execute: {
print("Entries: \(entryIDS.count) ")
weakSelf?.entryIDS.reverse()
weakSelf?.collectionView?.reloadData()
})
}
// Do any additional setup after loading the view.
}
I'd also recommend just using self instead of weakSelf?.
I just came across your problem, while searching for a solution of the same problem and i finally managed to figure out. so, i will try to answer it would be helpful for many others coming behind us.
The problem is that the array exists only inside the closure. So, the solution is to make an array outside of viewDidLoad and set it using once you have the complete array, then use didSet to set entryIDS
var entryIDS = [String]() {
didSet {
//something
}
}

Sort Firebase data by date

I keep getting this error and I failed to debug:
Could not cast value of type 'FIRDatabaseQuery' (0x10b32b700) to 'FIRDatabaseReference' (0x10b32b520).
That error comes from a regular .swift file with:
import Foundation
import Firebase
import FirebaseDatabase
let DB_BASE = FIRDatabase.database().reference()
class DataService {
static let ds = DataService()
private var _REF_BASE = DB_BASE
private var _REF_INCOMES = DB_BASE.child("incomes").queryOrdered(byChild: "date")
private var _REF_USERS = DB_BASE.child("users")
var REF_BASE: FIRDatabaseReference {
return _REF_BASE
}
var REF_INCOMES: FIRDatabaseReference {
return _REF_INCOMES as! FIRDatabaseReference // Thread 1: signal SIGABRT
}
[...]
}
Before adding .queryOrdered(byChild: "date") and as! FIRDatabaseReference everything worked except that I could not get a sort by date.
class IncomeFeedVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var incomes = [Income]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
DataService.ds.REF_INCOMES.observe(.value, with: { (snapshot) in
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshot {
if let incomeDict = snap.value as? Dictionary<String, AnyObject> {
let key = snap.key
let income = Income(incomeId: key, incomeData: incomeDict)
self.incomes.append(income)
}
}
}
self.tableView.reloadData()
})
}
[...]
}
What am I after? To start, I need to sort my date then work towards my Sketch view:
How do you sort? Few tutorials I see uses CoreData. Im using Firebase.
your private var _REF_INCOMES is FIRDatabaseQuery not FIRDatabaseReference ..
var REF_INCOMES: FIRDatabaseQuery {
return _REF_INCOMES
}
And please check this Q&A to sort your array

TableView data not showing after updating Firebase code (Swift 2, Firebase 3.x)

Have been updating my project to the newest firebase code and am now running into an issue.
Yesterday my app was working fine w/ the updates I implemented and all of the sudden my tableView is no longer displaying data and it's not even printing the data in my console.
Here's my code:
import UIKit
import Firebase
import MGSwipeTableCell
class FeedVCViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var posts = [Post]()
static var imageCache = NSCache()
var isPostHidden = false
var likeRef: FIRDatabaseReference!
var dislikeRef: FIRDatabaseReference!
var postRef: FIRDatabaseReference!
var postLiked = false
var postDisliked = false
private var _post: Post?
var post: Post? {
return _post
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 38, height: 38))
imageView.contentMode = .ScaleAspectFit
let image = UIImage(named: "unilogo2Whtie")
imageView.image = image
navigationItem.titleView = imageView
DataService.ds.REF_POSTS.observeEventType(.Value, withBlock: { snapshot in
print(snapshot.value)
self.posts = []
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots {
print("SNAP: \(snap)")
if let postDict = snap.value as? Dictionary<String, AnyObject> {
let key = snap.key
let post = Post(postKey: key, dictionary: postDict)
self.posts.append(post)
}
}
}
self.tableView.reloadData()
})
// Do any additional setup after loading the view.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
I must have done something wrong in the update I'm assuming, can anyone help?
I'm surprised you're not getting an error on the following lines:
var likeRef: FIRDatabaseReference!
var dislikeRef: FIRDatabaseReference!
var postRef: FIRDatabaseReference!
For me, I had to add the following line below import Firebase in order not to get an error when declaring the database reference:
import FirebaseDatabase
Then in your viewDidLoad method you need to add the reference to your database
likeRef = FIRDatabase.database().reference()
However, with this new version of Firebase, you don't need a separate reference for likeRef, dislikeRef and postRef. You can make just one main reference and then access the child of the reference for those details.

Resources