How to use Firestore Maps and Arrays and apply them to a TableView - uitableview

I have a document in Firestore which has an Array of Maps..... which I am finding difficult to work with. I have setup "Codable Struct's" per the recommended. and I can get a snapshot of the document in question no problem. below.
struct specificTourny : Codable, Identifiable {
#DocumentID var id: String?
var docuID: String
var poolstates: [pool]?
var poolGames: [match]?
var poolDescription: String
var brackDescription: String
}
struct match : Codable, Hashable {
var gameNumber: Int
var date: String
var time: String
var location: String
var fieldNumber: Int
var teamA: String
var teamAuid: String
var teamAscore: Int
var teamB: String
var teamBuid: String
var teamBscore: Int
}
My problem/question is when I use the data from the second Struct, "match", in lets say a tableview where I want to get a count of "match"s or even display that parsed data on a tableview from the second struct is not accessible via dot Notation? I am also not able to get a count of these individual match's either?
like so....
// for updating the cell text with information from my db.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellz = tableView.dequeueReusableCell(withIdentifier: "PoolCell", for: indexPath) as! PoolTableViewCell
cell.TeamName.text = "\(myArray[match.indices].teamA)"
// for getting the total number of matches I have stored in the doc to set my number of rows.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myArray[match].gameNumber.count
I know there must be a simple way to reference the data that is being Parsed! really I am looking for something like this to apply to the struct within the struct..
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myTeamsOnOff", for: indexPath) as! SettingsTableViewCell
cell.teNames.text = "\(myArray[indexPath.row].teamname)"
I have tried a lot of different solutions with no way forward.
adding an image of my db struct

Related

How to fix swift Index Out of Range ( 444 Index out of range) ..? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I have a struct data, I am trying to populate the table view at the specified indices, but if try to use the indexPath.row attribute for data and cell labels, i am having index out of range error everytime..
my struct:
struct TradingPairs: Codable {
var id: Int
var name: String
var baseAsset: String
var quoteAsset: String
}
struct PairByTicker: Codable {
var price: Float
var ask: Float
var askVolume: Float
var bid: Float
var bidVolume: Float
var volume: Float
var time: String
enum CodingKeys: String, CodingKey {
case price = "price"
case ask = "ask"
case askVolume = "askVolume"
case bid = "bid"
case bidVolume = "bidVolume"
case volume = "volume"
case time = "time"
}
}
and struct instance in ViewController:
var tradingPair = [TradingPairs]()
var pairByTicker = [PairByTicker]()
and in My TableView Cell cell for row at dequeue method:
cell.name.text = self.tradingPair[indexPath.row].name
cell.price.text = String(describing: self.pairByTicker[indexPath.row].price)
cell.volume.text = String(describing: self.pairByTicker[indexPath.row].volume)
return cell
--TableView DataSourse:
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.tradingPair.count + [self.pairByTicker].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TradingPairCell") as! TradingPairCell
// for data in self.pairByTicker {
// let pairs = data
// cell.configure(data: pairs)
// }
// for data in self.tradingPair {
// cell.configure(data: data)
// }
cell.name.text = self.tradingPair[indexPath.row].name
cell.price.text = String(describing: self.pairByTicker[indexPath.row].price)
cell.volume.text = String(describing: self.pairByTicker[indexPath.row].volume)
return cell
}
}
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 44
}
}
not sure why i am getting index out of range error.. (
You are returning cell for a section is self.tradingPair.count + [self.pairByTicker].count so if both array have 5 members then tableview aspects 10 rows data in cellForRowAtIndexPath.
But in cellForRowAtIndexPath it will work well for the index up to 4 and the app will get crash as soon as it try to get the object from that array for the index 5.
That is the reason of the crash and it will crash at line self.tradingPair[indexPath.row].name because the array tradingPair has only 5 member (from index 0 to 4) and it try to get the member of 5th index.
what you can do is you need to identify the exact number of rows for the tableview and then return the value for the method numberOfRowsInSection . like below...
Suppose you have the following model.
struct TradingPair {
var name: String
var price: Double
var volume: Double
}
Initialise the array like below...
let tradingPair = [
TradingPair(name: "name1", price: 12, volume: 15)
TradingPair(name: "name2", price: 26, volume: 25),
TradingPair(name: "name3", price: 37, volume: 12),
TradingPair(name: "name4", price: 22, volume: 6)
]
The implementation of the delegate and datasource methods of table view...
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tradingPair.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TradingPairCell") as! TradingPairCell
let obj = tradingPair[indexPath.row]
cell.name.text = obj.name
cell.price.text = String(obj.price)
cell.volume.text = String(obj.volume)
return cell
}
Hope from the above example you get the idea how to implement it.

Sectioning a tableview by dates from a dictionary

I have a dictionary that contains a key/value pair of a Date that contains an array of my custom object Meals grouped together by the same dates.
Meal Object:
class Meal: NSObject, Codable {
var id: String?
var foodname: String?
var quantity: Float!
var brandName: String?
var quantityType: String?
var calories: Float!
var date: Date?
}
In my TableView:
var grouped = Dictionary<Date, [Meal]>()
var listOfAllMeals = [Meal]() //already populated
self.grouped = Dictionary(grouping: self.listOfAllMeals.sorted(by: { ($0.date ?? nilDate) < ($1.date ?? nilDate) }),
by: { calendar.startOfDay(for: $0.date ?? nilDate) })
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return grouped.count
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return Array(grouped.keys)[section] as! String //this throws a thread error
}
This allows users to upload a meal multiple times a day for future viewing and now I want to show the meals in a TableView sectioned by their dates and sorted already by the latest. How do I achieve that?
Create a struct for the sections
struct Section {
let date : Date
let meals : [Meal]
}
and map the grouped dictionary to an array of Section
var sections = [Section]()
let sortedDates = self.grouped.keys.sorted(>)
sections = sortedDates.map{Section(date: $0, meals: self.grouped[$0]!)}
You could add a date formatter to display the Date instance more meaningful.
The table view data source methods are
override func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section].date.description
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].meals.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "foodCell", for: indexPath)
let meal = sections[indexPath.section].meals[indexPath.row]
...
Note:
Consider to use less optionals and a struct rather than a NSObject subclass. Unlike NSCoding Codable does not require to conform to NSObjectProtocol. And never declare properties as implicit unwrapped optional.
In your datamodel, you take the dictionary that you have with one day per entry, take the keys as an array, and sort the array.
Your tableview has as many sections as that array has entries. You create the title of each section from the date. For every section, you take the array of meals from the dictionary, so each section has a different number of rows, and the data from eac row is taken from one row of the arrays.
For example, to get the data for section 3, row 5, you take the date from your array of dates at index 3, you lookup the date in the dictionary and get an array of meals, and the meal at index 5 provides the data.

Sectioning a tableview with dictionaries [duplicate]

I have a dictionary that contains a key/value pair of a Date that contains an array of my custom object Meals grouped together by the same dates.
Meal Object:
class Meal: NSObject, Codable {
var id: String?
var foodname: String?
var quantity: Float!
var brandName: String?
var quantityType: String?
var calories: Float!
var date: Date?
}
In my TableView:
var grouped = Dictionary<Date, [Meal]>()
var listOfAllMeals = [Meal]() //already populated
self.grouped = Dictionary(grouping: self.listOfAllMeals.sorted(by: { ($0.date ?? nilDate) < ($1.date ?? nilDate) }),
by: { calendar.startOfDay(for: $0.date ?? nilDate) })
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return grouped.count
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return Array(grouped.keys)[section] as! String //this throws a thread error
}
This allows users to upload a meal multiple times a day for future viewing and now I want to show the meals in a TableView sectioned by their dates and sorted already by the latest. How do I achieve that?
Create a struct for the sections
struct Section {
let date : Date
let meals : [Meal]
}
and map the grouped dictionary to an array of Section
var sections = [Section]()
let sortedDates = self.grouped.keys.sorted(>)
sections = sortedDates.map{Section(date: $0, meals: self.grouped[$0]!)}
You could add a date formatter to display the Date instance more meaningful.
The table view data source methods are
override func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section].date.description
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].meals.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "foodCell", for: indexPath)
let meal = sections[indexPath.section].meals[indexPath.row]
...
Note:
Consider to use less optionals and a struct rather than a NSObject subclass. Unlike NSCoding Codable does not require to conform to NSObjectProtocol. And never declare properties as implicit unwrapped optional.
In your datamodel, you take the dictionary that you have with one day per entry, take the keys as an array, and sort the array.
Your tableview has as many sections as that array has entries. You create the title of each section from the date. For every section, you take the array of meals from the dictionary, so each section has a different number of rows, and the data from eac row is taken from one row of the arrays.
For example, to get the data for section 3, row 5, you take the date from your array of dates at index 3, you lookup the date in the dictionary and get an array of meals, and the meal at index 5 provides the data.

JSON Array to UITableView insert

Hello I have one question, I have one json url and this json url is return bellow json content;
{"ROW”:2,”COLUMN”:[“Name”,”Surname”],"DATA”:{“Name”:[“your_name”,”your_name1”],“Surname”:[“your_surname”,”your_surname1”]}}
But for me is needed this json format to convert Swift UITableView, im tested SwiftyJson but is not worked correctly for my issue. Im created one array and UITableView and im inserted correctly this array to UITableView, but I don’t know how can i add this json content to UITableView.
Thank you very much.
My Swift Code:
let animalArray = ["Firsname","Firsname","Firsname"]
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView:UITableView, numberOfRowsInSection section: Int)-> Int{
return animalArray.count
}
func tableView(_ tableView:UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for:indexPath)
cell.textLabel!.text = animalArray[indexPath.row]
return cell
}
You should first fetch JSON for example in viewDidLoad(), assign its values to some var or let declared in your view controller and then use it just the same way you just did.
Guide to fetch and decode JSON
Note your struct must be the exact copy of the JSON. Since it is
{
"ROW”: 2,
”COLUMN”:[“Name”,”Surname”],
"DATA”: { “Name”: [“your_name”,”your_name1”],
“Surname”: [“your_surname”,”your_surname1”] }
}
your struct must be
struct JSONStruct: Codable {
var ROW: Int
var COLUMN: [String]
var DATA: Dictionary<String: [String]>
}

What is the best aproach to feed data to a custom UITableViewCell that has multiple labels - Swift

I have a tableView with a custom viewCell which has three labels, displayCarName, displayMpg and displayCarPrice. I'm using three different arrays to feed each label in the tableView, everything is working fine but I feel like there is a better way to do this, may be using a single dictionary instead or something like that.
Here is the code I'm using.
private var carNameList = [String]()
private var mpgList = [Int]()
private var carPriceList = [Double]()
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return carNameList.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reusableCell", forIndexPath: indexPath) as! CustomCell
cell.displayCarName!.text= carNameList[indexPath.row]
cell.displayMpg!.text = String(mpgList[indexPath.row])
cell.displayCarPrice!.text = String(carPriceList[indexPath.row])
return cell
}
Is this a common way to feed multiple labels in a table row? If not, can someone help me improve the code shown above?
FYI- I actually have more than three labels in each row but for simplicity I'm only showing three.
Thanks
What you are using is sometimes referred to as parallel arrays. This approach was commonplace in languages lacking support for structures.
Since Swift does have support for structures, a better approach would be to define a class representing a Car, with three properties for car's Name, Mpg, and Price, and then using a single list of [Car] in place of three separate lists:
class Car {
let Name : String
let Mpg : Int
let Price : Double
init(name: String, mpg : Int, price : Double ) {
Name = name
Mpg = mpg
Price = price
}
}
...
private var carList = [Car]()
...
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reusableCell", forIndexPath: indexPath) as! CustomCell
Car car = carList[indexPath.row]
cell.displayCarName!.text= car.Name
cell.displayMpg!.text = String(car.Mpg)
cell.displayCarPrice!.text = String(car.Price)
return cell
}
Best way is to make a class for your data source
class SingleCellData {
var displayCarName : String!
var displayMpg : Int!
var displayCarPrice : Double!
}
In table View
var cellData : [SingleCellData] = []
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reusableCell", forIndexPath: indexPath) as! CustomCell
let sData = cellData[indexPath.row]
cell.displayCarName!.text= sData.displayCarName
cell.displayMpg!.text = String(sData.displayMpg)
cell.displayCarPrice!.text = String(StringsData.displayCarPrice)
return cell
}
You can also move the text assignment logic to the CustomCell class itself:
// Car.swift
class Car {
var name : String
var mpg : Int
var price : Double
}
// ViewController.swift
...
private var cars = [Car]()
...
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reusableCell", forIndexPath: indexPath) as! CustomCell
cell.car = cars[indexPath.row]
return cell
}
// CustomCell.swift
class CustomCell: UITableViewCell {
...
var car: Car? {
didSet {
displayCarName.text = car?.name
displayMpg.text = car?.mpg
displayPrice.text = car?.price
}
}
}

Resources