I'm creating a playlist app (Mock-Up Below) using Swift and Parse. Each user has the ability add a song (or multiple songs) to the TableView and "Like" or "Dislike" (toggle style) as many songs as they like, including their own. This functionality is very similar to the Instagram "Like" button behaviour.
I'm having a bit of trouble conceptualizing the data model. As shown below, I have a User table and a Playlist table (with all of the added songs):
User Table:
Playlist Table:
I'm having trouble with the next step, which is storing the "Like" data for each user and each song.
UPDATED: I've added a column in the 'PlaylistData' table in Parse called "userVotes". As you can see, I'm appending all of the data into an array called 'voters'. For some reason, I'm getting a fatal run time error - 'fatal error: unexpectedly found nil while unwrapping an Optional value'.
import UIKit
import Parse
class MusicPlaylistTableViewController: UITableViewController {
var usernames = [String]()
var songs = [String]()
var voters = [String]()
var numVotes = 0
override func viewDidLoad() {
super.viewDidLoad()
tableView.separatorColor = UIColor.grayColor()
let query = PFQuery(className:"PlaylistData")
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects! as? [PFObject] {
self.usernames.removeAll()
self.songs.removeAll()
self.voters.removeAll()
for object in objects {
let username = object["username"] as? String
self.usernames.append(username!)
let track = object["song"] as? String
self.songs.append(track!)
self.voters = object["userVotes"] as! [String]
print(self.voters)
}
self.tableView.reloadData()
}
} else {
print(error)
}
}
}
override func viewWillAppear(animated: Bool) {
}
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 usernames.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CellTrack", forIndexPath: indexPath) as! TrackTableViewCell
cell.username.text = usernames[indexPath.row]
cell.songTitle.text = songs[indexPath.row]
cell.votes.text = "\(numVotes)"
cell.selectionStyle = UITableViewCellSelectionStyle.None
return cell
}
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
}
}
I've also created a TableViewCell swift file for the prototype cell:
import UIKit
import Parse
class TrackTableViewCell: UITableViewCell {
var numVotes = 0
#IBOutlet weak var songTitle: UILabel!
#IBOutlet weak var username: UILabel!
#IBOutlet weak var votes: UILabel!
#IBAction func voteButton(sender: UIButton) {
self.numVotes = self.numVotes + 1
self.votes.text = "\(self.numVotes)"
}
}
At this point, all it does is when the heart is clicked, it increases the vote count by 1 on screen (the user can do this as many times as possible, no data is stored in the database). What I need help with is the best way to implement table(s) which allow me to keep track of which users have voted for each song (or not). I know I need a TRUE / FALSE column somewhere, but I can't seem to figure out how to model it. Any suggestions or links to SWIFT tutorials would be greatly appreciated. A simple explanation on what tables to create (or what columns to add to existing tables) would help the best. Thanks!
Related
I am very new to iOS development (with emphasis on very). I think I have grasped simple table views in Xcode without calling on a database, and I also think I understand the basics of how to call data from Firestore, but I cannot for the life of me figure out how to populate my TableView with data from Firestore.
The Firestore collection I want to populate with is called "articles", where each doc represents an article I want to display in a cell. Each doc has this structure of data:
imageURL: https://someurl.com
title: 5 places you don't want to miss
I have created a UITableView with a UITableViewCell inside it in Storyboard, where the TableViewCell's ContentView contains an ImageView for the 'imageURL' data in Firestore and a Label for the 'title' data in Firetore.
The UITableView in Storyboard is linked to ArtiklerTableViewController.swift.
Likewise is the UITableViewCell linked to ArtiklerCell.swift.
The two Swift files look like this now:
ArtiklerTableViewController.swift
class ArtiklerTableViewController: UITableViewController {
#IBOutlet var artiklerTableView: UITableView!
var artiklerArray: [String] = []
var documents: [DocumentSnapshot] = []
var db: Firestore!
override func viewDidLoad() {
super.viewDidLoad()
db = Firestore.firestore()
configureTableView()
loadData()
func configureTableView() {
tableView.delegate = self
tableView.dataSource = self
tableView.register(ArtiklerCell.self, forCellReuseIdentifier: "ArtiklerCell")
// remove separators for empty cells
tableView.tableFooterView = UIView()
// remove separators from cells
tableView.separatorStyle = .none
}
func loadData() {
db.collection("articles").getDocuments() { (QuerySnapshot, err) in
if let err = err {
print("Error getting documents : \(err)")
}
else {
for document in QuerySnapshot!.documents {
let documentID = document.documentID
let artiklerImageView = document.get("imageURL") as! URL
let artiklerTitleLabel = document.get("title") as! String
print(artiklerImageView, artiklerTitleLabel, documentID)
}
self.tableView.reloadData()
}
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("Tableview setup \(artiklerArray.count)")
return artiklerArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ArtiklerCell", for: indexPath) as! ArtiklerCell
let artikler = artiklerArray[indexPath.row]
print("Array is populated \(artiklerArray)")
return cell
}
}
ArtiklerCell.swift
import UIKit
import Firebase
class ArtiklerCell: UITableViewCell {
#IBOutlet weak var artiklerImageView: UIImageView!
#IBOutlet weak var artiklerTitleLabel: UILabel!
var db: Firestore!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
db = Firestore.firestore()
addSubview(artiklerImageView)
addSubview(artiklerTitleLabel)
configureImageView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configureImageView() {
artiklerImageView.layer.cornerRadius = 20
artiklerImageView.clipsToBounds = true
}
}
When I try to run the app, I get an error message from the ArtiklerTableViewController.swift regarding the line let artikler = artiklerArray[indexPath.row] in the cellForRowAt function, saying 'Initialization of immutable value 'artikler' was never used; consider replacing with assignment to '_' or removing it'.
I see that this error message makes sense, but I have absolutely no idea what I should do instead.
Pardon my extreme lack of knowledge! I have spent many days now trying to look for the answers I need online without finding a solution. I think I am too inexperienced to correctly search for and absorb the necessary knowledge for this problem.
Any answer will be immensely appreciated!
Thanks in advance from a desperate girl who doesn't want to give up on learning iOS dev as I go through building an app.
You already have the strings in an array and got the artikler corresponding to the row of the cell, now you just need to set the title and the image. Also, you need to append each element to the array before reloading.
func loadData() {
db.collection("articles").getDocuments() { (QuerySnapshot, err) in
if let err = err {
print("Error getting documents : \(err)")
}
else {
for document in QuerySnapshot!.documents {
let documentID = document.documentID
let artiklerImageView = document.get("imageURL") as! URL
let artiklerTitleLabel = document.get("title") as! String
self.artiklerArray.append(artiklerTitleLabel)
}
self.tableView.reloadData()
}
}
}
...
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ArtiklerCell", for: indexPath) as! ArtiklerCell
let artikler = artiklerArray[indexPath.row]
cell.artiklerTitleLabel.text = artikler
return cell
}
I'm creating a quiz app with custom cells that include a label of questions and then an answer coming from a UISegmentedControl.
The values of the segmentedcontrols get changed when scrolling and this leads to an inaccurate score. I understand that this is due to UITableView reusing cells.
My tableview's datasource in my main vc is simply the labels for all my questions coming from a plist file.
The code for my custom tableviewcell class is
class QuestionsTableViewCell: UITableViewCell {
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var selection: UISegmentedControl!
var question: String = "" {
didSet {
if (question != oldValue) {
questionLabel.text = question
}
}
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
//Just for testing
#IBAction func segmentChanged(_ sender: UISegmentedControl) {
print("value is ", sender.selectedSegmentIndex);
}
}
where the View is stored in an .XIB file.
And the code for my main vc is
class ViewController: UIViewController, UITableViewDataSource {
let questionsTableIdentifier = "QuestionsTableIdentifier"
#IBOutlet var tableView:UITableView!
var questionsArray = [String]();
var questionsCellArray = [QuestionsTableViewCell]();
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let path = Bundle.main.path(forResource:
"Questions", ofType: "plist")
questionsArray = NSArray(contentsOfFile: path!) as! [String]
tableView.register(QuestionsTableViewCell.self,
forCellReuseIdentifier: questionsTableIdentifier)
let xib = UINib(nibName: "QuestionsTableViewCell", bundle: nil)
tableView.register(xib,
forCellReuseIdentifier: questionsTableIdentifier)
tableView.rowHeight = 108;
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return questionsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(
withIdentifier: questionsTableIdentifier, for: indexPath)
as! QuestionsTableViewCell
let rowData = questionsArray[indexPath.row]
cell.question = rowData
return cell
}
#IBAction func calculate(_ sender: UIButton) {
var score = 0
for cell in tableView.visibleCells as! [QuestionsTableViewCell] {
score += cell.selection.selectedSegmentIndex
}
let msg = "Score is, \(score)"
print(msg)
}
#IBAction func reset(_ sender: UIButton) {
for cell in tableView.visibleCells as! [QuestionsTableViewCell] {
cell.selection.selectedSegmentIndex = 0;
}
}
}
What I'd like to do is just keep track of all 'selection' changes of the Questions cells in an array, and then use that array in cellForRowAt. I'm just confused as to how i can dynamically keep track of changes from a view in another class. I'm new to Swift and would like to solve this is a proper MVC fashion. Thanks
Instead of a simple string array as data source create a class holding the text and the selected index
class Question {
let text : String
var answerIndex : Int
init(text : String, answerIndex : Int = 0) {
self.text = text
self.answerIndex = answerIndex
}
}
Declare questionArray as
var questions = [Question]()
Populate the array in viewDidLoad with
let url = Bundle.main.url(forResource: "Questions", withExtension: "plist")!
let data = try! Data(contentsOf: url)
let questionsArray = try! PropertyListSerialization.propertyList(from: data, format: nil) as! [String]
questions = questionsArray.map {Question(text: $0)}
In the custom cell add a callback and call it in the segmentChanged method passing the selected index, the property question is not needed, the label is updated in cellForRow of the controller
class QuestionsTableViewCell: UITableViewCell {
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var selection: UISegmentedControl!
var callback : ((Int) -> ())?
#IBAction func segmentChanged(_ sender: UISegmentedControl) {
print("value is ", sender.selectedSegmentIndex)
callback?(sender.selectedSegmentIndex)
}
}
In cellForRow add the callback and update the model in the closure
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: questionsTableIdentifier, for: indexPath) as! QuestionsTableViewCell
let question = questions[indexPath.row]
cell.questionLabel.text = question.text
cell.selection.selectedSegmentIndex = question.answerIndex
cell.callback = { index in
question.answerIndex = index
}
return cell
}
To reset the segmented controls in the cells set the property in the model to 0 and reload the table view
#IBAction func reset(_ sender: UIButton) {
questions.forEach { $0.answerIndex = 0 }
self.tableView.reloadData()
}
Now you could calculate the score directly from the model instead of the view.
Don't try to use cells to hold information. As the user scrolls through your table view, cells that scroll out of view will get recycled and their field settings will be lost. Also, newly dequeued cells will have the settings from the last time they were used.
You need to refactor your code to read/write information into a data model. Using an array of Structs as a data model is a reasonable way to go. (Or, as vadian suggests in his answer, and array of Class objects, so you get reference semantics.)
You have an IBAction segmentChanged() in your custom cell class. The next trick is to notify the view controller when the user changes the selection, and to update cells when you set them up in cellForRowAt.
I suggest defining a protocol QuestionsTableViewCellProtocol, and have the view controller conform to that protocol:
protocol QuestionsTableViewCellProtocol {
func userSelected(segmentIndex: Int, inCell cell: UITableViewCell)
}
}
Add a delegate property to your QuestionsTableViewCell class:
class QuestionsTableViewCell: UITableViewCell {
weak var delegate: QuestionsTableViewCellProtocol?
//The rest of your class goes here...
}
Update your cell's segmentChanged() method to invoke the delegate's userSelected(segmentIndex:inCell:) method.
In your view controller's cellForRowAt, set the cell's delegate to self.
func userSelected(segmentIndex: Int, inCellCell cell: UITableViewCell) {
let indexPath = tableView.indexPath(for: cell)
let row = indexPath.row
//The code below assumes that you have an array of structs, `dataModel`, that
//has a property selectedIndex that remembers which cell is selected.
//Adjust the code below to match your actual array that keeps track of your data.
dataModel[row].selectedIndex = segmentIndex
}
Then update cellforRowAt() to use the data model to set the segment index on the newly dequeued cell to the correct index.
Also update your calculate() function to look at the values in your dataModel to calculate the score, NOT the tableView.
That's a rough idea. I left some details out as "an exercise for the reader." See if you can figure out how to make that work.
and thanks in advance for helping me out if you can. I'm still very new to Swift but looking forward to learning quickly so I can help others here on the forum.
For now, I have a problem that I need to solve and I have no idea where to start. Hypothetically, my goal is to achieve something like the following:
First View: User enters names of cities they have visited
Second View: User enters names of museums they saw in that city
Then, each time the user selects a city on the first View, the stored 'museum' information will be displayed.
So far, I've managed to get NSUserDefaults functional, but the same data is shown regardless of the cell that I select from the list on the initial View.
I am using the specific name of the array throughout the app to store, display and read data from NSUserDefaults, and I want to know the best way to edit this so that the app will check which cell I select and then display the appropriate information (or none if there hasn't been any entered for that cell).
I hope that was a good explanation, I know it was long - sorry. Below is my code so far. Any input or suggestions are welcome. Thank you!
import UIKit
import Foundation
class DetailViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var detailTextField: UITextField!
#IBOutlet weak var detailTableView: UITableView!
var receivedString:String = ""
var cell0Array:[NSString] = []
override func viewDidLoad() {
super.viewDidLoad()
detailTextField.delegate = self
detailTableView.delegate = self
detailTableView.dataSource = self
titleLabel.text = receivedString
if NSUserDefaults.standardUserDefaults().objectForKey("cell0Array") != nil {
cell0Array = NSUserDefaults.standardUserDefaults().objectForKey("cell0Array") as! [NSString]
} else {
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
let now = NSDate()
let formatter = NSDateFormatter()
formatter.dateStyle = .MediumStyle
formatter.timeStyle = .NoStyle
let date = formatter.stringFromDate(now)
let detailTextFieldContents = detailTextField.text
let token = "\(date): \(detailTextFieldContents!)lb"
cell0Array.insert(token, atIndex: 0)
NSUserDefaults.standardUserDefaults().setObject(cell0Array, forKey: "cell0Array")
NSUserDefaults.standardUserDefaults().synchronize()
detailTextField.text = ""
detailTextField.resignFirstResponder()
self.detailTableView.reloadData()
return true
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cell0Array.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let detailCell = tableView.dequeueReusableCellWithIdentifier("detailCell", forIndexPath: indexPath)
let detailItem = cell0Array[indexPath.row]
detailCell.textLabel!.text = detailItem as String
detailCell.textLabel!.textAlignment = .Center
self.view.layoutIfNeeded()
return detailCell
}
}
One obvious problem is this line:
func textFieldShouldReturn(textField: UITextField) -> Bool {
// ...
cell0Array.insert(token, atIndex: 0)
// ...
}
Instead of figuring out which row of the table this text field's cell is in, and placing the value at that index, you are always inserting token at index 0. Thus there is no correspondence between rows and entries in cell0Array.
I am querying parse for some information and displaying the info in a UITableViewCell, there is only one entry in the table, a test entry with fictional information but the table is displaying the first cell blank and then the second cell with the information in the table. I have attempted to google this and also add more rows of testing info but either way the table displays one cell at the beginning with no info
Here is my code:
var query = PFQuery(className: "marathons")
query.orderByAscending("end")
query.findObjectsInBackgroundWithBlock { (marathons, error:NSError?) -> Void in
if(error == nil ){
//success
for marathon in marathons! {
self.Name.append(marathon["Name"] as! String)
self.entryNumber.append(marathon["Number"] as! Int)
self.totalEntries.append(marathon["entries"] as! Int)
self.runnerDistance.append(marathon["distance"] as! Int)
}
self.TableView.reloadData()
}else {
print(error)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK: TableView
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Name.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let singleCell: TableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell") as! TableViewCell
singleCell.Name.text = names[indexPath.row]
singleCell.entryNumber.text = "\(entryNumbers[indexPath.row])"
singleCell.totalEntries.text = "\(entires[indexPath.row])"
singleCell.runnerDistance.text = "\(distance[indexPath.row])"
return singleCell
}
Name, entryNumber, totalEntries, and runnerDistance defined as:
#IBOutlet var Name: UILabel!
#IBOutlet var entryNumber: UILabel!
#IBOutlet var totalEntries: UILabel!
#IBOutlet var runnerDistance: UILabel!
Any advice? I am using swift, parse as my back end, and XCODE 7
Where are the model objects defined in your code? You're appending to objects named Name, entryNumber, totalEntries, and runnerDistance, but you're trying to assign values from properties named entryNumbers entries and distance.
Edit:
I see what's going on here. Based off your comment, you're initializing your arrays like this:
var names = [String()]
That actually creates an array with one empty string in it [""]. When you call append, you're adding another element to the array, which makes you wind up with ["","John"]. if you want an empty array you should be creating it like this:
var names = [String]()
I'm a newbie learning iOS and Swift so apologies ahead of time. Currently I'm trying to setup a tableView within a viewController and display data in the cells in a portion of the screen. My current problem seems to be in reloading the tableView data after the Alamofire HTTP request in viewDidLoad() is called for numberOfRowsInSection(). Here's the code:
import UIKit
import Alamofire
import SwiftyJSON
class CourseDetailViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var titleLabel: UILabel?
#IBOutlet weak var creditsLabel: UILabel?
#IBOutlet weak var descriptionLabel: UILabel?
#IBOutlet weak var tableView: UITableView!
var detailCourse: Course? {
didSet {
configureView()
}
}
var course: Course!
func configureView() {
self.title = detailCourse?.abbr
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "SectionCell")
tableView.delegate = self
tableView.dataSource = self
if let theCourse: Course = self.detailCourse as Course! {
var abbr: String = theCourse.abbr!
APIService.getCourseByAbbr(abbr) { (data) -> Void in
self.course = Course(courseJSON: data)
// Set labels
self.titleLabel?.text = self.course.title!
self.descriptionLabel?.text = self.course.description!
if let creditsArray = self.course.credits {
let minimumCredit = creditsArray[0] as Int
self.creditsLabel?.text = String(minimumCredit)
}
self.tableView.reloadData()
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return course.sections.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Configure the cell...
let cell = tableView.dequeueReusableCellWithIdentifier("SectionCell", forIndexPath: indexPath) as! SectionTableViewCell
let sectionCell = course.sections[indexPath.row]
cell.termLabel?.text = sectionCell.term
cell.timeLabel?.text = sectionCell.startTime
cell.instructorLabel?.text = sectionCell.instructor
return cell
}
}
When I run, I get the following error:
fatal error: unexpectedly found nil while unwrapping an Optional value
I believe that the reason may be that I set up the tableView within the viewController incorrectly.
For the full project, here is a link to the repo: https://github.com/classmere/app/tree/develop
The problem is that you're trying to unwrap an optional whose value is nil. When you declare the course property, since its an optional, its initial value is nil. Usually, optionals are declared with ? and the compiler will prevent you from accessing the underlying value without checking if the value is still nil. In this case however, you've made the course property an expected optional:
var course: Course!
This is like saying "I know that course will always have a value and will never be nil". We don't know that however, since its value is nil until the Alamofire callback successfully completes.
To fix this problem, start by making course a standard optional:
var course: Course?
Now Xcode will complain that you're accessing course without unwrapping it, since your declaration of course no longer unwraps it.
Fix this by forcibly unwrapping everything in the Alamofire callback:
APIService.getCourseByAbbr(abbr) { (data) -> Void in
println("APIService()")
self.course = Course(courseJSON: data)
// Notice we can access self.course using ! since just assigned it above
self.titleLabel?.text = self.course!.title!
self.descriptionLabel?.text = self.course!.description!
if let creditsArray = self.course!.credits {
let minimumCredit = creditsArray[0] as Int
self.creditsLabel?.text = String(minimumCredit)
}
self.tableView.reloadData()
}
Then in cellForRowAtIndexPath, we will use optional chaining to ensure we only access course's properties if they exist:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Configure the cell...
let cell = tableView.dequeueReusableCellWithIdentifier("SectionCell", forIndexPath: indexPath) as! SectionTableViewCell
if let section = course?.sections[indexPath.row] {
cell.termLabel?.text = section.term
cell.timeLabel?.text = section.startTime
cell.instructorLabel?.text = section.instructor
}
return cell
}
Finally in numberOfRowsForSection make sure to get the actual number of sections instead of always returning 50. We'll use the nil-coalescing operator to return 0 if course is nil:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return course?.sections.count ?? 0
}
That should fix your problem!