class ArtistModel {
var id: String?
var name: String?
var genre: String?
var img: String?
init(id: String?, name: String?, genre: String?, img: String?){
self.id = id
self.name = name
self.genre = genre
self.img = img
}
}
and this my tableviewCell
class addArtistTableViewCell: UITableViewCell {
#IBOutlet weak var lblName: UILabel!
#IBOutlet weak var lblGenre: UILabel!
#IBOutlet weak var img: UIImageView!
and this my viewController
import UIKit
import Firebase
import FirebaseDatabase
class addArtistViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var refArtists: FIRDatabaseReference!
#IBOutlet weak var textFieldName: UITextField!
#IBOutlet weak var textFieldGenre: UITextField!
#IBOutlet weak var img: UIImageView!
#IBOutlet weak var labelMessage: UILabel!
#IBOutlet weak var tableViewArtists: UITableView!
//list to store all the artist
var artistList = [ArtistModel]()
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//getting the selected artist
let artist = artistList[indexPath.row]
//building an alert
let alertController = UIAlertController(title: artist.name, message: "Give new values to update ", preferredStyle: .alert)
//the confirm action taking the inputs
let confirmAction = UIAlertAction(title: "Enter", style: .default) { (_) in
//getting artist id
let id = artist.id
//getting new values
let name = alertController.textFields?[0].text
let genre = alertController.textFields?[1].text
//calling the update method to update artist
self.updateArtist(id: id!, name: name!, genre: genre!)
}
//the cancel action doing nothing
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (_) in }
//adding two textfields to alert
alertController.addTextField { (textField) in
textField.text = artist.name
}
alertController.addTextField { (textField) in
textField.text = artist.genre
}
//adding action
alertController.addAction(confirmAction)
alertController.addAction(cancelAction)
//presenting dialog
present(alertController, animated: true, completion: nil)
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return artistList.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
//creating a cell using the custom class
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! addArtistTableViewCell
//the artist object
let artist: ArtistModel
//getting the artist of selected position
artist = artistList[indexPath.row]
//adding values to labels
cell.lblName.text = artist.name
cell.lblGenre.text = artist.genre
//returning cell
return cell
}
#IBAction func buttonAddArtist(_ sender: UIButton) {
addArtist()
}
override func viewDidLoad() {
super.viewDidLoad()
//getting a reference to the node artists
refArtists = FIRDatabase.database().reference().child("artists")
//observing the data changes
refArtists.observe(FIRDataEventType.value, with: { (snapshot) in
//if the reference have some values
if snapshot.childrenCount > 0 {
//clearing the list
self.artistList.removeAll()
//iterating through all the values
for artists in snapshot.children.allObjects as! [FIRDataSnapshot] {
//getting values
let artistObject = artists.value as? [String: AnyObject]
let artistName = artistObject?["artistName"]
let artistId = artistObject?["id"]
let artistGenre = artistObject?["artistGenre"]
let artistImg = artistObject?["artistImg"]
//creating artist object with model and fetched values
let artist = ArtistModel(id: artistId as! String?, name: artistName as! String?, genre: artistGenre as! String?, img: artistImg as! String?)
//appending it to list
self.artistList.append(artist)
}
//reloading the tableview
self.tableViewArtists.reloadData()
}
})
}
func addArtist(){
//generating a new key inside artists node
//and also getting the generated key
let key = refArtists.childByAutoId().key
//creating artist with the given values
let artist = ["id":key,
"artistName": textFieldName.text! as String,
"artistGenre": textFieldGenre.text! as String,
] //as [String : Any]
//adding the artist inside the generated unique key
refArtists.child(key).setValue(artist)
//displaying message
labelMessage.text = "Artist Added"
}
func updateArtist(id:String, name:String, genre:String){
//creating artist with the new given values
let artist = ["id":id,
"artistName": name,
"artistGenre": genre
]
//updating the artist using the key of the artist
refArtists.child(id).setValue(artist)
//displaying message
labelMessage.text = "Artist Updated"
}
First I'd cache the image.
let imageCache = NSCache<AnyObject, AnyObject>()
extension UIImageView {
func loadImageFromCacheWithUrlSting(urlString: String) {
self.image = nil
if let cachedImage = imageCache.object(forKey: urlString as AnyObject) as? UIImage {
self.image = cachedImage
return
}
let url = URL(string: urlString)
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error!)
}
DispatchQueue.main.async {
if let downloadedImage = UIImage(data: data!) {
imageCache.setObject(downloadedImage, forKey: urlString as AnyObject)
self.image = downloadedImage
}
}
}.resume()
}
This will save you downloading the image every time the view loads. Then add something like this to cellForRow
if let artistImage = dictionary["img"] as? String {
cell?.artistImageView.loadImageFromCacheWithUrlSting(urlString: artistImage)
}
Related
I'm trying to pass data onto my tableview, from my cloud firestore but it just not able to retrieve the data and post it on the tableview and so far most of of my attempts have failed. since I'm transition from real-time database to Firestore.
I've used multiple resources on stack, restructured my code multiple times and have now come down to this
here is also an image of my collection in Firestore firestore collection
import Foundation
class ProductList {
var id: String?
var name: String?
var dispensaryName: String?
var category: String?,
var brand: String?
var imageUrl: String?
init(id: String?,
name: String?,
dispensaryName: String?,
brand: String?,
category: String?,
imageUrl: String?) {
self.id = id
self.name = name
self.dispensaryName = dispensaryName
self.brand = brand
self.category = category,
self.imageUrl = imageUrl
}
}
import UIKit
class ProductListCell: UITableViewCell {
#IBOutlet weak var productImage: UIImageView!
#IBOutlet weak var dispensaryName: UILabel!
#IBOutlet weak var productName: UILabel!
#IBOutlet weak var categoryLabel: UILabel!
#IBOutlet weak var categoryStrain: UILabel!
}
import UIKit
import Firebase
import FirebaseFireStore
class ProductListController: UIViewController {
#IBOutlet weak var productListTableView: UITableView!
#IBOutlet weak var menuButton: UIBarButtonItem!
var dbRef: DatabaseReference!
var productSetup: [ProductList] = []
override func viewDidLoad() {
super.viewDidLoad()
productListTableView.dataSource = self
productListTableView.delegate = self
self.productListTableView.rowHeight = UITableView.automaticDimension
self.productListTableView.estimatedRowHeight = 363
menuButton.target = self.revealViewController()
menuButton.action = #selector(SWRevealViewController.revealToggle(_:))
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
dbRef = Database.database().reference().child("products");
//observing the data changes
dbRef.observe(DataEventType.value, with: { (snapshot) in
//if the reference have some values
if snapshot.childrenCount > 0 {
//clearing the list
self.productSetup.removeAll()
//iterating through all the values
for producting in snapshot.children.allObjects as! [DataSnapshot] {
//getting values
let productObject = producting.value as? [String: AnyObject]
let id = productObject?["id"]
let name = productObject?["name"]
let dispensaryName = productObject?["dispensaryName"]
let category = productObject?["category"]
let strain = productObject?["strain"]
let imageUrl = productObject?["imageUrl"]
//creating artist object with model and fetched values
let massProducts = ProductList(id: id as! String?,
name: name as! String?,
dispensaryName: dispensaryName as! String?,
category: category as! String?,
strain: strain as! String?,
imageUrl: imageUrl as! String?)
//appending it to list
self.productSetup.append(massProducts)
}
//reloading the tableview
print(self.productSetup)
self.productListTableView.reloadData()
}
})
}
}
extension ProductListController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return productSetup.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ProductListCell") as!
ProductListCell
let production: ProductList
production = productSetup[indexPath.row]
cell.productName.text = "\(String(describing: production.brand)): \(String(describing: production.name))"
cell.dispensaryName.text = production.dispensaryName
cell.categoryLabel.text = production.category
cell.productImage.text = production.imageUrl
return cell
}
}
I have reformatted the code quickly to make it easier to understand but it could be one of many things;
Check user authenticated with firebase on the device.
Ensure you have setup security settings correctly to allow reads in firebase.
Reformatted Code
ProductListController.swift
import Firebase
class ProductListController: UIViewController {
#IBOutlet weak var productListTableView: UITableView!
#IBOutlet weak var menuButton: UIBarButtonItem!
var productSetup = [ProductList]()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
productListTableView.dataSource = self
productListTableView.delegate = self
productListTableView.rowHeight = UITableView.automaticDimension
productListTableView.estimatedRowHeight = 363
menuButton.target = self.revealViewController()
menuButton.action = #selector(SWRevealViewController.revealToggle(_:))
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
fetchProducts { (products) in
self.productSetup = products
self.productListTableView.reloadData()
}
}
func fetchProducts(_ completion: #escaping ([ProductList]) -> Void) {
let ref = Firestore.firestore().collection("products")
ref.addSnapshotListener { (snapshot, error) in
guard error == nil, let snapshot = snapshot, !snapshot.isEmpty else {
return
}
completion(snapshot.documents.compactMap( {ProductList(dictionary: $0.data())} ))
}
}
}
extension ProductListController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return productSetup.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ProductListCell") as?
ProductListCell else { return UITableViewCell() }
cell.configure(withProduct: productSetup[indexPath.row])
return cell
}
}
ProductListCell.swift
import Firebase
class ProductListCell: UITableViewCell {
#IBOutlet weak var productImage: UIImageView!
#IBOutlet weak var dispensaryName: UILabel!
#IBOutlet weak var productName: UILabel!
#IBOutlet weak var categoryLabel: UILabel!
#IBOutlet weak var categoryStrain: UILabel!
func configure(withProduct product: ProductList) {
productName.text = "\(String(describing: product.brand)): \(String(describing: product.name))"
dispensaryName.text = product.dispensaryName
categoryLabel.text = product.category
fetchImage(withURL: product.imageUrl ) { (image) in
productImage.image = image
}
}
func fetchImage(withURL url: String, _ completion: #escaping (UIImage) -> Void) {
let ref = Storage.storage().reference(forURL: url)
ref.getData(maxSize: 1 * 1024 * 1024) { (data, error) in
guard error == nil, let imageData = data, let image = UIImage(data: imageData) else {
return
}
completion(image)
}
}
}
ProductList.swift
class ProductList {
var id: String
var name: String
var dispensaryName: String
var category: String
var brand: String
var imageUrl: String
init(id: String, name: String, dispensaryName: String, brand: String, category: String, imageUrl: String) {
self.id = id
self.name = name
self.dispensaryName = dispensaryName
self.brand = brand
self.category = category
self.imageUrl = imageUrl
}
convenience init(dictionary: [String : Any]) {
let id = dictionary["id"] as? String ?? ""
let name = dictionary["name"] as? String ?? ""
let dispensaryName = dictionary["dispensaryName"] as? String ?? ""
let brand = dictionary["brand"] as? String ?? ""
let category = dictionary["category"] as? String ?? ""
let imageUrl = dictionary["imageUrl"] as? String ?? ""
self.init(id: id, name: name, dispensaryName: dispensaryName, brand: brand, category: category, imageUrl: imageUrl)
}
}
I Hope you found this helpful.
I am trying to display users for a messaging app in a TableView however when I scroll it updates the cells and therefore pulls the data again thus lagging. I am using a prototype cell in a storyboard.
import UIKit
import FirebaseAuth
import FirebaseDatabase
class TableViewCell: UITableViewCell {
#IBOutlet weak var profilePicture: UIImageView!
#IBOutlet weak var statusImage: UIImageView!
#IBOutlet weak var nameLabel: UILabel!
override func prepareForReuse() -> Void {
profilePicture.image = nil
statusImage.backgroundColor = UIColor.systemGreen
nameLabel.text = nil
}
}
class MainController: UITableViewController {
#IBOutlet weak var tableViews: UITableView!
var users = [String]()
var decryptedUsers = [String]()
#IBAction func signOut(_ sender: Any) {
do {
let userid = Auth.auth().currentUser?.uid
let dbReference = Database.database().reference().child("Users").child(userid!).child("status")
dbReference.observeSingleEvent(of: .value, andPreviousSiblingKeyWith: { snapshot,error in
if (snapshot.value as? String == "online") {
dbReference.setValue("offline")
}
})
try Auth.auth().signOut()
self.performSegue(withIdentifier: "backToSignIn", sender: nil)
} catch {
let alert = UIAlertController(title: "Error", message: "There was an error signing out. Please ensure that you are connected to the internet.", preferredStyle: UIAlertController.Style.alert)
let okButton = UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil)
alert.addAction(okButton)
self.present(alert, animated: true, completion: nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
setOnline()
fetchUsers()
}
static var userslist = [String]()
func fetchUsers() {
let userid = (Auth.auth().currentUser?.uid)!
let dbRef = Database.database().reference().child("Chatlist").child(userid)
dbRef.observe(.value) { (snapshot, error) in
self.users.removeAll()
for yeet in snapshot.children {
let yeetsnap = yeet as! DataSnapshot
self.users.append(yeetsnap.childSnapshot(forPath: "id").value as! String)
print(self.users)
self.tableViews.reloadData()
}
}
}
func setOnline() {
let userid = Auth.auth().currentUser?.uid
let dbReference = Database.database().reference().child("Users").child(userid!).child("status")
dbReference.observeSingleEvent(of: .value, andPreviousSiblingKeyWith: { snapshot,error in
if (snapshot.value as? String == "offline") {
dbReference.setValue("online")
}
})
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: TableViewCell?
cell = nil
if (cell == nil) {
cell = (tableView.dequeueReusableCell(withIdentifier: "CellProto") as? TableViewCell)!
DispatchQueue.main.async {
cell!.profilePicture.layer.masksToBounds = true
cell!.profilePicture.layer.cornerRadius = (cell!.profilePicture.bounds.width) / 2
cell!.statusImage.layer.masksToBounds = true
cell!.statusImage.layer.cornerRadius = (cell!.statusImage.bounds.width) / 2
let dbRef = Database.database().reference().child("Users").child(self.users[indexPath.row])
dbRef.observeSingleEvent(of: .value) { (snapshot) in
cell!.nameLabel?.text = snapshot.childSnapshot(forPath: "username").value as? String
let urlstring = snapshot.childSnapshot(forPath: "imageURL").value as? String
if (urlstring != "default") {
let url = URL(string: urlstring!)
let data = try? Data(contentsOf: url!)
cell!.profilePicture.image = UIImage(data: data!)
}
let statusstring = snapshot.childSnapshot(forPath: "status").value as? String
if (statusstring == "online") {
cell!.statusImage.backgroundColor = UIColor.systemGreen
} else if (statusstring == "offline") {
cell!.statusImage.backgroundColor = UIColor.systemGray
} else if (statusstring == "appearoffline") {
cell!.statusImage.backgroundColor = UIColor.systemGray
} else if (statusstring == "dnd") {
cell!.statusImage.backgroundColor = UIColor.systemRed
}
}
}
}
print("test")
return cell!
}
}
Do not load the data every time within the cellForRow. This reloads the data every time you scroll, causing the table to lag.
A better approach would be to load the data once by putting the database retrieving code in here:
func viewWillAppear(_ animated: Bool){
super.viewWillAppear(animated)
// retrieve the data here by calling a separate function
}
Whatever you do, it is not a good coding approach to load data within the cellForRow method.
You can also retrieve the data by calling the function whenever the user swipes down from the top of the table to reload the table.
One more option would be to only load a number of rows at once (10 max for example), then when the user scroll to the bottom of the 10 rows and keeps scrolling it will then retrieve the next 10 datapoints for the next 10 rows in the table.
Having a problem retrieving data from Firebase in the same order that it was entered. I have tried this a number of ways using different variations of .valueAdded & .value to get this back in the same order but having no luck. Perhaps the way in which I am modelling this data is incorrect? Any help with this would be great. Thanks.
This is my Firebase Structure :
This is my data model:
struct RentalObjects {
var title = [String]()
var rentalType = [String]()
var dateAval = [String]()
var location = [String]()
var price = [String]()
var bond = [String]()
var pets = [String]()
var descripton = [String]()
}
This is my table view VC :
import UIKit
import FirebaseDatabase
class RentalTableViewVC: UIViewController, UITableViewDataSource, UITableViewDelegate{
#IBOutlet weak var rentalImage: UIImageView!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var rentalTitle: UILabel!
#IBOutlet weak var rentalPrice: UILabel!
var rentalsObject = RentalObjects()
var databaseRef:DatabaseReference?
var handle: DatabaseHandle?
var arrayOfTitles = [String?]()
var arrayOfBond = [String?]()
var arrayOfDateAval = [String?]()
var arrayOfDes = [String?]()
var arrayOfLocation = [String?]()
var arrayOfPets = [String?]()
var arrayOfPrice = [String?]()
var arrayOfRentalType = [String?]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rentalsObject.title.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cell")
cell.textLabel?.text = ("Title: \(rentalsObject.title[indexPath.row]), DateAval: \(rentalsObject.dateAval[indexPath.row])")
return cell
}
#IBAction func backPressed(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.dataSource = self
databaseRef = Database.database().reference().child("Rentals")
databaseRef?.observe(.childAdded, with: { (snapshot) in
if let dictonary = snapshot.value as? [String: AnyObject] {
print(snapshot)
switch snapshot.key {
case "bond" :
_ = dictonary.map{self.rentalsObject.bond.append(($0.value as? String)!)}
// print(self.arrayOfBond)
case "dateAval" :
_ = dictonary.map{self.rentalsObject.dateAval.append(($0.value as? String)!)}
case "description" :
_ = dictonary.map{self.rentalsObject.descripton.append(($0.value as? String)!)}
case "location" :
_ = dictonary.map{self.rentalsObject.location.append(($0.value as? String)!)}
case "pets" :
_ = dictonary.map{self.rentalsObject.pets.append(($0.value as? String)!)}
case "price" :
_ = dictonary.map{self.rentalsObject.price.append(($0.value as? String)!)}
case "rentalType" :
_ = dictonary.map{self.rentalsObject.rentalType.append(($0.value as? String)!)}
case "title" :
_ = dictonary.map{self.rentalsObject.title.append(($0.value as? String)!)}
print(self.rentalsObject.title)
// _ = dictonary.map{self.arrayOfTitles.append($0.value as? String)}
// print(self.arrayOfTitles)
default:
break
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
}
}
This is an example of the output from my tableView. I am trying to get this out in the same order it came in, in this example, 1,2,3,4,5. When I add in data from the other variables to this output they all become mixed up for some reason.
This is the code where the data is sent to firebase:
class AvertisingVC: UIViewController {
#IBOutlet weak var titleField: UITextField!
#IBOutlet weak var rentalTypeField: UITextField!
#IBOutlet weak var dateField: UITextField!
#IBOutlet weak var locationField: UITextField!
#IBOutlet weak var priceField: UITextField!
#IBOutlet weak var bondField: UITextField!
#IBOutlet weak var petsAllowedField: UITextField!
#IBOutlet weak var detailedDescription: UITextField!
var databaseRef:DatabaseReference? //reference to firebase dba
override func viewDidLoad() {
super.viewDidLoad()
databaseRef = Database.database().reference().child("Rentals") //can add .child(string:root) to add root dir to dba
}
#IBAction func backBtnPressed(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
#IBAction func submitForm(_ sender: Any) { // Send data to firebase on submit
if titleField.text != nil {
databaseRef?.child("title").childByAutoId().setValue(titleField.text)
titleField.text = ""
}
if rentalTypeField.text != nil {
databaseRef?.child("rentalType").childByAutoId().setValue(rentalTypeField.text)
rentalTypeField.text = ""
}
if dateField.text != nil {
databaseRef?.child("dateAval").childByAutoId().setValue(dateField.text)
dateField.text = ""
}
if locationField.text != nil {
databaseRef?.child("location").childByAutoId().setValue(locationField.text)
locationField.text = ""
}
if priceField.text != nil {
databaseRef?.child("price").childByAutoId().setValue(priceField.text)
priceField.text = ""
}
if bondField.text != nil {
databaseRef?.child("bond").childByAutoId().setValue(bondField.text)
bondField.text = ""
}
if petsAllowedField.text != nil {
databaseRef?.child("pets").childByAutoId().setValue(petsAllowedField.text)
petsAllowedField.text = ""
}
if detailedDescription.text != nil {
databaseRef?.child("description").childByAutoId().setValue(detailedDescription.text)
detailedDescription.text = ""
}
let alertController = UIAlertController(title: "Success!", message: "You have successfully listed a rental", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "Close Alert", style: .default, handler: nil)
alertController.addAction(defaultAction)
present(alertController, animated: true, completion: nil)
}
}
When data is interpreted from network data, swift doesn't guarantee the order of the interpreted values if they're ordered in an array or dictionary. You'd have to order the values yourself, or update a cache device-side, and sort that cache each time it is updated.
There's many ways to this simply, or intricately for various use-cases. But looking at your's, i'd add a sort on wherever you're reading the tableView's datasource, before you call the delegate and datasource methods. Taking your example, if you sorted by date:
override func viewDidLoad() {
super.viewDidLoad()
databaseRef = Database.database().reference().child("Rentals")
databaseRef?.observe(.childAdded, with: { (snapshot) in
if let dictonary = snapshot.value as? [String: AnyObject] {
print(snapshot)
switch snapshot.key {
case "bond" :
_ = dictonary.map{self.rentalsObject.bond.append(($0.value as? String)!)}
// print(self.arrayOfBond)
case "dateAval" :
_ = dictonary.map{self.rentalsObject.dateAval.append(($0.value as? String)!)}
case "description" :
_ = dictonary.map{self.rentalsObject.descripton.append(($0.value as? String)!)}
case "location" :
_ = dictonary.map{self.rentalsObject.location.append(($0.value as? String)!)}
case "pets" :
_ = dictonary.map{self.rentalsObject.pets.append(($0.value as? String)!)}
case "price" :
_ = dictonary.map{self.rentalsObject.price.append(($0.value as? String)!)}
case "rentalType" :
_ = dictonary.map{self.rentalsObject.rentalType.append(($0.value as? String)!)}
case "title" :
_ = dictonary.map{self.rentalsObject.title.append(($0.value as? String)!)}
print(self.rentalsObject.title)
// _ = dictonary.map{self.arrayOfTitles.append($0.value as? String)}
// print(self.arrayOfTitles)
default:
break
}
}
whateverCollectionIsYourInCacheDataSource = whateverCollectionIsYourInCacheDataSource.sorted(by: { ($0, $1) -> Bool in
return $0 < $1 //If you want to sort them by their date value. Make sure the dates are properly interpreted from whatever format they're saved in remotely into the Date type.
})
DispatchQueue.main.async {
//No need to call .reloadData and these methods in viewDidLoad, unless you REALLY have to (ie: your data is modified on purpose after being loaded initially). Calling these methods loads up the data anyways, so just call these whenever your data is downloaded, converted and sorted properly.
tableView.dataSource = self
tableView.dataSource = self
}
})
}
Hope it helps you on the right path. Look up network-based tableview / collectionview tutorials, they can be very helpful.
extension of Firebase Database Structure
After uploaded the image, name and phone to the Firebase Database and Firebase Storage.
I am having trouble with retrieving the image from the Firebase to the Table View.
This is the Table View Class :
#IBOutlet var tableViewHongKong: UITableView!
var restaurantList = [RestaurantModel]()
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference().child("restaurants")
ref?.observe(FIRDataEventType.value, with: {(snapshot) in
if snapshot.childrenCount>0
{
self.restaurantList.removeAll()
for restaurants in snapshot.children.allObjects as![FIRDataSnapshot]
{
let restaurantObject = restaurants.value as? [String: AnyObject]
let restaurantName = restaurantObject?["name"]
let restaurantPhone = restaurantObject?["phone"]
let restaurant = RestaurantModel(name: name as! String?, phone: phone as! String?)
self.restaurantList.append(restaurant)
}
}
self.tableViewHongKong.reloadData()
})
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return restaurantList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewControllerTableViewCell
let restaurant: RestaurantModel
restaurant = restaurantList[indexPath.row]
cell.nameLabel.text = restaurant.name
return cell
}
}
This is the Table View Cell Class :
#IBOutlet var myImage: UIImageView!
#IBOutlet var nameLabel: UILabel!
This is the Restaurant Model Class:
var name: String?
var phone: String?
init(name:String?, phone:String?) {
self.name = name;
self.phone = phone
}
As the image uploaded in another class, Restaurant.
var imageURL = ""
func addRestaurant()
{
ref = FIRDatabase.database().reference().child("restaurants")
let key = ref?.childByAutoId().key
let name = addName.text
let phone = addPhone.text
ref?.child(key!).setValue(["name": name, "phone": phone, "image": imageURL])
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
let image = info[UIImagePickerControllerOriginalImage] as? UIImage
addImage.image = image
var data = Data()
data = UIImagePNGRepresentation(image!)!
let uniqueName = NSUUID().uuidString
let imageRef = FIRStorage.storage().reference().child("restaurantImage").child("\(uniqueName)")
imageRef.put(data, metadata: nil).observe(.success){(snapshot) in
self.imageURL = (snapshot.metadata?.downloadURL()?.absoluteString)!
}
self.dismiss(animated: true, completion: nil)
}
For more detail, may visit Firebase Database Structure
Thank you very much ! ^.^
I am creating an inventory app in order to keep track of items held in a laboratory. In the laboratory there are different stations which contain different items in them, which as you can see is structured properly in my Firebase database.
Firebase Database
Iphone Simulator
My problem comes when I try to delete a particular item out of the tableCell. I am able to remove it from the UI but in firebase the data still remains. I have done coutless reserch but am not able to find anything relating to this particular problem.
Data Services Class
let DB_BASE = FIRDatabase.database().reference().child("laboratory") //contains the root of our database
let STORAGE_BASE = FIRStorage.storage().reference()
class DataService {
static let ds = DataService()
//DB References
private var _REF_BASE = DB_BASE
private var _REF_STATION = DB_BASE.child("stations")
private var _REF_USERS = DB_BASE.child("users")
//Storage Reference
private var _REF_ITEM_IMAGE = STORAGE_BASE.child("item-pics")
var REF_BASE: FIRDatabaseReference {
return _REF_BASE
}
var REF_STATION: FIRDatabaseReference {
return _REF_STATION
}
var REF_USERS: FIRDatabaseReference {
return _REF_USERS
}
var REF_ITEM_IMAGES: FIRStorageReference {
return _REF_ITEM_IMAGE
}
//creating a new user into the firebase database
func createFirebaseDBUser(_ uid: String, userData: Dictionary<String, String>) {
REF_USERS.child(uid).updateChildValues(userData)
}
}
Inventory View Controller
import UIKit
import Firebase
class InventoryViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var items = [Item]()
private var _station: Station!
private var _item: Item!
var sortIndex = 3
var imagePicker: UIImagePickerController!
static var imageCache: NSCache<NSString, UIImage> = NSCache()
var imageSelected = false
#IBOutlet weak var itemImageToAdd: UIImageView!
#IBOutlet weak var objectTextInput: UITextField!
#IBOutlet weak var brandTextInput: UITextField!
#IBOutlet weak var unitTextInput: UITextField!
#IBOutlet weak var amountTextInput: UITextField!
#IBOutlet weak var tableView: UITableView!
#IBOutlet var addItemView: UIView!
#IBOutlet weak var currentStationLabel: UILabel!
var station: Station {
get {
return _station
} set {
_station = newValue
}
}
override func viewDidLoad() {
super.viewDidLoad()
var currentStationName = station.title
currentStationLabel.text = currentStationName
self.items = []
let currentStation = station.title
let stationRef = DataService.ds.REF_STATION.child(currentStation!)
let inventoryRef = stationRef.child("inventory")
tableView.delegate = self
tableView.dataSource = self
imagePicker = UIImagePickerController()
imagePicker.allowsEditing = true
imagePicker.delegate = self
inventoryRef.observe(.value, with: { (snapshot) in
print(snapshot.value!)
self.items = []
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshot {
print("SNAP: \(snap)")
if let itemDict = snap.value as? Dictionary<String, AnyObject> {
let key = snap.key
let item = Item(itemKey: key,
itemData: itemDict)
self.items.append(item)
}
}
}
self.tableView.reloadData()
})
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = items[indexPath.row]
if let cell = tableView.dequeueReusableCell(withIdentifier: "inventoryTableCell", for: indexPath) as? ItemCell {
if let img = InventoryViewController.imageCache.object(forKey: NSString(string: item.imageURL!)) {
cell.updateItemUI(item: item, img: img)
} else {
cell.updateItemUI(item: item)
}
return cell
} else {
return ItemCell()
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func postToFirebase(itemImageURL: String) {
let post: Dictionary<String, AnyObject> = [
"objectLabel": objectTextInput.text! as AnyObject,
"brandLabel": brandTextInput.text! as AnyObject,
"unitLabel": unitTextInput.text! as AnyObject,
"amountLabel": amountTextInput.text! as AnyObject,
//post elsewhere as an image for future reference
"itemImageURL": itemImageURL as AnyObject,
]
let stationText = _station.title
let stationRef = DataService.ds.REF_STATION.child(stationText!)
let inventoryRef = stationRef.child("inventory")
let firebasePost = inventoryRef.childByAutoId()
firebasePost.setValue(post)
objectTextInput.text = ""
brandTextInput.text = ""
unitTextInput.text = ""
amountTextInput.text = ""
imageSelected = false
tableView.reloadData()
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerEditedImage] as? UIImage {
itemImageToAdd.image = image
imageSelected = true
} else {
print("Please select a valid image")
}
imagePicker.dismiss(animated: true, completion: nil)
}
#IBAction func backToStations(_ sender: Any) {
performSegue(withIdentifier: "backToStations", sender: nil)
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(tableView: (UITableView!), commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: (NSIndexPath!)) {
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let currentStation = station.title
let stationRef = DataService.ds.REF_STATION.child(currentStation!)
let inventoryRef = stationRef.child("inventory")
var deleteAction = UITableViewRowAction(style: .default, title: "Delete") {action in
//Insert code to delete values from Firebase
self.items.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath as IndexPath], with: .fade)
}
var editAction = UITableViewRowAction(style: .normal, title: "Edit") { action in
}
return [deleteAction, editAction]
}
}
My thought process is upon delete to call self_items.key reffering to the current key of the particular tableCell row. From there I would use the current key whick would be the autoID and remove the value that way. Unfortunatly though that crashes the program with a fatal nil error.
The best way I've found to solve this problem is in your delete action, delete the object from Firebase. Do not alter the tableview from here.
Then in your data listener, check when the data comes back as deleted(or NULL), then remove it from the tableview datasource array and update the tableview.