Swift Fatal Error index out of range while using searchbar in tableview - swift

i try to load an json data with tableView and i wanna add search data using uiSearchBar, data is search well but when i try to hit 'x' button on the searchBar or when i try to backspace the input it crash...Any help guys? or should i change my search method? thanks for the help. I'm still new in swift so if theres a better method to search please let me know:)
struct ProjectSumName: Decodable {
let id : Int
let name : String
enum CodingKeys : String, CodingKey {
case id = "id"
case name = "name"
}
}
class ProjectSumController: UIViewController {
#IBOutlet weak var SearchBar: UISearchBar!
#IBOutlet weak var ProjectSumTableView: UITableView!
var projectSum = [ProjectSumName]()
var filterProject : [ProjectSumName] = [ProjectSumName]()
var isSearch : Bool = false
override func viewDidLoad() {
super.viewDidLoad()
SearchBar.delegate = self
Loading()
let jsonUrl = "http://\(GlobalVariable.ip):7000/api/projectApi?UserId=\(GlobalVariable.UserIdProjectSum)"
guard let url = URL(string: jsonUrl) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
do{
let projectsum = try JSONDecoder().decode([ProjectSumName].self, from: data)
self.projectSum = projectsum
self.filterProject = projectsum
DispatchQueue.main.async {
SVProgressHUD.dismiss()
self.ProjectSumTableView.reloadData()
}
}catch {
print(error)
}
}.resume()
}
}
extension ProjectSumController : UISearchBarDelegate, UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isSearch{
return filterProject.count
}else{
return projectSum.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let proc = projectSum[indexPath.row]
let proc1 = filterProject[indexPath.row]
guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as? ProjectSumTableCell else {return UITableViewCell()}
if isSearch{
cell.NameLbl.text = proc1.name
}else{
cell.NameLbl.text = proc.name
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let sum = projectSum[indexPath.row]
let sum1 = filterProject[indexPath.row]
if isSearch{
performSegue(withIdentifier: "Segue", sender: sum1)
let projectIDs = sum1.id
GlobalVariable.ProjectId = String(projectIDs)
}else{
performSegue(withIdentifier: "Segue", sender: sum)
let projectID = sum.id
GlobalVariable.ProjectId = String(projectID)
}
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.isEmpty{
self.isSearch = false;
self.ProjectSumTableView.reloadData()
} else {
self.filterProject = self.projectSum.filter({ (ProjectSumName) -> Bool in
let tmp : NSString = NSString.init(string: ProjectSumName.name)
let range = tmp.range(of: searchText, options: NSString.CompareOptions.caseInsensitive)
return range.location != NSNotFound && range.location == 0
})
if(self.filterProject.count == 0){
self.isSearch = false;
}else{
self.isSearch = true;
}
self.ProjectSumTableView.reloadData()
}
}
"Fatal error: Index out of range
2019-06-27 09:43:46.167472+0700 ImmobiTracker[806:30114] Fatal error: Index out of range"
that crash come up everything i try to clear my searchbar...so when i try to type the first time to search its filtering the data, but when i try to clear the search bar it pop crash

There are two places where you are getting this error.
in cellForRowAt:
let proc = projectSum[indexPath.row]
let proc1 = filterProject[indexPath.row]
didSelectRowAt
let sum = projectSum[indexPath.row]
let sum1 = filterProject[indexPath.row]
Why:
You are trying to get an element from filterProject without using isSearch i.e. filterPoject array is empty. When isSearch is false then the error occurs because you are trying to get the element from the empty array.
How to Solve this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let sum: ProjectSumName
if isSearch{
sum = filterProject[indexPath.row]
}else{
sum = projectSum[indexPath.row]
}
GlobalVariable.ProjectId = String(sum.id)
performSegue(withIdentifier: "Segue", sender: sum)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as? ProjectSumTableCell else {return UITableViewCell()}
let proc: ProjectSumName
if isSearch{
proc = filterProject[indexPath.row]
}else{
proc = projectSum[indexPath.row]
}
cell.NameLbl.text = proc.name
return cell
}

Related

Issue with TableView and Firebase implementation (Swift)

I want to connect my TableView to what I query from Firestore. The query works, but I can't get the TableView to show the content. Right now its just a blank tableView. The TableViewCell file also has no issues, since it worked before without the firebase implementation (The Cell is registered correctly).
I suspect that the issue is in cellForRowAt and tried played around in there, but couldn't get anything to work.
Can you find the issue?
import UIKit
import Firebase
class popularViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet var table: UITableView!
var texttt = [TextPost]()
override func viewDidLoad() {
super.viewDidLoad()
gettingPosts()
table.register(textTableViewCell.nib(), forCellReuseIdentifier: textTableViewCell.identifier)
table.delegate = self
table.dataSource = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
gettingPosts()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let alle = models.count + texttt.count
return alle
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: textTableViewCell.identifier, for: indexPath) as! textTableViewCell
cell.configure(with: texttt[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 300
}
func gettingPosts(){
let db = Firestore.firestore()
let postsRef = db.collection("posts")
postsRef.addSnapshotListener { (querySnapshot, error) in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added){
let data = diff.document.data()
let Name = data["username"] as! String
let text = data["description"] as! String
let likes = data["likes"] as! Int
let typ = data["postType"] as! Int
let pfp = data["profileImage"] as! String
let uid = data["uid"] as! String
let pic = data["picture"]
let time = data["time"] as! String
if typ == 0{ // Text post
let dasDing = TextPost(numberOfComments: 0, username: Name, timestampName: time, userImageName: pfp, textName: text)
self.texttt.append(dasDing)
}
}
}
}
}
}
struct TextPost {
let numberOfComments: Int
let username: String
let timestampName: String
let userImageName: String
let textName: String
}
You need to reload data once you get data from firebase
func gettingPosts(){
let db = Firestore.firestore()
let postsRef = db.collection("posts")
postsRef.addSnapshotListener { (querySnapshot, error) in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added){
let data = diff.document.data()
let Name = data["username"] as! String
let text = data["description"] as! String
let likes = data["likes"] as! Int
let typ = data["postType"] as! Int
let pfp = data["profileImage"] as! String
let uid = data["uid"] as! String
let pic = data["picture"]
let time = data["time"] as! String
if typ == 0{ // Text post
let dasDing = TextPost(numberOfComments: 0, username: Name, timestampName: time, userImageName: pfp, textName: text)
self.texttt.append(dasDing)
}
}
}
DispatchQueue.main.async {
tableView.reloadData()
}
}
}

Swift - Search in UITableView [duplicate]

This question already has answers here:
Searchbar filtering issue
(3 answers)
Closed 3 years ago.
I'm trying to be able to search my Firebase data using a UISearchBar in my app & I am stuck. I am successfully receiving data from Firebase in a table view. I have a memories-writing app that the user can create memories (that are shown in a tableview from the firebase). memories has a title, description, a pic and a date and I want it to be able to search memories by the title.
I have a code here that doesn't work for some reason... il'l be glad if you could help me find out what's wrong in the code or find a replacement for this code :)
MemoryTitles class:
class MemoryTitles {
var title : String
init(withTitle: String) {
self.title = withTitle
}
}
MemoryViewController:
class MemoryViewController: UIViewController,UITableViewDelegate,UITableViewDataSource
// the filtered memories array for the search bar
var memoriesTitlesArr: [String] = []
var filteredDataa: [String] = []
// connections from storyboard to the code
#IBOutlet weak var tbl: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
// an array of memories
var memories : [Memory] = []
var ref = Database.database().reference()
let sref = Storage.storage().reference()
var lastIndex : Int = 0
var strMode : String = ""
// TableView functions
// Return the number of rows in section
// section - an index number identifying a section in tableView.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching {
return filteredDataa.count
} else {
return memories.count
}
// return memories.count
}
// Return Cell for row function : an object inheriting from UITableViewCell
// indexPath - an index path locating a row in tableView.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "iden")
if searching {
cell?.textLabel?.text = filteredDataa[indexPath.row]
} else {
var cell : UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: "iden", for: indexPath)
if cell == nil
{
cell = UITableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: "iden")
}
let temp = memories[indexPath.row]
cell?.textLabel?.text = temp.title
cell?.imageView?.image = temp.image
return cell!
}
return cell!
}
// Can edit row : asks the data source to verify that the given row is editable.
// indexPath - an index path locating a row in tableView.
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true // true if the row indicated by indexPath is editable; otherwise, false.
}
// Asks the data source to commit the insertion or deletion of a specified row.
// indexPath - an index path locating a row in tableView.
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete // editingStyle - the cell editing style corresponding to a insertion or deletion requested for the row specified by indexPath.
{
let temp = self.memories[indexPath.row]
self.memories.remove(at: indexPath.row)
self.ref.child("MEmories/\(temp.key)").removeValue()
tableView.deleteRows(at: [indexPath as IndexPath], with: .fade)
}
}
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
let rightButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(MemoryViewController.barButtonItemClicked(_:)))
self.navigationItem.setRightBarButton(rightButton, animated: true)
// Do any additional setup after loading the view.
self.loadMemories()
self.tbl.delegate = self
self.tbl.dataSource = self
}
// click on bar-Button function
#objc func barButtonItemClicked(_ sender:UIBarButtonItem)
{
print("+ clicked") // writes "+ clicked"
let addMemoryViewController = self.storyboard?.instantiateViewController(withIdentifier: "AddMemoryViewController") as! AddMemoryViewController
self.strMode = "newMemory"
self.navigationController?.pushViewController(addMemoryViewController, animated: true)
}
// Reading from NSUserDefault (A class that provides simple storage of different data types solution.)
func readFromNSUserDefault()-> Memory?
{
let d : UserDefaults = UserDefaults.standard
let strTitle = d.object(forKey: "title") as? String
let strBody = d.object(forKey: "body") as? String
let strImageRef = d.object(forKey: "imageRef") as? String
let uid = d.object(forKey: "uid") as? String
let imageData = d.object(forKey: "imageData") as? Data
let key = d.object(forKey: "key") as? String
let date = d.object(forKey: "date") as? NSNumber
let m = Memory(title: strTitle!, body: strBody!, key: key!, uid: uid!, imageRef: strImageRef!, date: date!) // A variable from type memory
m.image = UIImage(data: imageData!)
m.key = key!
return m
}
override func viewDidAppear(_ animated: Bool) {
let d = UserDefaults.standard
let newMemory = readFromNSUserDefault()
let userAdded = d.bool(forKey: "userAdded") //key new user = true
let userEdited = d.bool(forKey: "userEdited")//key user edited = true
if self.strMode == "newMemory" && userAdded
{
self.memories.append(newMemory!)
self.tbl.reloadData()
}
else if self.strMode == "edit" && userEdited
{
memories[lastIndex] = newMemory!
self.tbl.reloadData()
}
d.set(false, forKey: "userAdded")
d.set(false, forKey: "userEdited")
d.synchronize()
self.strMode = " "
}
// loading the memories from the Database
func loadMemories()
{
let UID = Auth.auth().currentUser!.uid
self.ref.child("MEmories").queryOrdered(byChild: "uid").queryEqual(toValue: UID).observeSingleEvent(of: .value, with: {
snapShot in
if let dict = snapShot.value as? NSDictionary
{
for d in (dict as? Dictionary<String,AnyObject>)!
{
let title = d.value["title"] as?String
let body = d.value["body"] as? String
let uid = d.value["uid"] as? String
let imageRef = d.value["imageRef"] as? String
let date = d.value["date"] as? NSNumber
let m = Memory(title: title!, body: body!, uid: uid!,imageRef:imageRef!, date: date!)
m.key = d.key
let tempImageRef = self.sref.child(m.imageRef)
tempImageRef.getData(maxSize: 500*1024*1024, completion: {(data,error) in
if error == nil
{
if let imageData = data
{
m.image = UIImage(data: imageData)
self.memories.append(m)
self.tbl.reloadData()
}
}
})
self.memoriesTitlesArr.append(title!)
}
}//end of if
})
}
// Notifies the view controller that a segue is about to be performed.
// segue - The segue object containing information about the view controllers involved in the segue.
// senderThe object that initiated the segue.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let identifier = segue.identifier
{
if identifier == "goToEdit"
{
let indexPath = self.tbl.indexPathForSelectedRow
let addMemoryViewController = segue.destination as! AddMemoryViewController
self.strMode = "edit"
self.lastIndex = (indexPath?.row)!
addMemoryViewController.mode = self.strMode
addMemoryViewController.current = memories[(indexPath?.row)!]
}
}
}
var searching = false
}
extension MemoryViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filteredDataa = memoriesTitlesArr.filter({ $0.prefix(searchText.count)==searchText.lowercased()})
searching = true
tbl.reloadData()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searching = false
searchBar.text = ""
tbl.reloadData()
}
}
Here is how you can get that working.
1. First of all, there is no need for searching to maintain the state of searching and not searching.
2. Secondly, use filteredData as the dataSource for tableView instead of memories. filteredData will initially contain all the objects from memories, i.e.
var memories : [Memory] = []
lazy var filteredData = self.memories
The UITableViewDataSource methods will be like,
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.filteredData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "iden") {
cell.textLabel?.text = self.filteredData[indexPath.row].title
return cell
}
return UITableViewCell()
}
Now, while searching update the filteredData with filtered memories using the relevant condition, i.e.
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
self.filteredData = self.memories.filter({ $0.title.hasPrefix(searchText) })
//change the condition as per your requirement......
self.tbl.reloadData()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchBar.text = nil
self.filteredData = self.memories
self.tbl.reloadData()
}
When cancelled, refill the filteredData with the whole memories data.

Index out of range custom cell TableViewController

I'm trying to populate 3 custom cells into a TableViewController.
but I always get index out of range error. I`m not sure whats wrong with my code. anyone can help me, I'm newbie in swift.
but when i use 0 for numberOfRowsInSection return, the output is the first cell.
here's my code :
class testResize: UITableViewController {
#objc var comments = [AnyObject]()
#objc var images = [UIImage]()
var getImg = [String]()
override func viewDidLoad() {
super.viewDidLoad()
loadPosts()
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let getCom = comments[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testResizeHeadCell.self), for: indexPath) as! testResizeHeadCell
let user = getCom["nickname"] as! String
let ava = getCom["ava"] as! String
if ava != "" {
let resource = ImageResource(downloadURL: URL(string: ava)!, cacheKey: ava)
cell.avaImg.kf.setImage(with: resource)
}
cell.username.text = user
return cell
}else if indexPath.row == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testResizeCell.self), for: indexPath) as! testResizeCell
cell.setCustomImage(image: images[indexPath.row])
return cell
}else {
let getCom = comments[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testRezieTextCell.self), for: indexPath) as! testRezieTextCell
let text = getCom["text"] as! String
cell.explaination.text = text
return cell
}
}
here is my load function :
#objc func loadPosts() {
let uuid = "959D1073"
let url = URL(string: "some/url.php")!
self.tableView.reloadData()
var request = URLRequest(url: url)
request.httpMethod = "POST"
let body = "uuid=\(uuid)"
//print(body)
request.httpBody = body.data(using: String.Encoding.utf8)
URLSession.shared.dataTask(with: request) { data, response, error in
DispatchQueue.main.async(execute: {
if error == nil {
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
self.comments.removeAll(keepingCapacity: false)
self.images.removeAll(keepingCapacity: false)
self.tableView.reloadData()
guard let parseJSON = json else {
print("Error While Parsing")
return
}
guard let posts = parseJSON["posts"] as? [AnyObject] else {
print("Error while parseJSONing")
return
}
self.comments = posts.reversed()
print(self.comments)
for i in 0 ..< self.comments.count {
let path = self.comments[i]["path"] as? String
self.getImg = [path!]
if !path!.isEmpty {
let url = NSURL(string: path!)!
let imageData = try? Data(contentsOf: url as URL)
let image = UIImage(data: imageData! as Data)!
self.images.append(image)
} else {
let image = UIImage()
self.images.append(image)
}
}
self.tableView.reloadData()
//print(posts)
} catch {
print(error)
}
}else{
print(error!)
}
})
}.resume()
}
i think you have a single comment and 3 cell type and when you use indexPath.row happen some thing like this :
for example :
comments = {[{nickname : "mahdi" , ava : "url"} ]}
if indexPath.row == 0 {
let getCom = comments[0]
let user = getCom["nickname"] as! String
let ava = getCom["ava"] as! String
}else if indexPath.row == 1 {
images[1]
}else {
let getCom = comments[2]
let text = getCom["text"] as! String
}
but you have just one comment and when you call comments[1] or commens [2] , you get index out of range error
please try this code :
class testResize:UITableViewController {
#objc var comments = [AnyObject]()
#objc var images = [UIImage]()
var getImg = [String]()
override func viewDidLoad() {
super.viewDidLoad()
loadPosts()
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (self.comments.count == 0 ? 0 : self.comments.count + 2)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let getCom = comments[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testResizeHeadCell.self), for: indexPath) as! testResizeHeadCell
let user = getCom["nickname"] as! String
let ava = getCom["ava"] as! String
if ava != "" {
let resource = ImageResource(downloadURL: URL(string: ava)!, cacheKey: ava)
cell.avaImg.kf.setImage(with: resource)
}
cell.username.text = user
return cell
}else if indexPath.row == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testResizeCell.self), for: indexPath) as! testResizeCell
cell.setCustomImage(image: images[indexPath.row - 1])
return cell
}else {
let getCom = comments[indexPath.row - 2]
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: testRezieTextCell.self), for: indexPath) as! testRezieTextCell
let text = getCom["text"] as! String
cell.explaination.text = text
return cell
}
}
and change your numberOfRowInSection :
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return (self.comments.count == 0 ? 0 : self.comments.count + 2)
}
I am assuming that you load your posts asynchronously.
But you do not check if there are actually enough elements in the array. You should check if there are actually enough elements in the array before you access it with a fixed index.
Additionally, you should change your numberOfRows method to the following:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return self.comments.count
}
After you have loaded your posts, you can then call
self.tableView.reloadData()

I want to get firestore data in dequeueReusableCell

I rewrote all the text and now I got the code I wanted to realize.
It can not be displayed on the tableCell, and the layout also collapses. I am sorry that the code and the body I wrote are not explained enough.
guard let userID = Auth.auth (). currentUser? .uid I want to always acquire userID with else {return}.
// guard let docSnapshot = querySnapshot, document.exists else {return}
Since an error occurs, it is commented out.
Within viewidLoad of UIViewController
var profDict: [ProfDic] = [] is in the UIViewController.
profUIView is being added to UIViewController.
func getFirebaseData() {
db = Firestore.firestore()
guard let userID = Auth.auth().currentUser?.uid else {return}
let ref = db.collection("users").document(userID)
ref.getDocument{ (document, error) in
if let document = document {
// guard let docSnapshot = querySnapshot, document.exists else {return}
if let prof = ProfDic(dictionary: document.data()!) {
self.profDict.append(prof)
print("Document data \(document.data())")
}
}else{
print("Document does not exist")
}
self.profUIView.tableView1.reloadData()
}
}
tableView1 has been added to ProfUIView.
class ProfUIView: UIView, UITableViewDelegate, UITableViewDataSource {
//omission...
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .blue
addSubview(tableView1)
tableView1.anchor(top: //omission...
sections = [
Section(type: .prof_Sec, items: [.prof]),
Section(type: .link_Sec, items: [.link]),
Section(type: .hoge_Sec, items: [.hoge0])
]
tableView1.register(TableCell0.self, forCellReuseIdentifier: TableCellId0)
tableView1.register(TableCell3.self, forCellReuseIdentifier: TableCellId3)
tableView1.register(TableCell5.self, forCellReuseIdentifier: TableCellId5)
tableView1.delegate = self
tableView1.dataSource = self
}
var tableView1:UITableView = {
let table = UITableView()
table.backgroundColor = .gray
return table
}()
//omission
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (baseVC?.profDict.count)!//sections[section].items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch sections[indexPath.section].items[indexPath.row] {
case .prof:
let cell0 = tableView.dequeueReusableCell(withIdentifier: TableCellId0, for: indexPath) as? TableCell0
cell0?.nameLabel.text = baseVC?.profDict[indexPath.row].userName
return cell0!
}
//omission...
}
}
Additional notes
import Foundation
import FirebaseFirestore
struct ProfDic {
var userName :String
var dictionary:[String:Any] {
return
["userName" : userName
]
}
}
extension ProfDic {
init?(dictionary:[String:Any]) {
guard let userName = dictionary["userName"] as? String
else {return nil}
self.init(userName: userName as String)
}
}
enter image description here
First create an empty array of ProfDic elements:
var profDict: [ProfDic] = []
Then create a function to load your Firebase Data:
func getFirebaseData() {
db = Firestore.firestore()
let userRef = db.collection("users").getDocuments() {
[weak self] (querySnapshot, error) in
for document in querySnapshot!.documents {
guard let docSnapshot = docSnapshot, docSnapshot.exists else {return}
if let prof = ProfDic(dictionary: docSnapshot.data()!) {
profDict.append(prof)
}
}
tableView.reloadData()
}
}
Call this function in viewDidLoad or viewDidAppear.
Then in tableView cellForRowAt you access your data like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch sections[indexPath.section].items[indexPath.row] {
case .prof:
let cell = tableView.dequeueReusableCell(withIdentifier: TableCellId, for: indexPath) as? TableCell
cell?.nameLabel.text = profDict[indexPath.row].userName
return cell!
}
}
EDIT:
Also in numberOfRowsInSection:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return profDict.count
}

How to use swiftyJson to get arrayvalue in TableView?

I use swiftyJson to parse data and, but do not know how to parse array.
Here is code.
import UIKit
import Alamofire
import SwiftyJSON
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var json:JSON = JSON.null
var urlSession = URLSession(configuration: .default)
#IBOutlet weak var myTableView: UITableView!
var pokamon = [[String:AnyObject]]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pokamon.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
var dict = pokamon[indexPath.row]
cell.nameLbl.text = dict["name"] as? String
cell.typesLbl.text = dict["types"]?[0] as? String
cell.hpLbl.text = dict["hp"] as? String
cell.subtypeLbl.text = dict["subtype"] as? String
if let image = URL(string: dict["imageUrl"] as! String){
let task = urlSession.downloadTask(with: image) { (url, repsponse, error) in
if error != nil{
print("sorry")
return
}
if let okURL = url{
do{
let downloadImage = UIImage(data: try Data(contentsOf: okURL))
DispatchQueue.main.async {
cell.myImage.image = downloadImage
}
}catch{
print("error")
}
}
}
task.resume()
}
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 224
}
override func viewDidLoad() {
super.viewDidLoad()
myTableView.delegate = self
myTableView.dataSource = self
Alamofire.request("https://api.pokemontcg.io/v1/cards").responseJSON(completionHandler: { response in
if response.result.isSuccess {
let json:JSON = try! JSON(data: response.data!)
let swiftyJsonVar = JSON(response.result.value!)
if let resData = swiftyJsonVar["cards"].arrayObject{
self.pokamon = resData as! [[String:AnyObject]]
}
if self.pokamon.count > 0{
self.myTableView.reloadData()
}
} else {
print("error: \(response.error)")
}
})
}
}
From cellForRowAt in tableView, it shows "Ambiguous use of 'subscript'" with below code, have no idea how to solve it. The rest like "name", "hp", and "subtype" are no problem!
cell.typesLbl.text = dict["types"]?[0] as? String
Could anyone help me with this error?
Thanks!
The compiler must know the type of any subscripted object. Tell the compiler that you expect an array of strings.
cell.typesLbl.text = (dict["types"] as? [String])?.first
In Swift 3+ all JSON values are Any not AnyObject so declare the array
var pokamon = [[String:Any]]()