Swift 3: index out of range error - ios

I'm new to iOS. Doing project by watching tutorial which is written using Swift 2. It works when author runs app but not in my case.
ViewController:
var books = [[String: AnyObject]]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
searchBar.delegate = self
}
And the extension
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "BookCell", for: indexPath)
// error on this line
if let volumeInfo = self.books[indexPath.row]["volumeInfo"] as? [String: AnyObject] {
cell.textLabel?.text = volumeInfo["title"] as? String
cell.detailTextLabel?.text = volumeInfo["subtitle"] as? String
}
return cell
}
}
The console output:
Please, help me to identify what is the cause.

You have to return books.count from numberOfRowsInSection, never "hard-code" that value if it's related to the data source array.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return books.count
}
PS: In Swift 3 you should use [String:Any] rather than [String:AnyObject]

I'm assuming a lot about your project but if you only have one section in your tableView, which is the default, you shouldn't use indexPath.section for your books dictionary. You should use indexPath.row as seen below
Change -
if let volumeInfo = self.books[indexPath.section]["volumeInfo"] as? [String: AnyObject]
To -
if let volumeInfo = self.books[indexPath.row]["volumeInfo"] as? [String: AnyObject] {

Related

dictionary for tableView datasource

I am trying to use a dictionary for a tableView datasource, I am getting an object back from the database that contains a key and an array of values, so a [String: [String]]
var requestedList = [String]()
var keyArr = [String]()
var requestedDictionary = [String: [String]]()
let tQuery = PFQuery(className: "MyClass")
tQuery.whereKey("username", equalTo: PFUser.current()?.username as Any)
tQuery.selectKeys(["descContent", "header"])
do {
let returnedObjects = try tQuery.findObjects()
for object in returnedObjects {
let header = object["header"] as! String
keyArr.append(header)
if let arr = object["descContent"] as! [String]? {
requestedDictionary[header] = arr
requestedList += arr
}
}
} catch {
}
I can't seem to correspond the values correctly to the rows of the tableView however, I was suggested to use an array to store the values which is what I have done with the keyArr. My problem is how do I access the contents of the keys and the corresponding values in the datasource methods?? This is what I have so far but I haven't been able to link the keys and values accordingly
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return requestedList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "RequestViewCell", for: indexPath) as! RequestViewCell
cell.descLbl.text = "Your ticket has been requested by \(requestedList[indexPath.row])"
cell.refLbl.text = "for: \(keyArr[indexPath.row])"
cell.leftBtn.tag = (indexPath.section * 100) + indexPath.row
cell.leftBtn.addTarget(self, action: #selector(leftClick(sender:)), for: .touchUpInside)
cell.rightBtn.tag = (indexPath.section * 100) + indexPath.row
cell.rightBtn.addTarget(self, action: #selector(rightClick(sender:)), for: .touchUpInside)
return cell
}
You can turn dictionary into tableView representable data this way.
let requestedDictionary:[String: [String]] = [
"Key-1":["Value-1","Value-2","Value-3","Value-4"],
"Key-A":["Value-X","Value-Y","Value-Z"],
"Key-a":["Value-x","Value-y"],
]
lazy var data:[(key:String,values:[String])] = requestedDictionary.compactMap({(key:$0,values:$1)})
func numberOfSections(in tableView: UITableView) -> Int {
data.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data[section].values.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = data[indexPath.section].values[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return data[section].key
}
Hope it helps.

Multiple Custom Cells in UITableView With Firebase Database

I want to create a tableview that has two custom cells in it, with the information being pulled from Firebase Database. The first custom cell displays the dates, and the second custom cell displays the events. When I run the app, the tableview is only returning the first custom cell, which is the dates. What would be causing this?
import UIKit
import Firebase
class augustController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var augustController: UITableView!
var ref = DatabaseReference()
var date = [String]()
var event = [String]()
var databaseHandle:DatabaseHandle = 0
var databaseHandle2:DatabaseHandle = 0
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
databaseHandle = ref.child("Events").child("August").child("dates").observe(.childAdded) { (snapshot) in
let post = snapshot.value as? String
if let actualPost = post {
self.date.append(actualPost)
}
}
databaseHandle2 = ref.child("Events").child("August").child("events").observe(.childAdded) { (snapshot) in
let post2 = snapshot.value as? String
if let actualPost2 = post2 {
self.event.append(actualPost2)
self.augustController.reloadData()
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return date.count
}
func tableView2(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return event.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let augustDate = tableView.dequeueReusableCell(withIdentifier: "augustDate") as! dateCell
augustDate.date.text = date[indexPath.row]
return(augustDate)
}
func tableView2(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let augustEvents = tableView.dequeueReusableCell(withIdentifier: "augustEvents") as! eventCell
augustEvents.even.text = event[indexPath.row]
return(augustEvents)
}
}
You implement cellForRow twice , you have to
var itemsArr = [Item]()
//
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return itemsArr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = itemsArr[indexPath.row]
if item.isDate {
let augustDate = tableView.dequeueReusableCell(withIdentifier: "augustDate") as! dateCell
augustDate.date.text = item.content
return augustDate
}
else {
let augustEvents = tableView.dequeueReusableCell(withIdentifier: "augustEvents") as! eventCell
augustEvents.even.text = item.content
return augustEvents
}
}
//
then make it one array and append items of this struct type
struct Item {
var isDate:Bool
var content:String
}

Firebase retrieve multiple data using UITableViewCell (Swift 3)

I'm in trouble with retrieving data from Firebase.
My JSON looks like this:
-user
-contacts
-autoID Value1
-contactName : A
-autoID Value2
-contactName : B
-autoID Value3
-contactName : C
(more contactName is coming, in same structure.)
But I have no idea how to get ALL contactName's Data(A,B,C....) then print in UITableView in Swift3.0
I want to make a result like this :
#In the TableView#
A
B
C
...
But My result is different, Maybe some problem in TableViewCell.swift... but I'm not sure
ViewController.swift :
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : MoneyTableViewCell = tableView.dequeueReusableCell(withIdentifier: "MoneyTableViewCell", for: indexPath) as! MoneyTableViewCell
cell.eachCell()
cell.layoutIfNeeded()
return cell
}
MoneyTableViewCell.swift
func eachCell() {
dbRef = Database.database().reference()
self.dbRef.child("user/contacts").observe(.value, with: {(snapshot) in
if let result = snapshot.children.allObjects as? [DataSnapshot] {
for child in result {
let receiverNameDB = child.childSnapshot(forPath: "contactName").value! as! String
self.nameArray.append(receiverNameDB)
self.nameLabel.text = receiverNameDB
print(self.nameArray)
continue
}
}
})
}
Can Anybody help me please ;)
Update - I solved this problem! Here is my edited code...
Thanks to #Abdul91
The problem was.. I had to get the data from DB first, then put them in the array. After that get each data for tableView.
My edited code will tell you more.
ViewController.swift:
var nameArray:[String] = []
override func viewDidLoad() {
super.viewDidLoad()
getNameDB{ value in
self.nameArray = value
self.tableView.reloadData()
}
tableView.register(UINib(nibName: "MoneyTableViewCell", bundle: nil), forCellReuseIdentifier: "MoneyTableViewCell")
}
//completion is for getting DB value outside of block..
func getNameDB(completion: #escaping (_ value: [String]) -> Void) {
self.dbRef.child("user/contacts").observe(.value, with: {(snapshot) in
if let result = snapshot.children.allObjects as? [DataSnapshot] {
for child in result {
let receiverNameDB = child.childSnapshot(forPath: "contactName").value! as! String
self.nameArray.append(receiverNameDB)
}
completion(self.nameArray)
}
})
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.nameArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : MoneyTableViewCell = tableView.dequeueReusableCell(withIdentifier: "MoneyTableViewCell", for: indexPath) as! MoneyTableViewCell
cell.nameLabel.text = nameArray[(indexPath as NSIndexPath).row]
cell.layoutIfNeeded()
return cell
}

How to populate table view with large data

I'm trying to populate a table view with a large data. However, my tableview can only display 20 string objects. How do I simply display the rest of the data and update the table view each time the user scrolls to the end?
var people = [People]()
let configureSession = URLSessionConfiguration.default
let session = URLSession(configuration: configure)
//Setup the Api Key...
let apiKey = "https://congress.api.sunlightfoundation.com/legislators?apikey=(//Api Key here...)"
if error != nil{
print("ERROR: \(error?.localizedDescription)")
return
} else if let jsonData = data {
do{
let parsedJSON = try JSONSerialization.jsonObject(with: jsonData, options: []) as! [String: AnyObject]
guard let results = parsedJSON["results"] as? [[String: AnyObject]] else {return}
for result in results{
let name = myClass()
name.firstName = result["first_name"] as! String
self.people.append(name)
}
DispatchQueue.main.async {
//Reload the data
self.table.reloadData()
}
} catch let error as NSError{
print(error)
}
}
}).resume()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return people.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
cell.textLabel?.text = people[indexPath.row] //Only displays 20... Need to display more!
return cell!
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
//Code to display the rest of the data goes here?
}
Make sure you return the correct value in numberOfRowsInSection method:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myLargeData.count
}
With provided myLargeData array there should be 30 rows in your tableView
You can use number of rows in section delegate of table view delegate to handle the more data in array.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return yourarray.count
}

How do I parse JSON data into a table view?

all Im a noob at swift lets just set the record,
Im trying to parse JSON data from an external php file and display the data in a tableview, the data is from a php file hosted elsewhere, its values are
{
'id' : '1',
'username' : 'test',
'password' : 'test',
'email' : 'test#test.com'
}
My code is as follows
class ForuthTableViewController: UITableViewController {
#IBOutlet var userTable: UITableView!
#IBOutlet weak var refreshButton: UIBarButtonItem!
var values:NSArray = []
override func viewDidLoad() {
super.viewDidLoad()
getUsers()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func refreshPressed(_ sender: UIBarButtonItem) {
getUsers()
}
func getUsers(){
let url = NSURL(string: "http://xxx.xxxxxx.xx/get.php")
let userdata = NSData(contentsOf: url! as URL)
do {
values = try JSONSerialization.jsonObject(with: userdata as! Data, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSArray
print("Parse success")
} catch {
print("Parse error")
return
}
print(values)
//userTable.reloadData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return values.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let cell = tableView.dequeueReusableCellWithIdentifier("userCell", forIndexPath: indexPath) as! UITableViewCell
let maindata = values[indexPath.row]
cell.username.text = maindata["username"] as? String
cell.password.text = maindata["password"] as? String
cell.email.text = maindata["email"] as? String
return cell
}
}
I followed a tutorial on youtube of creating a login / register app but it was from an old version of xcode and when I copy the code across theres errors everywhere so not sure where to start,
Tried looking for a simple JSON tutorial but there are so many different ways to approach JSON I'm not sure which is the right one.
Any ideas would be appreciated, or perhaps a simple fix.
got an error "Errors thrown from here not handled" : Catch the error.
do {
values = try JSONSerialization.jsonObject(with: userdata as! Data, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSArray
print("Parse success")
} catch {
print("Parse error")
print("Error: \(error)")
}
error "IndexPath not resolved" :
You are using method numberOfRowsInSection instead of cellForRowAtIndexPath.
Also override both methods.
Edit 1:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
should be:
override func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return values.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "userCell", for: indexPath) as! UITableViewCell
let maindata = values[indexPath.row]
cell.username.text = maindata["username"] as? String
cell.password.text = maindata["password"] as? String
cell.email.text = maindata["email"] as? String
return cell
}

Resources