Swift Tableview cell re-appear after deletion - ios

I am working on a twitter clone and I connected my app to firebase to read and write the posts. However, when I delete a cell the animation starts and the deletion actually happens from my posts array. The problem is when I try to delete another post the post I already deleted re-appears for some reason.
Here are some screenshots of what is happening:
First I delete a post and everything is normal.
deleting a post
Then the post is deleted
deleted post
now I try to delete another post
deleting another post
Then the older post re-appears in the place of the last post deleted.
deleted post reappears
This continues until I get an index out of bounds exception when I attempt to delete all the cells.
As seen from the screenshots, I delete the cells from a delete button which is why I need a callback from the cell.
Here is my code for the cell and for the "Home Screen"
class PostTableViewCell: UITableViewCell, UITextViewDelegate{
var didDelete : ((UITableViewCell) -> Void)?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
func set(post: Post){
username.text = post.username
postText.text = post.text
timestamp.text = post.createdAt.calenderTimeSinceNow()
postId = post.id
setPopupMenu(post: post)
}
func setPopupMenu(post: Post){
let options = { (action: UIAction) in
print(action.title)
if(action.title == "Delete"){
// delete post
self.didDelete!(self)
}
}
menuButton.showsMenuAsPrimaryAction = true
}
}
and this is the Home Screen code :
class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
let cellNib = UINib(nibName: "PostTableViewCell", bundle: nil)
var posts = [Post]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(cellNib, forCellReuseIdentifier: "postCell")
tableView.delegate = self
tableView.dataSource = self
readPosts()
// Do any additional setup after loading the view.
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
tableView.reloadRows(at: [indexPath], with: .fade)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! PostTableViewCell
cell.set(post: posts[indexPath.row])
cell.didDelete = { aCell in
self.progressView.isHidden = false
let currentIndexPath = tableView.indexPath(for: aCell)!
let db = Firestore.firestore()
db.collection("Posts").document(Auth.auth().currentUser!.uid).collection("Posts").document(cell.postId).delete() { err in
self.progressView.isHidden = false
if let err = err {
print("Error removing document: \(err)")
} else {
self.posts.remove(at: indexPath.row)
tableView.deleteRows(at: [currentIndexPath], with: .automatic)
self.progressView.isHidden = true
}
}
}
return cell
}
func readPosts(){
let db = Firestore.firestore()
db.collection("Posts").addSnapshotListener { documentSnapshot, error in
guard let document = documentSnapshot else {
print("Error fetching document: \(error!)")
return
}
var tempPosts = [Post]()
for doc in documentSnapshot!.documents{
db.collection("Posts").document(doc.documentID).collection("Posts").order(by: "timestamp").addSnapshotListener { documentSnapshot, error in
for docu in documentSnapshot!.documents {
let daate = docu.data()["timestamp"] as! Timestamp
let mydbl = daate.dateValue().timeIntervalSince1970 * 1000
let post = Post(id: docu.documentID, username: "#\(docu.data()["username"]!)", userId: doc.documentID , text: docu.data()["text"] as! String, timestamp: mydbl , numLikes: 0, numReposts: 0)
let isDuplicate = tempPosts.contains(where: { $0.id == post.id })
if(!isDuplicate){
tempPosts.insert(post, at: 0)
}
}
self.posts.removeAll()
self.posts = tempPosts
self.tableView.reloadData()
}
}
self.progressView.isHidden = true
}
}
}
Note that when I comment out the firebase document deletion part everything works fine and the deleted document is not re produced

Related

UITableViewCell public function not executing

var list = [String]()
#IBOutlet weak var TableView: UITableView!
override func viewDidLoad() {
self.title = "Routines"
TableView.delegate = self
TableView.dataSource = self
super.viewDidLoad()
}
//refresh view when going back to this viewcontroller
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("Test Worked")
TableView.reloadData()
}
//generating rows
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return (list.count)
}
//returning text in UITableViewCell
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = UITableViewCell(style:
UITableViewCell.CellStyle.default, reuseIdentifier:
"prototype1")
print("printed")
cell.textLabel?.text = list[indexPath.row]
return cell
}
//deleting rows
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath)
{
if editingStyle == UITableViewCell.EditingStyle.delete{
deleteAllData("ToDo")
self.list.remove(at: indexPath.row)
TableView.reloadData()
}
}
#IBAction func didAdd() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(identifier: "addRoutinePage")as! addRoutinePage
self.navigationController?.pushViewController(vc, animated: true)
}
//function to get data from core data
func getData()
{
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "ToDo")
request.returnsObjectsAsFaults = false
do{
//fetching data from coredata
let result = try context.fetch(request)
for data in result as! [NSManagedObject]
{
//appending the list from the value in coredata (attribute) or entity
self.list.append(data.value(forKey: "title")as! String)
print("append success")
}
}catch {
print("failed")
}
}
What is wrong with my code? Everything seems to work except for the UITableViewCell, the print command I entered just to check if the function is executed didn't even work. I tried TableView.reloadData() but it still didn't work. Logically if the problem is with the public function or data source or delegate it won't even generate any rows, but rows are generated. I tried resizing the cell height size too but it still won't work. Please help!
There are a few errors with the code:
You need to reload once the data fetching from CoreData is complete.
func getData() {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "ToDo")
request.returnsObjectsAsFaults = false
do {
let result = try context.fetch(request)
for data in result as! [NSManagedObject] {
self.list.append(data.value(forKey: "title")as! String)
}
self.TableView.reloadData()
} catch {
print(error)
}
}
Also, don't forget to call the getData function.
override func viewDidLoad() {
super.viewDidLoad()
title = "Routines"
TableView.delegate = self
TableView.dataSource = self
getData()
}

Reload TableView After Deleting, Adding, or Modifying Firestore Document and Paginating Results

I am retrieving documents from Firebase Firestore and displaying them in a table view. From the table view I want to be able to delete and add items. I also modify documents from the item detail view. I'll focus on my issues deleting items for this question though. I'm getting paginated results with my query by using the last snapshot to only get the next set of items. I'm also using a listener to get realtime updates for when items are modified. The issue with deleting is how to I handle it correctly? What I currently have deletes items just fine but then doubles the remaining rows in the table view.
var items = [Item]()
var itemQuery: Query?
var lastSnapshot: QueryDocumentSnapshot?
func getItems() {
if lastSnapshot == nil {
itemQuery = Firestore.firestore().collection("items").whereField("collection", isEqualTo: self.collection!.id).order(by: "name").limit(to: 25)
} else {
itemQuery = itemQuery?.start(afterDocument: lastSnapshot!)
}
itemQuery!.addSnapshotListener( { (snapshot, error) in
guard let snapshot = snapshot else {
return
}
if snapshot.documents.last != nil {
self.lastSnapshot = snapshot.documents.last
} else {
return
}
if let error = error {
print(error.localizedDescription)
} else {
for document in snapshot.documents {
let docName = document["name"] as? String
let docId = document.documentID
let docImages = document["images"] as? [String]
let docCollection = document["collection"] as? String
let docInfo = document["info"] as? String
let docQuantity = document["quantity"] as? Int
let item = Item(id: docId, name: docName!, collection: docCollection!, info: docInfo!, images: docImages!, quantity: docQuantity!)
self.items.append(item)
}
if self.items.count >= 25 {
self.addFooter()
}
self.tableView.reloadData()
}
})
}
func deleteItem(at indexPath: IndexPath) {
let itemToDelete = items[indexPath.row]
// Delete images from storage
for url in itemToDelete.images {
let store = Storage.storage()
let storageRef = store.reference(forURL: url)
storageRef.delete { error in
if let error = error {
print(error.localizedDescription)
} else {
print("Image file deleted successfully")
}
}
}
Firestore.firestore().collection("items").document(itemToDelete.id).delete() { error in
if let error = error {
print(error.localizedDescription)
} else {
print("Item deleted")
}
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("numberOfRows(): \(items.count)")
return items.count
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath) as! ItemCell
let item = items[indexPath.row]
cell.itemNameLabel.text = item.name
if item.images.count > 0 {
let thumbnailUrl = item.images[0]
cell.itemImageView.sd_setImage(with: URL(string: thumbnailUrl), placeholderImage: UIImage(named: "photo"), completed: { (image, error, cacheType, imageUrl) in
cell.itemImageView.roundCornersForAspectFit(radius: 10)
})
} else {
cell.itemImageView.image = UIImage(named: "photo")
}
return cell
}
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
print("Items before delete: \(items.count)")
deleteItem(at: indexPath)
// items.removeAll()
// tableView.reloadData()
items.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
print("Items after delete: \(items.count)")
}
}
You can use Property Observer to handle your tableView.reloadData()
var items = [Item]() {
didSet {
tableView.reloadData()
}
}
what it does above is whenever variable items is modified, it will trigger didSet {} block of code.
Hope is will answer your question.

UITableView SIGBRT error when trying to "swipe left to delete " at indexpath.row

I have a simple app that populates a UITableView based on data inputed in a different ViewController. I am trying to implement the "swipe left to delete"
My problem is that this UITableView is a dropdown table view. That is when I click on one cell of the UITableView the cells open up and show me the internal cells associated with that one cells.
I think I am missing something simple as my code to delete the row does not work, it just throws a SIGBRT error. I think because maybe I trying to remove the wrong array maybe? I think it is messed up because it is a dropdown UITableView, so I am left with a bunch of extra UITableview rows?
Code to added delete button and remove selected row.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// remove the item from the data model
tableViewData.remove(at: indexPath.row)
// delete the table view row
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
Whole code in the UITableViewController is as follows:
import Foundation
import UIKit
private let reuseidentifier = "Cell"
struct cellData {
var opened = Bool()
var title = String()
var exerciseData = [String]()
var repsSetsData = [String]()
}
//here
struct Contact {
var fullname: String
var exercises : [Exercise]
}
class Exercise : NSObject , NSSecureCoding{
static var supportsSecureCoding: Bool = true
var excerciseName: String
var excerciseReps: String
var excerciseSets: String
init(Name : String, Reps : String, Sets : String) {
excerciseName = Name
excerciseReps = Reps
excerciseSets = Sets
}
func encode(with aCoder: NSCoder) {
aCoder.encode(excerciseName, forKey: "excerciseName")
aCoder.encode(excerciseReps, forKey: "excerciseReps")
aCoder.encode(excerciseSets, forKey: "excerciseSets")
}
required convenience init?(coder aDecoder: NSCoder) {
let excerciseName = aDecoder.decodeObject(forKey: "excerciseName") as! String
let excerciseReps = aDecoder.decodeObject(forKey: "excerciseReps") as! String
let excerciseSets = aDecoder.decodeObject(forKey: "excerciseSets") as! String
self.init(Name: excerciseName, Reps: excerciseReps, Sets: excerciseSets)
}
}
class ContactController: UITableViewController {
//new
var tableViewData = [cellData]()
var contacts = [Contact]()
override func viewDidLoad() {
super.viewDidLoad()
//getting data from CoreData
self.contacts = CoreDataManager.sharedInstance.retrieveDataFromCoreData()
tableView.register(UINib(nibName: "ExerciseCell", bundle: nil), forCellReuseIdentifier: "ExerciseCell")
for contact in contacts{
var exerciseData = [String]()
var repsSetsData = [String]()
for exercise in contact.exercises{
let name = exercise.excerciseName
let sets = exercise.excerciseSets
let reps = exercise.excerciseReps
exerciseData.append(name)
repsSetsData.append("Reps: " + reps + " Sets: " + sets)
}
self.tableViewData.append(cellData.init(opened: false, title: contact.fullname, exerciseData:exerciseData, repsSetsData: repsSetsData))
}
self.tableView.reloadData()
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationItem.title = "Workouts"
view.backgroundColor = .white
tableView.register(UITableViewCell.self, forCellReuseIdentifier: reuseidentifier)
}
#IBAction func handleAddContact(_ sender: Any) {
let controller = AddContactController()
controller.delegate = self
self.present(UINavigationController(rootViewController: controller), animated: true, completion: nil)
}
//UITABLEVIEW
//all new
override func numberOfSections(in tableView: UITableView) -> Int {
//new
return tableViewData.count
}
override func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
//new
if tableViewData[section].opened == true {
return tableViewData[section].exerciseData.count + 1
}else {
return 1
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseidentifier, for: indexPath)
cell.textLabel?.text = tableViewData[indexPath.section].title
return cell
}else {
//use a different cell identifier if needed
let cell = tableView.dequeueReusableCell(withIdentifier: "ExerciseCell", for: indexPath) as! ExerciseCell
cell.exerciseLabel.text = tableViewData[indexPath.section].exerciseData[indexPath.row - 1]
cell.repsSetsLabel.text = tableViewData[indexPath.section].repsSetsData[indexPath.row - 1]
cell.repsSetsLabel.sizeToFit()
return cell
}
}
//did select row new
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableViewData[indexPath.section].opened == true {
tableViewData[indexPath.section].opened = false
let sections = IndexSet.init(integer: indexPath.section)
tableView.reloadSections(sections, with: .none) //play around with animation
}else {
tableViewData[indexPath.section].opened = true
let sections = IndexSet.init(integer: indexPath.section)
tableView.reloadSections(sections, with: .none) //play around with animation
}
}
//being able to delete a row
// this method handles row deletion
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// remove the item from the data model
tableViewData.remove(at: indexPath.row)
// delete the table view row
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
}
//this is an extention to addContactController. this is what happens whent he done button is clicked in addcontactcontroller
extension ContactController: AddContactDelegate {
func addContact(contact: Contact) {
self.dismiss(animated: true) {
//Saving Data to CoreData
CoreDataManager.sharedInstance.addContactsToCoreData(contact: contact)
self.contacts.append(contact)
//Settings values in table view
var exerciseData = [String]()
var repsSetsData = [String]()
for exercise in contact.exercises{
let name = exercise.excerciseName
let sets = exercise.excerciseSets
let reps = exercise.excerciseReps
exerciseData.append(name)
repsSetsData.append("Reps: " + reps + " Sets: " + sets)
}
self.tableViewData.append(cellData.init(opened: false, title: contact.fullname, exerciseData:exerciseData, repsSetsData: repsSetsData))
self.tableView.reloadData()
}
}
}
After deleting the rows can you try to reload the tableview cells like this: self.tableView.reloadData()
It's supposed that you delete a row not an entire section , so replace
tableViewData.remove(at: indexPath.row)
with
tableViewData[indexPath.section].exerciseData.remove(at: indexPath.row)
also make sure exerciseData is mutable ( declared as var )

tableview in tabbar not reloading

I have a TabBar with various Tabs in my RestaurantApp, When I click the addToCart and goes to the CartViewContorller, the added item don't show I have to relaunch the App to see the item there. I have seen similar questions with various answer on this question here but non of the solutions seems to work in my case I don't really know whats wrong. Below is my code for the CartViewContorller I want to reload tableview anytime it is loaded. Thanks all for your help
import UIKit
import Alamofire
import os.log
class CartViewController: UITableViewController {
var cartData = [CartResponse.Cart]()
override func viewDidLoad() {
super.viewDidLoad()
cart()
tableView.delegate = self
tableView.dataSource = self
let nib = UINib(nibName: "viewCartCell", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "cartCustomCell")
let footerView = UIView()
footerView.backgroundColor = UIColor.red
footerView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 60)
tableView.tableFooterView = footerView
}
override func viewDidAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tableView.reloadData()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cartData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell: CartTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "cartCustomCell", for: indexPath) as? CartTableViewCell else {
os_log("Dequeue cell isn't an instance of CustomTableCell", log: .default, type: .debug)
fatalError()
}
cell.recipeNameLbl?.text = cartData[indexPath.row].recipeName
cell.restaurantNameLbl?.text = cartData[indexPath.row].restaurantName
cell.addtionalNoteLbl?.text = cartData[indexPath.row].additionalNote
cell.quantityLbl?.text = cartData[indexPath.row].recipeQuantity
cell.totalLbl?.text = cartData[indexPath.row].recipePrice
cell.totalCostLbl?.text = cartData[indexPath.row].totalCost
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
guard editingStyle == .delete else {return}
//getting userId from defaults
let CartId = cartData[indexPath.row].cartId
let cartId = CartId
//creating parameters for the post request
let parameters: Parameters=[
"cartId":Int(cartId)
]
//Constant that holds the URL for web service
let URL_SELECT_FROM_CART = "http://localhost:8888/restaurant/deleteFromCart.php?"
Alamofire.request(URL_SELECT_FROM_CART, method: .post, parameters: parameters).responseJSON {
response in
//printing response
print(response)
}
cartData.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
}
//Fetching from Cart Method
func cart(){
//getting userId from defaults
let defaultValues = UserDefaults.standard
let userId = defaultValues.string(forKey: "userid")
//creating parameters for the post request
let parameters: Parameters=[
"userId":Int(userId!)!
]
//Constant that holds the URL for web service
let URL_SELECT_FROM_CART = "http://localhost:8888/restaurant/cart.php?"
Alamofire.request(URL_SELECT_FROM_CART, method: .post, parameters: parameters).responseJSON {
(response) in
let result = response.data
do{
let decoder = JSONDecoder()
let downloadedCart = try decoder.decode(CartResponse.self, from: result!)
self.cartData = downloadedCart.cartItem
DispatchQueue.main.async {
self.tableView.reloadData()
}
}catch {
print(error)
}
}.resume()
}
}
You can use :
import UserNotifications
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "loadCart"), object: nil)
see more in this answer
You have to call cart() this method in viewWillAppear instead of calling viewDidload
override func viewWillAppear(_ animated: Bool) {
self.cart()
}

Displaying Comments Under a Post

I'm new to this and having trouble displaying the comments of a post in a table view with two cells (one cell = post, second cell = comments). Here is my code for the View Controller:
import UIKit
class PostDetailViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var postId = ""
var post = Post()
var user = User()
var comments = [Comment]()
var users = [User]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
print("postId = \(postId)")
loadPost()
loadComments()
}
func loadPost() {
API.Post.observePost(withId: postId) { (post) in
guard let postUid = post.uid else {
return
}
self.fetchUser(uid: postUid, completed: {
self.post = post
self.tableView.reloadData()
})
self.navigationItem.title = post.title
}
}
func fetchUser(uid: String, completed: #escaping () -> Void ) {
API.User.observeUser(withId: uid, completion: {
user in
self.user = user
completed()
})
}
func loadComments() {API.Post_Comment.REF_POST_COMMENTS.child(self.postId).observe(.childAdded, with: {
snapshot in
API.Comment.observeComments(withPostId: snapshot.key, completion: { comment in
self.fetchUser(uid: comment.uid!, completed: {
self.comments.append(comment)
self.tableView.reloadData()
})
})
})
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Detail_CommentSegue" {
let commentVC = segue.destination as! CommentViewController
let postId = sender as! String
commentVC.postId = postId
}
if segue.identifier == "Detail_ProfileUserSegue" {
let profileVC = segue.destination as! ProfileUserViewController
let userId = sender as! String
profileVC.userId = userId
}
}
}
extension PostDetailViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return comments.count + 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "DetailPostCell", for: indexPath) as! DetailTableViewCell
cell.post = post
cell.user = user
cell.delegate = self
return cell
} else {
let commentCell = tableView.dequeueReusableCell(withIdentifier: "Detail_CommentCell") as! CommentTableViewCell
let comment = comments[indexPath.row]
let user = users[indexPath.row]
commentCell.comment = comment
commentCell.user = user
return commentCell
}
}
}
extension PostDetailViewController: DetailTableViewCellDelegate {
func goToCommentVC(postId: String) {
performSegue(withIdentifier: "Detail_CommentSegue", sender: postId)
}
func goToProfileUserVC(userId: String) {
performSegue(withIdentifier: "Detail_ProfileUserSegue", sender: userId)
}
}
The error I'm getting is at:
let comment = comments[indexPath.row]
"fatal error: Index out of range"
There are comments and they appear on the Comments View Controller without issue. I realize the error has something to do with calling two cells but I am unable to figure out a fix.
You are not accounting for the first row when getting a value from your arrays.
The simple fix is to update cellForRowAt as follows:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "DetailPostCell", for: indexPath) as! DetailTableViewCell
cell.post = post
cell.user = user
cell.delegate = self
return cell
} else {
let commentCell = tableView.dequeueReusableCell(withIdentifier: "Detail_CommentCell") as! CommentTableViewCell
let comment = comments[indexPath.row - 1] // here
let user = users[indexPath.row - 1] // here
commentCell.comment = comment
commentCell.user = user
return commentCell
}
}
Note the two minor changes marked // here. The idea is that comment[0] is at row 1.
Some better suggestions:
Do not have multiple arrays. Have one array for your data. Define a structure that holds all of the data for one row. Then have one array of those structures. This makes your data easier to handle and makes things like sorting and filtering vastly simpler.
Consider putting your different data in different sections instead of all in one section.

Resources