Search code examples
swiftfirebasefirebase-realtime-databaseuicollectionviewcell

How should I retrieve data to the UICollectionViewCell from Firebase?


I'm a new in Swift and Firebase, so I have a question. I have a model with data. But i need to retrieve data from firebase and fill UICollectionViewCells with this data.

This is a model of data:

 // MARK: - Public API
var title = ""
var description = ""
var ref = Database.database().reference()

init(withSnapshot: DataSnapshot) {
    self.description = withSnapshot.childSnapshot(forPath: "description").value as? String ?? "No Description"
   // self.featuredImage = withSnapshot.childSnapshot(forPath: "image").value as? String ?? "No Image"
    self.title = withSnapshot.childSnapshot(forPath: "title").value as? String ?? "No Title"
}

var programsArrayDataSource = [TrainingProgram]()

func fetchPrograms() {
    let programsRef = self.ref.child("programs")
    programsRef.observeSingleEvent(of: .value, with: { snapshot in
        let allPrograms = snapshot.children.allObjects as! [DataSnapshot]
        for programSnap in allPrograms {
            let aProgram = TrainingProgram(withSnapshot: programSnap)
            self.programsArrayDataSource.append(aProgram)
        }

       // self.programTableView.reloadData()
    })
}

UICollectionViewCell

import UIKit

class TrainingProgramCollectionViewCell: UICollectionViewCell {



    @IBOutlet weak var roundedView: UIView!
    @IBOutlet weak var bgView: UIView!
    @IBOutlet weak var viewForImage: UIView!

    @IBOutlet weak var buttonOutlet: UIButton!


    @IBOutlet weak var featuredImageView: UIImageView!

    @IBOutlet weak var descriptionLabel: UILabel!
    @IBOutlet weak var titleLabel: UILabel!

    var trainingPrograms: TrainingProgram? {
        didSet {
            self.updateUI()
        }
    }



    private func updateUI()
    {
        if let trainingProgram = trainingPrograms {

            featuredImageView.image = trainingPrograms!.featuredImage
            titleLabel.text = trainingPrograms!.title
            descriptionLabel.text = trainingPrograms!.description

            buttonOutlet.backgroundColor = UIColor(red: 0, green: 122/255, blue: 255/255, alpha: 1)
            buttonOutlet.layer.cornerRadius = 25
            buttonOutlet.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4).cgColor
            buttonOutlet.layer.shadowOffset = CGSize(width: 0, height: 5)
            buttonOutlet.layer.shadowRadius = 15
            buttonOutlet.layer.shadowOpacity = 1
            bgView!.layer.cornerRadius = 20
            featuredImageView.roundCorners(corners: [.topLeft, .topRight], radius: 20.0)
            viewForImage.roundCorners(corners: [.topLeft, .topRight], radius: 20.0)
            roundedView.layer.cornerRadius = 20



            featuredImageView.contentMode = .scaleAspectFill

            let view = UIView(frame: featuredImageView.frame)
            view.clipsToBounds = true



            featuredImageView.clipsToBounds = true



        } else {
            featuredImageView.image = nil
            titleLabel.text = nil
            descriptionLabel.text = nil
        }


    }

    override func layoutSubviews() {
        super.layoutSubviews()

        self.layer.cornerRadius = 3.0
        layer.shadowRadius = 10
        layer.shadowOpacity = 0.2
        layer.shadowOffset = CGSize(width: 5, height: 10)

        self.clipsToBounds = false
    }

    override func prepareForReuse() {
        super.prepareForReuse()


    }

}

UIViewController

class TrainingProgramViewController: UIViewController {

@IBOutlet weak var collectionView: UICollectionView!


  //    var trainingPrograms = TrainingProgram.fetchTrainingProgram()
var trainingPrograms = [TrainingPrograms]()
    let cellScale: CGFloat = 0.7


override func viewDidLoad() {
    super.viewDidLoad()

    let screenSize = UIScreen.main.bounds.size

    collectionView.dataSource = self
    collectionView.delegate = self

    let cellWidth = floor(screenSize.width * cellScale)
    let cellHeight = floor(screenSize.height * cellScale)

    let insetX = (view.bounds.width - cellWidth) / 3.0
    let insetY = (view.bounds.height - cellHeight) / 2.0

    let layout = collectionView!.collectionViewLayout as! UICollectionViewFlowLayout
    layout.itemSize = CGSize(width: cellWidth, height: cellHeight)
    collectionView?.contentInset = UIEdgeInsets(top: insetY, left: insetX, bottom: insetY, right: insetX)

}

}

extension TrainingProgramViewController: UICollectionViewDataSource {

func numberOfSections(in collectionView: UICollectionView) -> Int {
    return 1
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return trainingPrograms.count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{

    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TrainingProgramCollectionViewCell", for: indexPath) as!  TrainingProgramCollectionViewCell
    let trainingProgram = trainingPrograms[indexPath.item]
    cell.trainingPrograms = trainingProgram

    return cell

}

}

extension TrainingProgramViewController : UIScrollViewDelegate, UICollectionViewDelegate
{
    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>)
    {
        let layout = self.collectionView?.collectionViewLayout as! UICollectionViewFlowLayout
        let cellWidthIncludingSpacing = layout.itemSize.width + layout.minimumLineSpacing

        var offset = targetContentOffset.pointee
        let index = (offset.x + scrollView.contentInset.left) / cellWidthIncludingSpacing
        let roundedIndex = round(index)

        offset = CGPoint(x: roundedIndex * cellWidthIncludingSpacing - scrollView.contentInset.left, y: -scrollView.contentInset.top)
        targetContentOffset.pointee = offset
    }
}

How to retrieve data from Firebase to this array ? I need to retrieve two labels and one image

Firebase structure. "link" - link from Firebase Storage. This is an example of a structure that will programs

program1
    description: 
      "Lorem"
    image: 
     "link"
    title: 
     "Workout1"
 program2
    description: 
     "Lorem"
    image: 
     "link"
    title: 
     "Workout2"

Solution

  • Reading through the comments it appears you are asking how to read data from Firebase. Since there's no Firebase code in the question, I'm having to guess what you ultimately want to accomplish so I would first direct you to the Firebase Guide Reading and Writing Data as that covers the topic. Then, see Working With Lists

    To provide a leg up, here's one possible solution.

    Since you have a tableView, that would usually be backed by a tableView datasource. So the concept is to read the data from Firebase and then populate that datasource, then refresh the tableView which will show the objects to the user in the UI.

    Let's start with a class to hold the data from Firebase Realtime Database

    class ProgramClass {
        var description = ""
        var image = ""
        var title = ""
    
        init(withSnapshot: DataSnapshot) {
            self.description = withSnapshot.childSnapshot(forPath: "description").value as? String ?? "No Description"
            self.image = withSnapshot.childSnapshot(forPath: "image").value as? String ?? "No Image"
            self.title = withSnapshot.childSnapshot(forPath: "title").value as? String ?? "No Title"
        }
    }
    

    and then a class var to hold those objects

    var programsArrayDataSource = [ProgramClass]()
    

    keep in mind that class var will act as the dataSource which backs your tableview. When a change occurs in firebase, the dataSource would be updated via Firebase observe (.childAdded, .childChanged or .childRemoved) and then the tableView reloaded.

    To actually read the Firebase structure presented in your question, use the following code. This will both read Firebase as well as populating the dataSource for your tableView.

    func fetchPrograms() {
        let programsRef = self.ref.child("programs")
        programsRef.observeSingleEvent(of: .value, with: { snapshot in
            let allPrograms = snapshot.children.allObjects as! [DataSnapshot]
            for programSnap in allPrograms {
                let aProgram = ProgramClass(withSnapshot: programSnap)
                self.programsArrayDataSource.append(aProgram)
            }
    
            self.programTableView.reloadData()
        })
    }
    

    From there you can handle the tableView code; when a row needs refreshing, get that object from the dataSource via the index (row), read the data from that object and populate the tableView cell with it in whatever format you need.