How to access Array created in closure? - ios

I created scheduleDict1 and inserted it into scheduleArray1. I can only get the closure to "see" scheduleArray1 when it is declared in the closure as in the code now. However, I can't access scheduleArray1 anywhere other than the closure.
I have tried declaring the scheduleArray1 in the MainViewController class instead of the closure but it will not be seen inside the closure!
import UIKit
import Firebase
class MainViewController: UITableViewController {
// var scheduleArray1 = [[String: String]]()
var scheduleDict = [String: Any]()
override func viewDidLoad() {
super.viewDidLoad()
retrieveSchedule()
}
func retrieveSchedule(){
let ref = Database.database().reference(withPath: "squadrons/vt-9/2019-04-02/events/")
ref.observe(.value) { (snapshot) in
var scheduleArray1 = [[String: String]]()
var count = 0
let enumerator = snapshot.children
while let rest = enumerator.nextObject() {
let refToPost = Database.database().reference(withPath: "squadrons/vt-9/2019-04-02/events/" + "\(count)")
refToPost.observe(.value, with: { (snapshot) in
// let data = snapshot.children
let scheduleDict1 = snapshot.value as! [String: String]
scheduleArray1.append(scheduleDict1)
// self.print(scheduleArray1)
})
count += 1
}
}
}
}

Related

TableView not reloading after .childRemoved from external Firebase server

I'm having trouble with loading the table view. The moment I call self.messagesTable.reloadData() my local tableView does not reload the data deleted from a external Firebase server.
These two are my handlers for the observer:
fileprivate var _refHandle: FIRDatabaseHandle!
fileprivate var _refHandleDel: FIRDatabaseHandle!
Outlet
#IBOutlet weak var messagesTable: UITableView!
Code in FCViewController:
var ref: FIRDatabaseReference!
var messages: [FIRDataSnapshot]! = []
var messageClass = [Message]()
var messageDictionary = [String: Message]()
func configureDatabase() {
// configure database to sync messages and connect it to the data base starting at "/"
ref = FIRDatabase.database().reference()
// create a listener on what happend on the database
_refHandle = ref.child("messages").observe(.childAdded) { (snapshot: FIRDataSnapshot!) in
self.messages.append(snapshot)
// Animate and scroll the new message loaded
self.messagesTable.insertRows(at: [IndexPath(row: self.messages.count - 1, section: 0)], with: .automatic)
self.scrollToBottomMessage()
}
_refHandleDel = ref.child("messages").observe(.childRemoved, with: { (snapshot: FIRDataSnapshot!) in
self.messageDictionary.removeValue(forKey: snapshot.key)
// MY PROBLEM IS HERE: The table does not load properly and does not reloadData from the server after deleting the snapshot.key
self.messagesTable.reloadData()
}, withCancel: nil)
}
deinit {
// set up what needs to be deinitialized when view is no longer being used
// we remove the observer that is all the time checking for updates in the .observer handler
ref.child("messages").removeObserver(withHandle: _refHandle)
ref.child("messages").removeObserver(withHandle: _refHandleDel)
// we also remove the listener for auth changes when the user registers
FIRAuth.auth()?.removeStateDidChangeListener(_authHandle)
}
Scroll Messages Function also inside the FCViewController:
func scrollToBottomMessage() {
if messages.count == 0 { return }
let bottomMessageIndex = IndexPath(row: messagesTable.numberOfRows(inSection: 0) - 1, section: 0)
messagesTable.scrollToRow(at: bottomMessageIndex, at: .bottom, animated: true)
}
Message Class Object in a separate .swift file courtesy of #Jay
class Message: NSObject {
class MessageClass {
var key = ""
var name = ""
var text = ""
var timestamp = ""
}
var messagesArray = [MessageClass]()
let ref = FIRDatabase.database().reference()
func readInAllMessages() {
let messagesRef = ref.child("messages")
messagesRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let snap = child as! FIRDataSnapshot
let msg = self.snapToMsgClass(child: snap)
self.messagesArray.append(msg)
}
})
}
func addRemoveObserver() {
let messagesRef = ref.child("messages")
messagesRef.observe(.childRemoved, with: { snapshot in
let keyToRemove = snapshot.key
if let i = self.messagesArray.index(where: { $0.key == keyToRemove }) {
self.messagesArray.remove(at: i)
}
})
}
func snapToMsgClass(child: FIRDataSnapshot) -> MessageClass {
let dict = child.value as! [String:Any]
let name = dict["name"] as! String
let msg = MessageClass()
msg.name = name
msg.key = child.key
return msg
}
}

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

Can't get value of node in Firebase

I read a lot examples and most of them have old style (even they are written current year). Please help understand where my code is wrong? It is built but I can't get a value 123456.
import UIKit
import Firebase
class ViewController: UIViewController {
#IBOutlet weak var val_txt: UITextField!
let ref = FIRDatabase.database().reference()
var barcode = 0
override func viewDidLoad() {
super.viewDidLoad()
FIRAuth.auth()?.signIn(withEmail: "*****#gmail.com", password: "*****", completion: {(user,error) in print("Авторизация Ок!!!")})
}
#IBAction func getData_btn(_ sender: Any) {
ref.child("goods").child("1").observe(FIRDataEventType.value, with: {(snapshot) in
let postDict = snapshot.value as? [String:AnyObject] ?? [:]
print(postDict["barcode"] as? Int)
})
print(barcode)
}
I've change code in order to understand Does print execute and I found that it doesn't
print("Method started")
ref.child("goods").child("1").observe(FIRDataEventType.value, with:{(snapshot) in
let postDict = snapshot.value as? [String:AnyObject] ?? [:]
print("Method is executing")
})
print("Method completed")
And I get just two rows of print
"Method started"
"Method completed"
If you want value only for "1":
var ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
ref.child("goods").child("1").observeSingleEvent(of: .value, with: { (snapshot) in
let id = snapshot.childSnapshot(forPath: "barcode")
print(id)
})
but if you want all barcodes:
var ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
ref.child("goods").observeSingleEvent(of: .value, with: { (snapshot) in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots
{
let barcode = snap.childSnapshot(forPath: "barcode").value! as! String
print(barcode)
}
}
})
In your code let ref = FIRDatabase.database().reference() do not Point to reference URL because during initialization your firebase not configure in your app delegate file (Not called FIRApp.configure()).
So put in func viewDidLoad() as follow:
import UIKit
import Firebase
class ViewController: UIViewController {
#IBOutlet weak var val_txt: UITextField!
let ref:FIRDatabaseReference!
var barcode = 0
override func viewDidLoad() {
super.viewDidLoad()
ref:FIRDatabaseReference = FIRDatabase.database().reference()
FIRAuth.auth()?.signIn(withEmail: "*****#gmail.com", password: "*****", completion: {(user,error) in print("Авторизация Ок!!!")})
}
Use below code it will help you.
let ref = FIRDatabase.database().reference()
ref.child("goods").child("1").observe(DataEventType.value, with: {(snapshot) in
if snapshot.childrenCount>0 {
for transactions in snapshot.children.allObjects as! [DataSnapshot] {
var obj = transactions.value as! [String:AnyObject]
print(obj["barcode"] as! Int)
}
}
})

Retrieving objects from firebase

I'm working on simple program - I create objects of products and then I count their calories.
I want to count sum of all calories of my products.
I've created a method, allowing me to save data properly in Firebase, but I got stuck while retrieving them:
import UIKit
import Firebase
class TotalViewController: UIViewController {
var products = [Products]()
#IBOutlet weak var calotyCounter: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
DataService.dataService.PRODUCT_BASE.observeEventType(.Value, withBlock: { snapshot in
print(snapshot.value)
self.products = []
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots {
if let postDictionary = snap.value as? Dictionary<String, AnyObject> {
let key = snap.key
let product = Products(key: key, dictionary: postDictionary)
}
}
}
self.updateCalory()
})
// Do any additional setup after loading the view.
}
func updateCalory() {
var CaloryArray: [Int] = []
for product in products {
CaloryArray.append(Int(product.productCalories))
}
print (CaloryArray)
calotyCounter.text? = String(CaloryArray.reduce(0, combine: +))
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
I got an empty array, instead of array of objects callory value.
Here is my model of Products.I made it through dictionary
import Foundation
import Firebase
class Products {
private var _productName: String!
private var _productCalories: Int!
var productName: String {
return _productName
}
var productCalories: Int {
return _productCalories
}
init(key: String, dictionary: Dictionary<String, AnyObject>) {
if let calories = dictionary["calories"] as? Int {
self._productCalories = calories
}
if let name = dictionary["name"] as? String {
self._productName = name
}
}
}
What I'm doing wrong?
You have only initiated the empty array of products in viewDidLoad()
self.products = []
and not assigning any thing to it anywhere. thats why you are getting the empty array.
and on updateCalory() method you are looping on empty array (array with zero items)
EDIT 1
you must append the product i.e
let product = Products(key: key, dictionary: postDictionary)
to your products array in loop. like this
for snap in snapshots {
if let postDictionary = snap.value as? Dictionary<String, AnyObject> {
let key = snap.key
let product = Products(key: key, dictionary: postDictionary)
self. products.append(product) // add this line
}
}

Resources