Swift - Save all documents of firestore collection in a list of objects - swift

I want to retrieve all the documents of a firestore collection, and then write it in an object then append it my list of objects.
after that i display it in an UITableView.
Here is what I have, it works without errors but when I run it, nothing is displayed.
The list structure:
struct RewardsStruct {
//var rewardKey: String
var Reward: String
var noPoints: String
var QRimageURL: ImageURL = ImageURL(url: nil, didLoad: false)
var Desc: String
var isvalid: Bool
}
Here is my retrieving code:
private func getRewards() {
var rewardsList = [RewardsStruct]()
let db = Firestore.firestore()
db.collection("Rewards").getDocuments { (snapshot, error) in
if error != nil {
print(error)
} else {
for document in (snapshot?.documents)! {
let code = RewardsStruct( Reward: document.data()["Reward"] as! String , noPoints: document.data()["noPoints"] as! String , QRimageURL: document.data()["QRimageURL"] as! ImageURL, Desc:document.data()["Desc"] as! String, isvalid: (document.data()["isvalid"] != nil) )
self.rewardsList.append(code)
DispatchQueue.main.async {
self.CodeTable.reloadData()
}
}
}
}
}
The rest of code in ViewController as some requests
class RewardsVC: UIViewController {
var rewardsList = [RewardsStruct]()
var reward:RewardsStruct!
#IBOutlet weak var infoView: UIView!
#IBOutlet weak var ViewLabel: UILabel!
#IBOutlet weak var CodeTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
CodeTable.delegate = self
CodeTable.dataSource = self
ViewLabel.isHidden = true
infoView.makeCornerRounded(cornerRadius: 30, maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner])
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
rewardsList.removeAll()
getRewards()
}
private func getRewards() {
....
}
extension RewardsVC: UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
CodeTable.backgroundColor = UIColor(named: "#F5F5F5")
return 60
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 125
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("List number of rows")
print(rewardsList.count)
return rewardsList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "RewardsCell") as! RewardsCell
let object = rewardsList[indexPath.row]
cell.Reward.text = object.Reward
cell.Desc.text = object.Desc
cell.noPoints.text = "-" + object.noPoints + " Points"
return cell
}
func addShadow(backgroundColor: UIColor = .white, cornerRadius: CGFloat = 12, shadowRadius: CGFloat = 5, shadowOpacity: Float = 0.1, shadowPathInset: (dx: CGFloat, dy: CGFloat), shadowPathOffset: (dx: CGFloat, dy: CGFloat)) {
} }
Here is my FireStore:

Try this example code, to ...save all documents of firestore collection in a list of objects....
getRewards(...) is called an asynchronous function, and needs a way to "wait" for the results to be available before you can use them.
There are many ways to do this, here I present an example code that uses a completion handler to pass the results (or errors) of getRewards(...) back to the calling function. Note, the code is untested since I do not have your database (or even Firestore).
// -- here some error type for testing
enum FireError: Error {
case decodingError
case badError
// ...
}
// -- here completion handler
private func getRewards(completion: #escaping ([RewardsStruct], FireError?) -> ()) {
var rewardsList = [RewardsStruct]()
let db = Firestore.firestore()
db.collection("Rewards").getDocuments { (snapshot, error) in
if error != nil {
print(error)
completion([], FireError.badError) // <-- here
} else {
for document in (snapshot?.documents)! {
let code = RewardsStruct(Reward: document.data()["Reward"] as! String , noPoints: document.data()["noPoints"] as! String , QRimageURL: document.data()["QRimageURL"] as! ImageURL, Desc:document.data()["Desc"] as! String, isvalid: (document.data()["isvalid"] != nil) )
self.rewardsList.append(code)
}
completion(rewardsList, nil) // <-- here
}
}
}
Use the function like this:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
getRewards() { results, error in // <-- here
if error == nil {
rewardsList.removeAll()
rewardsList = results
DispatchQueue.main.async {
self.CodeTable.reloadData()
}
} else {
// todo deal with errors
}
}
}
Alternatively, you can also use this code, without using a completion handler, since getRewards(...) is inside your UIViewController.
private func getRewards() {
let db = Firestore.firestore()
db.collection("Rewards").getDocuments { (snapshot, error) in
if error != nil {
print(error)
} else {
self.rewardsList.removeAll() // <-- here
for document in (snapshot?.documents)! {
let code = RewardsStruct(Reward: document.data()["Reward"] as! String , noPoints: document.data()["noPoints"] as! String , QRimageURL: document.data()["QRimageURL"] as! ImageURL, Desc:document.data()["Desc"] as! String, isvalid: (document.data()["isvalid"] != nil) )
self.rewardsList.append(code) // <-- here
}
DispatchQueue.main.async { // <-- here
self.CodeTable.reloadData()
}
}
}
}
and use it like this:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
getRewards()
}

Related

Filtering Image Data with Search Bar

I am trying to filter the data from API. The is successful loaded into view controller with table view cell . This is a movie applications . I am trying to filter the data based on the user type into the text box . I mentioned in the code filter my the title of the movie but The code is only able to filter the title and overview of the movie but the Image fields remain unfiltered such as image , overview etc. Here is the struct model .
import Foundation
struct Movie: Decodable {
let originalTitle: String
let overview: String
let posterPath: String
enum CodingKeys: String, CodingKey {
case originalTitle = "original_title"
case overview
case posterPath = "poster_path"
}
}
Here is the protocol class code .
import Foundation
class MoviePresenter: MoviePresenterProtocol {
private let view: MovieViewProtocol
private let networkManager: NetworkManager
var movies = [Movie]()
private var cache = [Int: Data]()
var rows: Int {
return movies.count
}
init(view: MovieViewProtocol, networkManager: NetworkManager = NetworkManager()) {
self.view = view
self.networkManager = networkManager
}
func getMovies() {
let url = "https://api.themoviedb.org/3/movie/popular?language=en-US&page=3&api_key=6622998c4ceac172a976a1136b204df4"
networkManager.getMovies(from: url) { [weak self] result in
switch result {
case .success(let response):
self?.movies = response.results
self?.downloadImages()
DispatchQueue.main.async {
self?.view.resfreshTableView()
}
case .failure(let error):
DispatchQueue.main.async {
self?.view.displayError(error.localizedDescription)
}
}
}
}
func getTitle(by row: Int) -> String? {
return movies[row].originalTitle
}
func getOverview(by row: Int) -> String? {
return movies[row].overview
}
func getImageData(by row: Int) -> Data? {
return cache[row]
}
private func downloadImages() {
let baseImageURL = "https://image.tmdb.org/t/p/w500"
let posterArray = movies.map { "\(baseImageURL)\($0.posterPath)" }
let group = DispatchGroup()
group.enter()
for (index, url) in posterArray.enumerated() {
networkManager.getImageData(from: url) { [weak self] data in
if let data = data {
self?.cache[index] = data
}
}
}
group.leave()
group.notify(queue: .main) { [weak self] in
self?.view.resfreshTableView()
}
}
}
Here is the controller code .
import UIKit
class MovieViewController: UIViewController, UISearchBarDelegate {
#IBOutlet weak var userName: UILabel!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
private var presenter: MoviePresenter!
var finalname = ""
override func viewDidLoad() {
super.viewDidLoad()
userName.text = "Hello: " + finalname
setUpUI()
presenter = MoviePresenter(view: self)
searchBarText()
}
private func setUpUI() {
tableView.dataSource = self
tableView.delegate = self
}
private func searchBarText() {
searchBar.delegate = self
}
#IBAction func selectSegment(_ sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 1{
setUpUI()
presenter = MoviePresenter(view: self)
presenter.getMovies()
}
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText == ""{
presenter.getMovies()
}
else {
presenter.movies = presenter.movies.filter({ movies in
return movies.originalTitle.lowercased().contains(searchText.lowercased())
})
}
tableView.reloadData()
}
}
extension MovieViewController: MovieViewProtocol {
func resfreshTableView() {
tableView.reloadData()
}
func displayError(_ message: String) {
let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
let doneButton = UIAlertAction(title: "Done", style: .default, handler: nil)
alert.addAction(doneButton)
present(alert, animated: true, completion: nil)
}
}
extension MovieViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
presenter.rows
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: MovieViewCell.identifier, for: indexPath) as! MovieViewCell
let row = indexPath.row
let title = presenter.getTitle(by: row)
let overview = presenter.getOverview(by: row)
let data = presenter.getImageData(by: row)
cell.configureCell(title: title, overview: overview, data: data)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let dc = storyboard?.instantiateViewController(withIdentifier: "MovieDeatilsViewController") as! MovieDeatilsViewController
let row = indexPath.row
dc.titlemovie = presenter.getTitle(by: row) ?? ""
dc.overview = presenter.getOverview(by: row) ?? ""
dc.imagemovie = UIImage(data: presenter.getImageData(by: row)!)
self.navigationController?.pushViewController(dc, animated: true)
}
}
extension MovieViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
}
Here is the screenshot of the result .
Caching image in tableview is a little bit tricky, and you may get problem when the cell changes or reusing itself,
that's cause you see same image when texts are different.
there are 2 famous package you can use it for you're problem and it's easy to use with a lot of options.
1- Kingfisher
2- SDWebImage

How to add data into Firestore Swift by tapping on a cell

I have more restaurants , each have diferent food.Look here
This is how im retrieving data from the Firestore. In the previous controller I have a list of restaurants, each contains a list food.
struct Food {
var photoKeyRestaurant: String
var foodName: String
var foodDescription: String
var restaurantName: String
var priceFood: Int
}
class RestaurantViewController: UIViewController {
var restaurantName: String!
var food: [Food] = []
private let tableView: UITableView = {
let table = UITableView()
return table
}()
func getDatabaseRecords() {
let db = Firestore.firestore()
// Empty the array
food = []
db.collection("RestaurantViewController").whereField("restaurantName", isEqualTo: restaurantName).getDocuments { (snapshot, error) in
if let error = error {
print(error)
return
} else {
for document in snapshot!.documents {
let data = document.data()
let newEntry = Food(photoKeyRestaurant: data["photoKeyRestaurant"] as! String, foodName: data["foodName"] as! String, foodDescription: data["foodDescription"] as! String, restaurantName: data["restaurantName"] as! String , priceFood: data["priceLabel"] as! Int
)
self.food.append(newEntry)
}
}
DispatchQueue.main.async {
// self.datas = self.filteredData
self.tableView.reloadData()
}
}
}
How can I add the data of the selected cell by pressing on + to Firestore in this function ?
I'vrea create a protocol in my FoodTableViewCell , and I've called it in the RestaurantViewController.
func diddTapButtonCell(_ cell: FoodTableViewCell) {
let db = Firestore.firestore()
db.collection("cart").addDocument.(data: foodName) { (err) in
}
Edited: Added Table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return food.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FoodTableViewCell", for: indexPath) as! FoodTableViewCell
cell.delegate = self
let mancare = food[indexPath.row]
let storageRef = Storage.storage().reference()
let photoRef = storageRef.child(mancare.photoKeyRestaurant)
cell.foodImage.sd_setImage(with: photoRef)
cell.descriptionLabel.text = mancare.foodDescription
cell.foodNameLabel.text = mancare.foodName
cell.priceLabel.text = "\(mancare.priceFood) lei"
//Fac ca imaginea sa fie cerc - start
cell.foodImage.layer.borderWidth = 1
cell.foodImage.layer.masksToBounds = false
cell.foodImage.layer.borderColor = UIColor.black.cgColor
cell.foodImage.layer.cornerRadius = cell.foodImage.frame.height/2
cell.foodImage.clipsToBounds = true
//Fac ca imaginea sa fie cerc - finish
return cell
}
This is my tableview cell code
protocol CustomCellDelegate {
func diddTapButtonCell (_ cell: FoodTableViewCell)
}
class FoodTableViewCell: UITableViewCell {
var delegate: CustomCellDelegate?
#IBOutlet weak var foodImage: UIImageView!
#IBOutlet weak var foodNameLabel: UILabel!
#IBOutlet weak var descriptionLabel: UILabel!
#IBOutlet weak var priceLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
#IBAction func addToCart(_ sender: Any) {
delegate?.diddTapButtonCell(self)
}
}
https://firebase.google.com/docs/firestore/manage-data/add-data
And here is an example from one of my projects for adding data to firestore.
func updateDocument(rootCollection : String, doc: String, newValueDict: [String : Any], completion:#escaping (Bool) -> Void = {_ in }) {
let db = Firestore.firestore()
db.collection(rootCollection).document(doc).setData(newValueDict, merge: true){ err in
if let err = err {
print("Error writing document: \(err)")
completion(false)
}else{
completion(true)
}
}
}

Not able to load data from firestore to uitableview

I am able to query the data and match it to my model but am not able to display it in my table view. I have 3 files I am working with apart from the storyboard.
Here is the main view controller:
class MealplanViewController: UIViewController {
var db: Firestore!
var mealplanArray = [Mealplan]()
#IBOutlet weak var mealplanTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
mealplanTableView?.dataSource = self
mealplanTableView?.delegate = self
db = Firestore.firestore()
loadData()
// Do any additional setup after loading the view.
}
func loadData() {
userEmail = getUserEmail()
db.collection("Meal_Plans").getDocuments() {querySnapshot , error in
if let error = error {
print("\(error.localizedDescription)")
} else {
self.mealplanArray = querySnapshot!.documents.compactMap({Mealplan(dictionary: $0.data())})
print(self.mealplanArray)
DispatchQueue.main.async {
self.mealplanTableView?.reloadData()
}
}
}
}
func getUserEmail() -> String {
let user = Auth.auth().currentUser
if let user = user {
return user.email!
} else {
return "error"
}
}
}
// MARK: - Table view delegate
extension MealplanViewController: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return mealplanArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let cell = tableView.dequeueReusableCell(withIdentifier: "MealplanTableViewCell", for: indexPath)
let mealplanRow = mealplanArray[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "MealplanTableViewCell") as! MealplanTableViewCell
cell.setMealplan(mealplan: mealplanRow)
return cell
}
}
And here is the cell where I am showing one of the queried values:
class MealplanTableViewCell: UITableViewCell {
#IBOutlet weak var mealplanNameLabel: UILabel!
func setMealplan(mealplan: Mealplan) {
// Link the elements with the data in here
mealplanNameLabel.text = mealplan.mpName
print(mealplan.mpName)
}
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
}
}
And finally, here is the data model:
import Foundation
import Firebase
protocol MealplanSerializable {
init?(dictionary:[String:Any])
}
struct Mealplan {
var mealplanId:String
var mpName:String
]
}
}
extension Mealplan : MealplanSerializable {
init?(dictionary: [String : Any]) {
guard let
let mealplanId = dictionary["mealplanId"] as? String,
let mpName = dictionary["mpName"] as? String,
else { return nil }
self.init(mealplanId: mealplanId, mpName: mpName)
}
}
I am getting just an empty table view with no data in it.

Dictionary search the key and get the value

I got a plist object which contains all the words key=english and value=malay and I assigned in to 2 different arrays which is english and malay. Now I want a textfield where I want to search the english word and print the malay word in the label.
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate {
#IBOutlet weak var selectedLabel: UILabel!
#IBOutlet weak var searchText: UITextField!
#IBOutlet weak var wordTable: UITableView!
var english = [String]()
var malay = [String]()
var words: [String: String] = [:]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
wordTable.dataSource = self
wordTable.delegate = self
searchText.delegate = self
if let path = Bundle.main.path(forResource: "words", ofType: "plist"){
if let plistData = FileManager.default.contents(atPath: path){
do {
let plistObject = try PropertyListSerialization.propertyList(from: plistData, options: PropertyListSerialization.ReadOptions(), format: nil)
words = (plistObject as? [String: String])!
english = [String] (words.keys)
malay = [String] (words.values)
} catch {
print("Error Serialize")
}
} else {
print("Error reading data")
}
} else {
print("Property list")
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return english.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: UITableViewCell!
cell = tableView.dequeueReusableCell(withIdentifier: "tabelCell")
if cell == nil {
cell = UITableViewCell(
style: UITableViewCellStyle.value2,
reuseIdentifier: "tableCell")
print("creating a table cell")
}
cell!.textLabel!.text = english[indexPath.row]
cell!.detailTextLabel?.text = malay[indexPath.row]
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedLabel.text = malay[indexPath.row]
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
// Hide the keyboard
textField.resignFirstResponder()
return true
}
#IBAction func searchBtn(_ sender: UIButton) {
let result = words.filter {$0.key == searchText.text}
if result.count > 0 {
print(result)
selectedLabel.text! = result.values //error
} else {
print("Not found")
}
}
}
the output I expecting is textfield(Bus) which is english word then in the label show me the malay word(Bas)
You have a plist file as a Dictionary. So you can get the dictionary object from the plist file and already answer here.
Make a structure for better data binding.
struct Word {
var english: String
var malay: String
}
Then declare an array of words globally in your ViewController.
var words: [Word] = [] // An empty array
In viewDidLoad: fetch data from plist file.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
wordTable.dataSource = self
wordTable.delegate = self
searchText.delegate = self
if let path = Bundle.main.path(forResource: "words", ofType: "plist") {
if let plistData = FileManager.default.contents(atPath: path){
do {
guard let plistObject = try PropertyListSerialization.propertyList(from: plistData, options: [], format: nil) as? [String: String] else {
// Plist is not [String: String]
return
}
// Here you need to change the code. Converting the dictionary into struct array
var words: [Word] = plistObject.map {Word(english: $0.key, malay: $0.value)}
/// Then sort by english word if needed
words.sorted {$0.english < $1.english}
} catch {
print("Error Serialize")
}
} else {
print("Error reading data")
}
} else {
print("Property list")
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return words.count
}
Update your cell data as well.
cell!.textLabel!.text = words[indexPath.row].english
cell!.detailTextLabel?.text = words[indexPath.row].malay
And your button action with minor modification:
#IBAction func searchBtn(_ sender: UIButton) {
let result = words.filter {$0.english == searchedText}
if let word = result.first {
selectedLabel.text = word.malay
} else {
selectedLabel.text = "" // No data found
}
}
You can replace $0.english == searchedText with {$0.english.contains(searchedText)} if you want to filter with contains, But in that case you might get the multiple result. I assume that in your case you need it as a translator so use ==.
Why don't you search in your plist object? I think it is simpler
#IBAction func searchBtn(_ sender: UIButton) {
guard let words = plistObject as? [String: String], let key = searchText.text else { return }
selectedLabel.text = words[key] ?? ""
}
Something like this.

How to access a property of a tableview cell from viewcontroller?

Please see screenshot. There is a repliesTableView, replyTextField and replyButtonin ViewController. repliesTableView cell is called ReplyCell. In ReplyCell there is a commentTableView to list all comments for that reply and a textfField, a commentButton to add new comments.
I have problem when add new replies and new comments. I guess I need to make comments array in ReplyCell empty when I click the Reply button. How can I make this happen? I have no idea how to access comments arrayfrom the root ViewController.
Exact problems: fter clicking commentButton, all comments in every cell doubled. After clicking replyButton, comments went to wrong cell.
Code:
import UIKit
import Firebase
class TopicForumVC: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
#IBOutlet weak var topicNameLabel: UILabel!
#IBOutlet weak var replyNumberLabel: UILabel!
#IBOutlet weak var repliesTableView: UITableView!
#IBOutlet weak var replyTextField: UITextField!
var topicName:String?
var firstKey:String?
var secondKey:String?
var replies = [String]()
var replyButtonTapped = false
override func viewDidLoad() {
super.viewDidLoad()
repliesTableView.delegate = self
repliesTableView.dataSource = self
replyTextField.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
topicNameLabel.text = self.topicName
loadReplies()
}
func loadReplies() {
self.replies = []
DataService.ds.Categories_Base.child(self.firstKey!).child("Topics").observe(.value, with:{(snapshot) in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots {
if let topicDict = snap.value as? Dictionary<String,AnyObject> {
if let topic = topicDict["text"] as? String {
if topic == self.topicName {
self.secondKey = snap.key
UserDefaults.standard.setValue(snap.key, forKey: Key_SecondKey)
if let replyDict = topicDict["replies"] as? Dictionary<String,AnyObject> {
for eachDict in replyDict {
if let textDict = eachDict.value as? Dictionary<String,AnyObject> {
if let reply = textDict["text"] as? String {
self.replies.append(reply)
self.replyNumberLabel.text = String(self.replies.count)
}
}
}
}
}
}
}
}
self.repliesTableView.reloadData()
}
})
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return replies.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "ReplyCell") as? ReplyCell {
let reply = replies[indexPath.row]
cell.configureReplyCell(reply: reply)
return cell
} else {
return UITableViewCell()
}
}
#IBAction func replyButtonTapped(_ sender: Any) {
replyButtonTapped = true
if let reply = replyTextField.text, reply != "" {
self.replies = []
DataService.ds.Categories_Base.child(self.firstKey!).child("Topics").child(self.secondKey!).child("replies").childByAutoId().child("text").setValue(reply)
self.repliesTableView.reloadData()
let i = replies.count
for n in 0..<i {
let indexPath = IndexPath(row: n, section: 1)
let cell = repliesTableView.cellForRow(at: indexPath) as! ReplyCell
cell.comments = []
cell.repliesToReplyTableView.reloadData()
}
self.replyTextField.text = ""
self.replyButtonTapped = false
}
}
}
import UIKit
import Firebase
class ReplyCell: UITableViewCell,UITableViewDataSource,UITableViewDelegate, UITextFieldDelegate {
#IBOutlet weak var replyTextView: UITextView!
#IBOutlet weak var repliesToReplyTableView: UITableView!
#IBOutlet weak var commentTextField: UITextField!
var reply:String?
var comments = [String]()
var replyKey:String?
override func awakeFromNib() {
super.awakeFromNib()
self.comments = []
repliesToReplyTableView.delegate = self
repliesToReplyTableView.dataSource = self
commentTextField.delegate = self
loadComments()
}
func configureReplyCell(reply:String) {
self.reply = reply
self.replyTextView.text = self.reply
}
func loadComments() {
self.comments = []
if let firstKey = UserDefaults.standard.value(forKey: Key_FirstKey) as? String, let secondKey = UserDefaults.standard.value(forKey: Key_SecondKey) as? String {
DataService.ds.Categories_Base.child(firstKey).child("Topics").child(secondKey).child("replies").observe(.value, with:{(snapshot) in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots {
if let replyDict = snap.value as? Dictionary<String,AnyObject> {
if let reply = replyDict["text"] as? String {
if reply == self.reply {
self.replyKey = snap.key
DataService.ds.Categories_Base.child(firstKey).child("Topics").child(secondKey).child("replies").child(snap.key).child("comments").observe(.value, with: { (commentSnapshot) in
if let commentSnapshots = commentSnapshot.children.allObjects as? [FIRDataSnapshot] {
for commentSnap in commentSnapshots {
if let commentDict = commentSnap.value as? Dictionary<String,AnyObject> {
if let comment = commentDict["text"] as? String {
self.comments.append(comment)
}
}
}
}
self.repliesToReplyTableView.reloadData()
})
}
}
}
}
}
})
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return comments.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let commentCell = tableView.dequeueReusableCell(withIdentifier:"CommentCell")
commentCell?.textLabel?.text = comments[indexPath.row]
return commentCell!
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
#IBAction func commentBtnPressed(_ sender: Any) {
if let comment = commentTextField.text, comment != "" {
self.comments = []
if let firstKey = UserDefaults.standard.value(forKey: Key_FirstKey) as? String, let secondKey = UserDefaults.standard.value(forKey: Key_SecondKey) as? String {
DataService.ds.Categories_Base.child(firstKey).child("Topics").child(secondKey).child("replies").child(self.replyKey!).child("comments").childByAutoId().child("text").setValue(comment)
if let myViewController = parentViewController as? TopicForumVC {
// myViewController.repliesTableView.reloadData()
myViewController.replies = []
}
self.repliesToReplyTableView.reloadData()
self.commentTextField.text = ""
self.replyKey = ""
}
}
}
I don't really know the exact circumstances of what you're building but there are two ideas that may offer some guidance.
1) If your table is displaying content from a data source then you will likely have some kind of reference. E.g. when loading the cells (in this case CustomCell) you'll do something like get the index of the cell and get the same index from the data, and put that data in the cells content. If that's the case, all you have to do on the button click is use tableview.cellForRowAtIndexPath with your sender object, and then remove the array from the data source, e.g. tableDataSource[index] = nil and reload the tableView.
2) If you have a stored property on the CustomCell that you've add specifically for storing this array, then you'd cast the sender object to CustomCell and remove the property, as in Kim's answer.
Hope this helps, but without more information it's kind of hard to tell.
let cell = tableview.cellForRowAtIndexPath(...) as? CustomCell
if cell != nil {
let arr = cell.array
}
BTW: I would re-think storing your array in the cell..