Search code examples
iosswiftuitableviewswift3http-live-streaming

Getting a UITableView to refresh users after one begins a live stream


The landing page for an app I am working on has a place holder avatar named after the application that is present when no user is actively live streaming (using the Red5 Pro streaming framework for this).

However when someone does begin to stream, I want it to automatically refresh the tableview and display the new user's avatar. What I've written so far kind of works, but not entirely. When someone begins livestreaming the placeholder avatar does disappear, but the user that's streaming's avatar doesn't appear.

If I close the app and reopen it, then it is displayed correctly. Here's my code in Swift 3, what am I doing wrong? Should the call to refresh be moved out of ViewDidLoad? Am I using Dispatch Queue incorrectly? Thanks

import UIKit
import Firebase

class HomeController: UIViewController, UITableViewDataSource,     UITableViewDelegate, cellDelegate {

@IBOutlet var tableView: UITableView!
var stream: String!
var top: [SSStream] = []
var recent: [SSStream] = [SSStream()]
var trending: [SSStream] = [SSStream()]
var ref: FIRDatabaseReference!

override func viewDidLoad() {
    navigationItem.title = "Swiffshot"
    navigationController?.navigationBar.isTranslucent = false
    let settings = UIButton(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
    settings.setImage(#imageLiteral(resourceName: "Settings"), for: .normal)
    settings.addTarget(self, action: #selector(settingsPressed), for: .touchUpInside)
    navigationItem.leftBarButtonItem = UIBarButtonItem(customView: settings)
    let friends = UIButton(frame: CGRect(x: 0, y: 0, width: 23, height: 20))
    friends.setImage(#imageLiteral(resourceName: "AllFriends"), for: .normal)
    friends.addTarget(self, action: #selector(friendsPressed), for: .touchUpInside)
    navigationItem.rightBarButtonItem = UIBarButtonItem(customView: friends)

    let nib = UINib(nibName: "MainHeader", bundle: Bundle.main)
    tableView.register(nib, forHeaderFooterViewReuseIdentifier: "MainHeader")

    ref = FIRDatabase.database().reference()


    if !SSContact.shared.active {
        performSegue(withIdentifier: "fromMainToAuth", sender: self)
    }

    SSContact.shared.load() { SSContact.shared.propertyCheck(self) { } }

//        SSContact.shared.subscribeToTop(pulse: { (streams) in
//            self.top.removeAll()
//            self.top.append(contentsOf: streams)
//            self.tableView.reloadSections(IndexSet(integer: 0), with:     .automatic)
//        })

    ref.child("streams").observe(.value, with: { (snapshot) in

        print("I ran")

        self.top.removeAll()
        if let userData = snapshot.value as? NSDictionary {
            for stream in userData {
                let newStream = SSStream()
                newStream.username = stream.key as! String
                print("Found stream \(stream.key as! String)")
                newStream.isPrivate = !((stream.value as! NSDictionary)["public"] as! Bool)
                newStream.views = (stream.value as! NSDictionary)["views"] as! Int
                newStream.isEnabled = true
                self.top.append(newStream)
            }
        }
        if self.top.isEmpty {
            print("No Streams Found")
            self.top.append(SSStream())
        }
        DispatchQueue.main.async {
            self.tableView.reloadSections(IndexSet(integer: 0), with: .automatic)
            self.tableView.reloadData()
        }
    })
}

func cellGotPressed(_ stream: String) {
    self.stream = stream
    performSegue(withIdentifier: "toPlayer", sender: self)
}

func settingsPressed() {
    performSegue(withIdentifier: "toSettings", sender: self)
}

func friendsPressed() {
    performSegue(withIdentifier: "fromMainToExpandable", sender: self)
}

func cameraTapped() {
    performSegue(withIdentifier: "toRed", sender: self)
}

func cellTapped() {
    print("Cell Tapped")
}

// MARK: Segue

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "toPlayer" {
        let player = segue.destination as! VideoPlayerViewController
        player.isSubscribing = true
        player.stream = stream
    }
}

// MARK: Table View Functions

func numberOfSections(in tableView: UITableView) -> Int {
    return 3
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 1
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    if indexPath.section == 0 {
        let cell = tableView.dequeueReusableCell(withIdentifier: "big") as! CategoryRow
        cell.section = indexPath.section
        cell.top = top
        cell.delegate = self
        cell.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(cameraTapped)))
        return cell
    } else {
        let cell = tableView.dequeueReusableCell(withIdentifier: "small") as! CategoryRow
        cell.section = indexPath.section
        cell.recent = recent
        cell.trending = trending
        cell.delegate = self
        return cell
    }
}

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    if section == 0 {
        return nil
    } else {
        let cell = self.tableView.dequeueReusableHeaderFooterView(withIdentifier: "MainHeader")
        let header = cell as! MainHeader
        if section == 1 {
            header.fillHeader("RECENT")
        } else if section == 2 {
            header.fillHeader("Trending + Now")
        } else {
            print("Unknown Section")
        }
        return header
    }
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    if indexPath.section == 0 {
        return 300
    } else {
        return 100
    }
}

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    if section == 0 {
        return 0
    } else {
        return 50
    }
}

func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat {
    return 50
}
}

Solution

  • I realized what I needed to do. I needed to set the

    ref.child("streams").observe
    

    in the call to Dispatch.Queue. By setting the reference before dispatch was called, the program wasn't syncing properly. It should be like this:

    DispatchQueue.main.async {
    ref.child("streams").observe(.value, with: { (snapshot) in
            self.tableView.reloadSections(IndexSet(integer: 0), with: .automatic)
            self.tableView.reloadData()
        }
    })