Retrieving objects from firebase - ios

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

Related

How to populate an object array using a Firebase Database

I'm unable to create a function which returns an array of Product objects from my Firebase database.
I attempted using a switch within the closure to set variables to their respective names, and then instantiate the object after all variables have been set. I tried to play around with the scope of the closure to try and access the "retrieved value". Below is a snapshot of my Firebase Database, my product object, and my function.
func createArray() -> [Product] {
var newArray = [Product]()
let ref = Database.database().reference()
var productName = String()
var productHealth = String()
var productPrice = 0
var productImage = String()
for bigIndex in 0..<7 {
for smallIndex in 0..<4{
ref.child("masterSheet/\(bigIndex)/\(smallIndex)").observeSingleEvent(of: .value) { (snapshot) in let retrievedValue = snapshot.value}
//I used to have a switch statement here that went like
//switch smallIndex{ case 0: productPrice = retrievedValue as! String }
}
let completeProduct = Product(productName: productName, productHealth: productHealth, productPrice: productPrice, productImage: productImage)
newArray.append(completeProduct)
}
return newArray
}
Product Object:
import Foundation
import UIKit
class Product {
var productName: String
var productHealth: String
var productPrice: Int
var productImage: String
init(productName: String, productHealth: String, productPrice: Int, productImage: String ){
self.productName = productName
self.productHealth = productHealth
self.productPrice = productPrice
self.productImage = productImage
}
}
My goal is to produce an array of all of the items in the Database.
You need to fetch the value from smallIndex and set it into modelClass Product like this:
func createArray() -> [Product] {
var newArray = [Product]()
let ref = Database.database().reference()
for bigIndex in 0..<7 {
for smallIndex in 0..<4{
ref.child("masterSheet/\(bigIndex)/\(smallIndex)").observeSingleEvent(of: .value) { (snapshot) in let retrievedValue = snapshot.value}
//I used to have a switch statement here that went like
//switch smallIndex{ case 0: productPrice = retrievedValue as! String
let singleProduct = Product()
singleProduct.productName = "fetch value from small index"
singleProduct.productPrice = retrievedValue
sigleProduct.productHealth = "fetch value from small index"
newArray.append(singleProduct)
}
}
}
return newArray
}
For anyone wondering, this is the solution that ended up working for me :)
var product: [Product] = []
ref = Database.database().reference()
databaseHandle = ref.child("masterSheet").observe(.value) { (snapshot) in
guard let rawData = snapshot.value as? [AnyObject] else { return }
for item in rawData {
guard let itemArray = item as? [AnyObject] else { continue }
var pro = Product()
if itemArray.count > 0 {
pro.productName = itemArray[0] as? String
}
if itemArray.count > 1 {
pro.productHealth = itemArray[1] as? String
}
if itemArray.count > 2 {
pro.productPrice = itemArray[2] as? Int
}
if itemArray.count > 3 {
pro.productImage = itemArray[3] as? String
}
// if you use dict, do like this:
//pro.productImage = itemArray["productImage"] as? String
//pro.productPrice = itemArray["productPrice"] as? Int
product.append(pro)
}
self.tableView.reloadData()
}

Not able to retrieve right amount of objects from array in model controller when calling a method to get an array of data count

In detail, I am retrieving data from firebase and I am creating an array of restaurants objects from each entry received and Im then performing a count on the number of elements are in the array, which should be 5,23: (restaurant #1)has 5 elements in the array,(restaurant #2) 23 (there's only two restaurants in the dataset). This is being done on my model controller which looks like this
class OverviewModelController {
//MARK: - SharedController
static let sharedController = OverviewModelController()
//MARK: - Source
var restaurantList = [Restaurant]()
var filteredList = [Restaurant]()
//MARK: - FireBase Reference
var ref: DatabaseReference?
func fetchAllData() {
self.ref = Database.database().reference()
self.ref?.child("restaurants").observe( .childAdded , with: { (snap) in
guard let topArray = snap.value as? [[String:Any]] else {print(":(") ; return }
var restaurantArray = [Restaurant]()
for dictionary in topArray {
guard let address = dictionary["address"] as? String,
let city = dictionary["city"] as? String,
let inspectionDate = dictionary["inspectionDate"] as? String,
let name = dictionary["name"] as? String,
let major = dictionary["major"] as? Int,
let minor = dictionary["minor"] as? Int,
let violationTitle = dictionary["violationTitle"] as? String else { continue }
//MARK: - creates restaurants from the list above
let restaurant = Restaurant(address: address, city: city, inspectionDate: inspectionDate, name: name, major: major, minor: minor, violationTitle: violationTitle)
//MARK: - Adds a restaurant to restaurant array instance
restaurantArray.append(restaurant)
}
self.restaurantList = restaurantArray
print(self.restaurantList.count)
})
}
Once I run the method above on a viewcontroller, it prints right count from the view did load but if I check the restaurantList count from a method in the viewController I only get 23 as oppose to 5, 23. I guess I am a bit loss on what I'm doing wrong. I know im doing something that only allows me to receive the last restaurant. I assumed I passed the array of data through the shared controller but it doesn't give me the result I want. The viewcontroller is below:
class MapViewController: UIViewController, UISearchBarDelegate {
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var homeSearchBar: DesignableSearchBar!
var finalArray = [Restaurant]()
override func viewDidLoad() {
super.viewDidLoad()
FirebaseApp.configure()
homeSearchBar.delegate = self
fetch()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
func fetch() {
OverviewModelController.sharedController.fetchAllData()
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
print(OverviewModelController.sharedController.restaurantList.count)
self.homeSearchBar.resignFirstResponder()
DispatchQueue.main.async {
self.homeSearchBar.text = ""
}
}

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

How to retrieve data from Firebase to TableViewCell

I'm working on simple program - I create objects of products and then I count their calories.
I want to get all my product in TableViewController
I've created a method, allowing me to save data properly in Firebase, but I got stuck while retrieving them to cell. I got no mistakes, but I don't have any results as well
import UIKit
import Firebase
class MainTableViewController: UITableViewController {
var products = [Products]()
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.tableView.reloadData()
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return products.count
}
override func tableView(tableView:UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ProductCell") as! UITableViewCell?
let ProductItem = products[indexPath.row]
cell?.textLabel?.text = ProductItem.productName
cell?.detailTextLabel?.text = String(ProductItem.productCalories)
return cell!
}
This is my DataService file:
import Foundation
import Firebase
let URL_BASE = FIRDatabase.database().reference()
class DataService {
static let dataService = DataService()
private var _REF_BASE = URL_BASE
private var _USER_BASE = URL_BASE.child("users")
private var _PRODUCЕ_BASE = URL_BASE.child("products")
var REF_BASE: FIRDatabaseReference {
return _REF_BASE
}
var USER_BASE: FIRDatabaseReference {
return _USER_BASE
}
var PRODUCT_BASE: FIRDatabaseReference {
return _PRODUCЕ_BASE
}
var CURRENT_USER_REF: FIRDatabaseReference {
let userID = NSUserDefaults.standardUserDefaults().valueForKey("uid") as! String
let currentUser = URL_BASE.child("users").child(userID)
return currentUser
}
func CreateNewProduct(product: Dictionary<String, AnyObject>) {
let ProductNewRef = DataService.dataService.PRODUCT_BASE.childByAutoId()
ProductNewRef.setValue(product)
}
I can't get what I'm doing wrong. All products are represented as a 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
}
}
}
You intilized the
var products = [Products]()
But not adding any items from firebase callback
if let postDictionary = snap.value as? Dictionary<String, AnyObject> {
let key = snap.key
let product = Products(key: key, dictionary: postDictionary)
}
please add self.products.append(product)
if let postDictionary = snap.value as? Dictionary<String, AnyObject> {
let key = snap.key
let product = Products(key: key, dictionary: postDictionary)
self.products.append(product)
}

how to use updateValue to add an object in Swift

I have a User Struct that I'm casting to Json to be able to get into NSUserDefaults...
import Foundation
struct User {
var name = ""
var stores: [Store] = []
init?(json: [String: AnyObject]) {
if let name = json["name"] as? String,
storesJSON = json["stores"] as? [[String: AnyObject]]
{
self.name = name
self.stores = storesJSON.map { Store(json: $0)! }
} else {
return nil
}
}
init() { }
func toJSON() -> [String: AnyObject] {
return [
"name": name,
"stores": stores.map { $0.toJSON() }
]
}
}
and I am using a Data Manager class (Singleton) to add a new User. But I can't figure out what to pass into updateValue in my addPerson function below? Alternatively is there another way to get this object into NSUserDefaults?
import Foundation
class DataManager {
static let sharedInstance = DataManager()
var users = [String : User]()
init() {
let userDefaults = NSUserDefaults.standardUserDefaults()
if let var userFromDefaults = userDefaults.objectForKey("users") as? [String : User] {
users = userFromDefaults
}
else {
// add default values later
}
}
var userList: [String] {
var list: [String] = []
for userName in users.keys {
list.append(userName)
}
list.sort(<)
return list
}
func addPerson(newUserName: String) {
users.updateValue(User(), forKey: newUserName)
// saveData()
}
You should change your interface of the addPerson function, use addPerson(newUser: User) instead of using addPerson(newUserName: String) as #iosDev82 said:
// Because your addPerson function needs two parameters: a name and a user object
func addPerson(newUser: User) {
users.updateValue(newUser, forKey: newUser.name)
// saveData()
}
so you can:
let newName = textField.text.capitalizedString
let newUser = User(["name": newName, "stores" : []])
DataManager.sharedInstance.addPerson(newUser)
I think you already know how to create a User object. And that is what you should pass as an argument to your following function. Something like this.
var aUser = User(["name": textField.text. capitalizedString])
DataManager.sharedInstance.addPerson(aUser)
func addPerson(newUser: User) {
users[newUser.name] = newUser
// saveData()
}

Resources