I want to change my model object datasource to firebase. I have a file that serves as the datasource for the UICollection view, homeViewController.swift . homeViewControlle
I hope this will get you started. It would be better to have the whole schema of the database, but I have made this based on what I could see from your screenshot. It also seems that having a separate BusinessCategory tree is not needed since you have the category type for each business in the Business tree, although that is completely up to you.
If you want to provide a more complete screenshot of your database (just something that shows the keys and data types), I would be happy to modify this code.
Since I don't know how you update your collection view, I have made it so it returns a Dictionary where the key is the category and the value is an array of bussinesses of that category. This should be an easy format if you are using sections in your collection view.
With regards to the typealias FirebaseRootDictionary, it might need to be modified as I was guessing on what your database schema was.
If you have any questions or problems with this code just put a comment beneath, and I'll try to fix it.
So to get your data:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Business.getBusinesses { (businesses) in
print(businesses)
}
}
Then inside that closure have it update the collection view.
import Foundation
import Firebase
final class Business : NSObject {
typealias FirebaseRootDictionary = Dictionary<String,Dictionary<String,Dictionary<String,String>>>
var name: String
var category: String
var email: String
var imageUrl: String
override var description: String {
return "Business(name: \"\(name)\", category: \"\(category)\", email: \"\(email)\", imageUrl: \"\(imageUrl)\")"
}
init(name:String, category:String, email:String, imageUrl:String) {
self.name = name
self.category = category
self.email = email
self.imageUrl = imageUrl
}
class func getBusinesses(completionHandler:@escaping (_ businesses: BusinessesDictionary)->()) { // -> [Business]
let ref = FIRDatabase.database().reference().child("BusinessCategories")
var businesses = BusinessesDictionary()
ref.observeSingleEvent(of: .value, with: { (snapshot) in
guard let value = snapshot.value as? FirebaseRootDictionary else { return }
let categories = value.keys.sorted()
var arr = [Business]() // Array of businesses for category
for cat in categories {
guard let data = value[cat] else { continue }
let businessKeys = data.keys.sorted()
for key in businessKeys {
guard let businessData = data[key] else { continue }
guard let name = businessData["BusinessName"], let category = businessData["Category"], let email = businessData["email"], let imageUrl = businessData["imageUrl"] else { continue }
let business = Business(name: name, category: category, email: email, imageUrl: imageUrl)
arr.append(business)
}
businesses[cat] = arr
arr.removeAll()
}
completionHandler(businesses)
})
}
}
Edit:
So for the view, you have a tableview with one cell per section/category. The cell has a collection view, which has a collection view cell with a image view and label. So here I have a table view controller that will handle all that.
import UIKit
typealias BusinessesDictionary = Dictionary<String,[Business]> // I have moved this typealias to here instead of inside the Business Model.
class TableViewController: UITableViewController {
var tableData = BusinessesDictionary()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(CategoryCell.self, forCellReuseIdentifier: "cell")
self.tableView.allowsSelection = false
Business.get { (businesses) in
self.tableData = businesses
self.tableView.reloadData()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return self.tableData.keys.count
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let category = self.tableData.keys.sorted()[section]
return category
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? CategoryCell else { return UITableViewCell() }
// Configure the cell...
let category = self.tableData.keys.sorted()[indexPath.section]
guard let businesses = self.tableData[category] else { return UITableViewCell() }
cell.businesses = businesses
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 120
}
}
The table view cell file.
class CategoryCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
var collectionView: UICollectionView!
var businesses = [Business]()
override func layoutSubviews() {
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) // You may wan to change this as this is the spacing between cells
layout.itemSize = CGSize(width: 100, height: 120) // You may wan to change this as this is the cell size
layout.scrollDirection = .horizontal
collectionView = UICollectionView(frame: self.bounds, collectionViewLayout: layout)
collectionView.topAnchor.constraint(equalTo: self.topAnchor)
collectionView.leftAnchor.constraint(equalTo: self.leftAnchor)
collectionView.rightAnchor.constraint(equalTo: self.rightAnchor)
collectionView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(BusinessCell.self, forCellWithReuseIdentifier: "businessCell")
collectionView.backgroundColor = .white
self.addSubview(collectionView)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return businesses.count
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "businessCell", for: indexPath) as? BusinessCell else { return UICollectionViewCell() }
// Configure the cell
let business = self.businesses[indexPath.row]
cell.nameLabel.text = business.name
cell.imageView.image = UIImage(named: business.imageUrl)
return cell
}
}
This is the collection view cell.
class BusinessCell: UICollectionViewCell {
var imageView: UIImageView!
var nameLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
imageView = UIImageView(frame: CGRect(x: 20, y: 20, width: 60, height: 60))
imageView.contentMode = .scaleAspectFit
nameLabel = UILabel(frame: CGRect(x: 0, y: 90, width: 100, height: 30))
nameLabel.font = UIFont.systemFont(ofSize: 11)
nameLabel.textAlignment = .center
self.addSubview(imageView)
self.addSubview(nameLabel)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
Here is a screenshot of the test database I made.