Thread 1: Fatal error: Index out of range, return array.count + 1 - swift

I have a tableview with at all times one cell. If there is data to download from Firebase it will put it in an array called posts. When there's for example, two "in my case" servers that the user will download, it will only display one cell instead of two. I thought I could fix this by changing return posts.count to return posts.count + 1 because of the one cell that will be shown at all times. But if I use return posts.count + 1 I will get a
Thread 1: Fatal error: Index out of range
error on line let post = posts[indexPath.row]. I have read about this error, but I can't seem to fix it.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if posts.count == 0 {
self.tableView.setEmptyMessage("No Servers uploaded!")
return 1
} else {
self.tableView.restore()
return posts.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "ProfileCell", for: indexPath) as! ProfileCellTableViewCell
cell.delegate = self
return cell
}else {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TableViewCellMYposts
cell.Map_image.image = nil
let post = posts[indexPath.row]
cell.post = post
cell.delegate = self
return cell
}
}

Assuming you have some piece of data in posts[0], you are never actually displaying it. For indexPath.row = 0, you are displaying a profile cell, and then you start displaying the data from posts[1] and on. Change your problem line to:
let post = posts[indexPath.row - 1]

Related

Fatal error: Index out of range when filling cells

I am getting the this error: Index out of range when trying to fill cells out of a table, any idea why this happens?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: VCInstanciasCell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! VCInstanciasCell
cell.siteLabel.text = String?([arrayItinerario[indexPath.section]][indexPath.row].ubicacion!) ?? ""
cell.txtLabel.text = String?([arrayItinerario[indexPath.section]][indexPath.row].maquinaTipo!) ?? ""
cell.orderLabel.text = String?([arrayItinerario[indexPath.section]][indexPath.row].fma!) ?? ""
cell.codeLabel.text = String?([arrayItinerario[indexPath.section]][indexPath.row].iso!) ?? ""
cell.newLabel.text = String?([arrayItinerario[indexPath.section]][indexPath.row].instanciaID!) ?? ""
cell.setLeftUtilityButtons(self.leftButtons(), withButtonWidth: 50.0)
return cell
}
the error is at cell.siteLabel.text = String?([arrayItinerario[indexPath.section]][indexPath.row].ubicacion!) ?? "" in indexPath.Row
It is quite hard to answer this question without seeing how arrayItinerario is defined, and what triggers the cellForRow (when is the timing you're calling reloadData)
But first of all, I'd try to simplify your code, it will make it easier to understand where the crash comes from, as you're using force unwrapped and also cannot know in which of the row/section you get the index out of range.
I added some checks + guards + prints, it will explain my answer + make your testing easier:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: VCInstanciasCell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! VCInstanciasCell
if let value = value(for: indexPath) {
cell.siteLabel.text = ""
if let ubicacion = value.ubicacion {
cell.siteLabel.text = String(ubicacion)
}
// and so on
} else {
print("couldn't get value for indexPath:\(indexPath)")
}
return cell
}
func value(for indexPath: IndexPath) -> SomeObject? {
let section = indexPath.section
let row = indexPath.row
guard section < arrayItinerario.count else {
print("section \(section) is out of range for list with count: \(arrayItinerario.count)")
return nil
}
let list = arrayItinerario[section]
guard row < list.count else {
print("row \(row) is out of range for list with count: \(list.count)")
return nil
}
return list[row]
}

Swift - Table View - Repeat order of different sections for multiple data

I have a table with four different sections. After I fetch data from the database and store them in an array, I want to display them in the table view. This means, for each object, I will extract information and put it into the correct section of the table view.
To describe it more simple:
Object 1
TableViewCell 1
TableViewCell 2
TableViewCell 3
TableViewCell 4
Object 2
TableViewCell 1
TableViewCell 2 ..
Currently, my approach looks like
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->
UITableViewCell {
let participationObject = Participation(json: (hitsSource?.hit(atIndex: indexPath.row))!)
checkIfUserAlreadyRatedForParticipationContent(participationObject: participationObject)
if (indexPath.section == 0 || indexPath.section % 4 == 0) {
let userProfileCell = tableView.dequeueReusableCell(withIdentifier: "userProfileCell", for: indexPath) as! ChallengeInfoUserTableViewCell
self.dataAccessService.fetchUserById(completionHandler: { (challengeOrganizer) in
let cell = tableView.cellForRow(at: indexPath) as! ChallengeInfoUserTableViewCell
userProfileCell.fullNameLabel.text = challengeOrganizer.firstName + " " + challengeOrganizer.lastName
userProfileCell.fetchProfileImageOfUser(userObject: challengeOrganizer)
userProfileCell.fetchNumberOfWonChallengesByOrganizerId(challengeOrganizer: challengeOrganizer)
userProfileCell.starNumberLabel.text = challengeOrganizer.stars
userProfileCell.delegate = self
}, uid: participationObject.userId)
return userProfileCell
}
else if (indexPath.section % 4 == 1) {
let challengeImageCell = tableView.dequeueReusableCell(withIdentifier: "challengeImageCell", for: indexPath) as! ChallengeInfoImageTableViewCell
dataAccessService.fetchParticipationImageByParticipantIdChallengeId(completionHandler: { (participationImage) in
let cell = tableView.cellForRow(at: indexPath) as! ChallengeInfoImageTableViewCell
challengeImageCell.checkIfToAddOrRemovePlayIcon(participationObject: participationObject)
challengeImageCell.setChallengeImage(challengeImage: participationImage)
challengeImageCell.delegate = self
challengeImageCell.moreButton.tag = indexPath.row
}, participantId: participationObject.userId, challengeId: challengeObject.id)
return challengeImageCell
}
else if (indexPath.section % 4 == 2) {
let commentCell = tableView.dequeueReusableCell(withIdentifier: "commentCell", for: indexPath) as! ChallengeContentDetailCommentTableViewCell
commentCell.fetchCommentsByParticipationId(participationObject: participationObject)
return commentCell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func numberOfSections(in tableView: UITableView) -> Int {
return (hitsSource?.numberOfHits())! * 4
}
I have two issues:
1.) I get always the same object from my data source
2.) When calling let cell = tableView.cellForRow(at: indexPath) as! ChallengeInfoImageTableViewCell It crashes at some point.
Is my approach even legit? Is it clean?
This depends on the data array and you are ignoring the section parameter in numberOfSections(in:)
Never retrieve data asynchronously in cellForRow and update the cell by calling tableView.cellForRow(at:. Cells can be moved off screen immediately so there is no cellForRow(at.
Note:
The first expression in (indexPath.section == 0 || indexPath.section % 4 == 0) is redundant. indexPath.section % 4 is 0 if section is 0.

Thread 1: Fatal error: Index out of range from tableViewCell

I have error says "Thread 1: Fatal error: Index out of range".
on
cell.titleLabel.text = cellDataArrayPoster[indexPath.row].jobTitlePoster as? String
please notice that I'm using two different cells,
as prototypeCells. Moreover, they both have different identifier.
both arrays have getting their data from firebase.
var cellDataArray = [cellData]()
var cellDataArrayPoster = [cellDataPoster]()
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellDataArray.count + cellDataArrayPoster.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let index = indexPath.row
if index == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! infoCell
cell.titleLabel.text = cellDataArray[indexPath.row].jobTitle as? String
cell.companyLabel.text = cellDataArray[indexPath.row].companyName
//cell.timeStampLabel.text = cellDataArray[indexPath.row].createdAt.calenderTimeSinceNow()
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellPoster", for: indexPath) as! infoCellPoster
cell.titleLabel.text = cellDataArrayPoster[indexPath.row].jobTitlePoster as? String
//cell.timeStampLabel.text = cellDataArray[indexPath.row].createdAt.calenderTimeSinceNow()
return cell
}
}
You misunderstood the concept of table view delegate methods. It is good to read more from the documentation.
My general rule of thumb is to always use only 1 array as data source for table view to avoid index out of range situations.
In your particular case the error is saying all about it - you are trying to reach index number that is out of range of the array. The easiest workaround will be to combine the two arrays in one, and have some sort of inheritance between the objects so they can fit.

Displaying two reusable cells in tableview - Swift 3

I have two custom reusable table view cells in my table view. The first cell, I would like it to be present at all times. The second cell and beyond, are returning a count that is being passed from mysql database.
// return the amount of cell numbers
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
// cell config
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row < 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! InfoCell
//set the data here
return cell
} else {
let Postcell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexPath) as! PostCell
let post = posts[indexPath.row]
let image = images[indexPath.row]
let username = post["user_username"] as? String
let text = post["post_text"] as? String
// assigning shortcuts to ui obj
Postcell.usernameLbl.text = username
Postcell.textLbl.text = text
Postcell.pictureImg.image = image
return Postcell
}
} // end of function
My first cell is there and so are the post.count, but for some reason the posts.count is missing one post and I believe this is because of the first cell. Can anybody help me with this? thanks in advance.
You need to adjust the value returned from numberOfRowsInSection to account for the extra row. And you would need to adjust the index used to access values from your posts array to deal with the extra row.
But a much better solution is to use two sections. The first section should be your extra row and the second section would be your posts.
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 1
} else {
return posts.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! InfoCell
//set the data here
return cell
} else {
let Postcell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexPath) as! PostCell
let post = posts[indexPath.row]
let image = images[indexPath.row]
let username = post["user_username"] as? String
let text = post["post_text"] as? String
// assigning shortcuts to ui obj
Postcell.usernameLbl.text = username
Postcell.textLbl.text = text
Postcell.pictureImg.image = image
return Postcell
}
}

Assigning an array count to a tableviewcell label

I have a posts array in which I'm returning all the posts from a PHP file. I'm trying to assign the number of posts to a label in a tableview cell. However, when I assign the number of posts to a label, I'm getting a nil value. Can anyone help with this?
var posts = [AnyObject]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 1
} else {
return posts.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! InfoCell
print(posts.count)
cell.PostsLbl.text = String(posts.count)
return cell
}
}
It looks like you are getting posts async from PHP file and after you get them you reload only the second section.