accessing a nested array to populate tableview in swift - arrays

here is my model class I can access the TotalMeetings and FriendsDetail but unable to access MeetingsDetail
struct TotalMeetings: Decodable {
var status: Int
var message: String
var friends: [FriendDetail]?
}
struct FriendDetail: Decodable {
var id: Int
var meetings: [MeetingsDetail]
}
struct MeetingsDetail: Decodable {
var meeting_id: Int
var meeting_with: String
}
This is how am parsing my response and can get successfully the value of FriendDetail but how do I get the values e.g. meeting_id from the MeetingsDetail
var friendsArr = [FriendDetail]()
meetingsData = try decoder.decode(TotalMeetings.self, from: response.data!)
friendsArr = meetingsData.friends
This is how im populating the tableview
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return friendsArr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! FriendsMeetingTVC
cell.emailLbl.text = friendsArr[indexPath.row].email //successful
// let meetingId = friendsArr[indexPath.row].meetings[indexPath.row].id // im trying this way but it show an error stating index out of range
// cell.userIdLbl.text = String(meetingId)
return cell
}

Related

Im having issues to show the array data after I decode it from JSON. How do I show the array to table view using delegate

I already have the array that is printed on the console after I decode it from JSON data. However, the table delegate are not working.
I tried using static array that is created on viewController and its working , but when I use the JSON data it wont work anymore. here is my table view protocols below.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return placeModel.indexCount
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// let cell = UITableViewCell(style: .default, reuseIdentifier: "restoCell")
let cell = tableView.dequeueReusableCell(withIdentifier: "restoCell", for: indexPath)
let array = placeModel.name[indexPath.row]
cell.textLabel?.text = String (array)
return cell
}
Here is whats on my ViewController , take note that the PlacesModel is at another swift file. the DispatchQueue.main.async is placed at didUpdateRestoLocation function. i tried place the delegate but still not working
class ViewController: UIViewController, CLLocationManagerDelegate {
var locationManager = CLLocationManager()
var placesManager = PlacesManager()
var placesClient = GMSPlacesClient()
var placeModel = PlacesModel(name: [], placesID: [], indexCount: 0)
#IBOutlet weak var cityText: UILabel!
#IBOutlet weak var kahitSaan: UILabel!
#IBOutlet weak var tableViewCafe: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
//ask location authorization
locationManager.requestWhenInUseAuthorization()
locationManager.requestAlwaysAuthorization()
placesManager.delegate = self
locationManager.delegate = self
tableViewCafe.delegate = self
tableViewCafe.dataSource = self
}
//MARK: - gps button
#IBAction func button(_ sender: Any) {
locationManager.startUpdatingLocation()
}
}
//MARK: - location manager delegate
extension ViewController {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
let lat = location.coordinate.latitude
let lon = location.coordinate.longitude
placesManager.fetchCafes( latitude: lat , longitude: lon)
}
locationManager.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error)
}
}
//MARK: - PLACESMANAGER - DELEGATE
extension ViewController : PlacesManagerDelegate {
func didUpdateRestoLocation(_ placeManager: PlacesManager, placesModel: PlacesModel) {
DispatchQueue.main.async {
self.kahitSaan.text = "\(placesModel.name[0])"
self.tableViewCafe.reloadData()
}
}
func didFailWithErrorProblem(error: Error) {
print(error)
}
//MARK: - table view populate
extension ViewController : UITableViewDelegate , UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//indexCount is the number of index of array from the data.
return placeModel.indexCount
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// let cell = UITableViewCell(style: .default, reuseIdentifier: "restoCell")
let cell = tableView.dequeueReusableCell(withIdentifier: "restoCell", for: indexPath)
let array = placeModel.name[indexPath.row]
cell.textLabel?.text = String (array)
return cell
}
}
my swift file PlaceModel.swift is at below. this is where the json data is placed after is was parsed.
struct PlacesModel {
let name : [String]
let placesID : [String]
let indexCount : Int
}
> here is the array of restaurant names
`["BJ\'s Restaurant & Brewhouse", "Fish\'s Wild", "Starbucks", "Pho Hoa Noodle Soup - Homestead", "Yayoi Cupertino", "Chipotle Mexican Grill", "Gochi Cupertino", "212 New York Pizza", "Oakmont Sandwiches", "Via Mia Pizza", "Mr. Ma’s Kitchen", "Bento Corner", "Duan Chun Zhen Noodle House", "Lei Garden", "Parkview Kitchen & Spirits", "Homestead Bowl & The X Bar", "Lee\'s Sandwiches", "TLT & Grill Soup", "Taiwan Porridge", "Taste Good Beijing Cuisine- Cupertino"]`

parsing nested arrays to populate uitableview in swift

This is the API response which I have parsed
{
"status": 0,
"message": "Friends found.",
"friends": [
{
"id": 52,
"meetings": [
{
"id": 47,
"meeting_with": "Bbb"
}
]
}
]
}
The model class
struct TotalMeetings: Decodable {
var status: Int
var message: String
var friends: [FriendDetail]?
}
struct FriendDetail: Decodable {
var id: Int
var meetings: [MeetingsDetail]
}
struct MeetingsDetail: Decodable {
var id: Int
var meeting_with: String
}
Im calling the API here and the call is successful.
var meetingssData :Friends!
let decoder = JSONDecoder()
do{
meetingsData = try decoder.decode(TotalMeetings.self, from: response.data!)
let meet = [self.meetingsData!.friends].compactMap({$0}).flatMap({$0})
print(meetingsData!)
}catch{
print(error)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! FriendsMeetingTVC
return cell
}
Please guide me how do I populate the tableview with the response coming from API call.
you have to declared array instance of type according to your need either FriendDetail or MeetingsDetail.
After fetching data from api parse it and store mapped data into an array instance. Then, reload table view.
numberOfRowsInSection method of tableview returns number of rows using your array count.
You can access value by using dot notation as shown in "cellForRowAt indexPath:" method.
class MeetingVC: UIViewController, UITableViewDelegate, UITableViewDatasource {
var friendsArr = [FriendDetail]()
func fetchFriendDetail() {
let decoder = JSONDecoder()
do {
let meetingsData = try decoder.decode(TotalMeetings.self, from: response.data!)
self.friendsArr = meetingsData.friends ?? []//[meetingsData.friends].compactMap({$0}).flatMap({$0})
print(self.friendsArr)
self.tableView.reloadData()
}catch{
print(error)
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return friendsArr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! FriendsMeetingTVC
cell.emailTxt.text = friendsArr[indexPath.row].email
return cell
}
}

populate tableview cell from complex api json using swift

I'm trying to parse API JSON and display it in UITableView, but the problem is I cannot access all array in this API.
struct RootResults: Codable {
var results: [results]
}
// MARK: - results
struct results: Codable {
var container_number: String?
var commodities: [commodities]
}
// MARK: - commodities
struct commodities: Codable {
var commodity_name_en: String?
var varieties: [varieties]
}
// MARK: - varieties
struct varieties: Codable {
var variety_name_en: String?
var variety_name_ar: String?
}
import UIKit
class ViewController: UIViewController {
#IBOutlet var resultsTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
abuseedoAPIget()
}
var arrData = [results]()
var arrResults = [commodities]()
func abuseedoAPIget(){
let urlJSON = "http://abuseedotrading.com/apps/api/acp/?key=4ea1e08dd9ab329bbdaa9e5b42939c04&query=list_containers"
guard let url = URL(string: urlJSON) else {return}
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else {return}
guard error == nil else {return}
do {
let decoder = JSONDecoder()
let APIResponse = try decoder.decode(RootResults.self, from: data)
self.arrData = APIResponse.results
DispatchQueue.main.async{
self.resultsTable.reloadData()
}
} catch let error {
print("Failed to decode JSON:", error)
}
}.resume()
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell
let dataa = arrData[indexPath.row]
cell.conLabel.text = dataa.container_number
cell.comLabel.text = dataa.commodity_name_en
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
}
For Swift to be able to decode the JSON response into objects you need to have a similar structure defined in Swift using objects.
Your RootResults object needs to implement Codable protocol and represent the JSON structure.
Below a part of the returned JSON:
{
"status": 200,
"runtime": 1.7315270900726,
"results_count": 13,
"results": [
{
"container_id": 36473,
"container_number": "MMAU1163814",
"shipment_id": 17359,
}
}
RootResults would look something like this:
struct RootResults: Codable {
let status: Int
let runtime: Float
let results_count: 13
let results: [Container]
}
struct Container: Codable {
let container_id: Int
let container_number: String
let shipment_id: Int
}
More information on Swift Codable
Swift codable
SO question about
In the function tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) you are accessing data on different levels.
The container_number -property is accessible when retrieving an object from results. The commodity_name_en is a "level" deeper and part-of the commodities -array. To access the first item in the commodities -array , try the following:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell
let dataa = arrData[indexPath.row]
cell.conLabel.text = dataa.container_number
cell.comLabel.text = dataa.commodities[0].commodity_name_en
return cell
}
As Vadian mentions, it's common to start types(struct, class, enum) with a capital letter in Swift. Have a look at struct and classes documentation

Array list item count and number of element in an array mismatch while using swiftyjson?

import UIKit
import Alamofire
import SwiftyJSON
class RecentAdded: UIViewController ,UITableViewDataSource,UITableViewDelegate{
#IBOutlet var tableView: UITableView!
var list:JSON!
var sendurl:String!
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(.GET, "http://api.dirble.com/v2/stations/recent", parameters: ["token": "260674ecb51572a8faa4e77199"])
.responseJSON { response in
if let json = response.result.value {
self.list = JSON(data: response.data!)
print(self.list) /// Showing less element if element is more than 25
self.tableView.dataSource = self
self.tableView.delegate = self
self.tableView.reloadData()
print(self.list.arrayValue.capacity) // Printing the actual capacity
}
}
// Do any additional setup after loading the view.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.list.arrayValue.capacity
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as! RecentCellTableViewCell
let sampleArray = self.list.array
let imageURL:String! = sampleArray![indexPath.row]["image"]["thumb"]["url"].stringValue
if imageURL != ""{
Alamofire.request(.GET, imageURL).responseImage { (response) -> Void in
guard let image = response.result.value else { return }
cell.img!.image = image
}
}else{
cell.img!.image = UIImage(named: "rad")!
}
cell.nm?.text = sampleArray![indexPath.row]["name"].stringValue
let catarr = sampleArray![indexPath.row]["categories"].array
let des:String! = "category : " + catarr![0]["title"].stringValue + " " + "slug : " + catarr![0]["slug"].stringValue
cell.des?.text = des
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath) as! RecentCellTableViewCell
let sampleArray = self.list.array
let url = sampleArray![indexPath.row]["streams"].array
sendurl = url![0]["stream"].stringValue
self.performSegueWithIdentifier("next", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//
if (segue.identifier == "next") {
// initialize new view controller and cast it as your view controller
var viewController = segue.destinationViewController as! Player
viewController.urll = sendurl
}
}
}
My problem is when i am printing list.arrayvalue.capacity it is showing the actual size of array which is correct but when i tried to print element of array it show's less element then its counting. so i am not sure what is wrong in code????/
The main problem is in printing element. Not printing all elements.
I think you're confusing the array capacity with the actual item count. For numberOfRowsInSection, use the count property of the array instead:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.list.arrayValue.count
}
More details about count vs. capacity in this answer: Swift array.capacity vs array.count

How do I populate two sections in a tableview with two different arrays using swift?

I have two arrays Data1 and Data2 and I want to populate the data within each of these (they contain strings) into a tableview in two different sections.
The first section should have a heading "Some Data 1" and the second section should be titled "KickAss".
I have both sections populating with data from the first array (but with no headings either).
Here is my code so far:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var rowCount = 0
if section == 0 {
rowCount = Data1.count
}
if section == 1 {
rowCount = Data2.count
}
return rowCount
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
let ip = indexPath
cell.textLabel?.text = Data1[ip.row] as String
return cell
}
in the cellForRowAtIndexPath method, is it possible for me to identify the section somehow like I did in the numberOfRowsInSection method?
Also, how do I give titles to each section?
TableView Cells
You could use a multidimensional array. For example:
let data = [["0,0", "0,1", "0,2"], ["1,0", "1,1", "1,2"]]
For the number of sections use:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return data.count
}
Then, to specify the number of rows in each section use:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data[section].count
}
Finally, you need to setup your cells:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellText = data[indexPath.section][indexPath.row]
// Now do whatever you were going to do with the title.
}
TableView Headers
You could again use an array, but with just one dimension this time:
let headerTitles = ["Some Data 1", "KickAss"]
Now to set the titles for the sections:
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section < headerTitles.count {
return headerTitles[section]
}
return nil
}
The code above checks to see there's a title for that section and returns it, otherwise nil is returned. There won't be a title if the number of titles in headerTitles is smaller than the number of arrays in data.
The Result
You could create a Struct to hold the data that belongs to a section, as an alternative to my previous answer. For example:
struct SectionData {
let title: String
let data : [String]
var numberOfItems: Int {
return data.count
}
subscript(index: Int) -> String {
return data[index]
}
}
extension SectionData {
// Putting a new init method here means we can
// keep the original, memberwise initaliser.
init(title: String, data: String...) {
self.title = title
self.data = data
}
}
Now in your view controller you could setup your section data like so:
lazy var mySections: [SectionData] = {
let section1 = SectionData(title: "Some Data 1", data: "0, 1", "0, 2", "0, 3")
let section2 = SectionData(title: "KickAss", data: "1, 0", "1, 1", "1, 2")
return [section1, section2]
}()
Section Headers
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return mySections.count
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return mySections[section].title
}
Compared to my previous answer, you now don't have to worry about matching the number of headerTitles to the number of arrays in data.
TableView Cells
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return mySections[section].numberOfItems
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellTitle = mySections[indexPath.section][indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel?.text = cellTitle
return cell
}
You can determine which section you are in by looking at indexPath.section.
To specify the titles, override the function
func tableView(tableView: UITableView!, titleForHeaderInSection section: Int) -> String!
In Swift 4 or Swift 5 you can use the code below.
Here a custom header section with filter is shown:
create a project
Add table view
create UITableView Cell
Connect label to uitable view & table with view controller
Add bellow code
import UIKit
struct Category {
let name : String
var items : [[String:Any]]
}
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource,UITextFieldDelegate {
#IBOutlet weak var txtName: UITextField!
#IBOutlet weak var tableView: UITableView!
var originalArr = [[String:Any]]();
var recentArr = [[String:Any]]();
var searchArrRes = [[String:Any]]()
var searching:Bool = false
//
var sections = [Category]()
override func viewDidLoad() {
super.viewDidLoad()
//Assign delegate don't forget
txtName.delegate = self
tableView.delegate = self
tableView.dataSource = self
recentArr = [
["name": "Enamul", "number": "+8800000003"],
["name": "Enam", "number": "+8800000004"]
]
originalArr = [
["name": "abdul", "number": "+8800000001"],
["name": "abdin", "number": "+8800000002"],
["name": "Enamul", "number": "+8800000003"],
["name": "enam", "number": "+8800000004"],
["name": "Rafi", "number": "+8800000005"],
["name": "Ehaque", "number": "+8800000006"]
]
//my array
sections = [
Category(name:"Recent", items:recentArr),
Category(name:"ALL", items:originalArr)
]
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
guard let tableView = view as? UITableViewHeaderFooterView else { return }
tableView.textLabel?.textColor = UIColor.red
}
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool{
//input text
let searchText = textField.text! + string
searchArrRes = self.originalArr.filter({(($0["name"] as! String).localizedCaseInsensitiveContains(searchText))})
if(searchArrRes.count == 0){
searching = false
}else{
searching = true
}
self.tableView.reloadData();
return true
}
func numberOfSections(in tableView: UITableView) -> Int {
if( searching == true){
return 1
}
return self.sections.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if( searching == true){
return ""
}
return self.sections[section].name
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if( searching == true){
return searchArrRes.count
}else{
let items = self.sections[section].items
return items.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! Custom_cell
// var dict = itemsA[indexPath.section]
if( searching == true){
var dict = searchArrRes[indexPath.row]
cell.name.text = dict["name"] as? String
cell.number.text = dict["number"] as? String
}else{
let items = self.sections[indexPath.section].items
let item = items[indexPath.row]
cell.name.text = item["name"] as? String
cell.number.text = item["number"] as? String
}
return cell
}
}
You can download full source from GitHub.GitHub Like: https://github.com/enamul95/Custom_table_view_section.git
Can do Sections in Tableview and can change the colours of Header sections
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tabview: UITableView!
var tablecell = NewTableViewCell()
let data = [["SWIFT", "BALENO", "ALTO", "CIAZ"], ["INNOVA", "GLANZA", "FORTUNER"] , ["BMW X5", "BMW M4", "BMW 7 Series", "BMW X7", "BMW i3"]]
let brand: Array<String> = ["MARUTHI", "TOYOTA", "BMW"]
override func viewDidLoad() {
super.viewDidLoad()
}
func numberOfSections(in tableView: UITableView) -> Int {
return brand.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data[section].count }
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.brand[section]
}
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
// can change the header color of background and title with this code :)
(view as! UITableViewHeaderFooterView).contentView.backgroundColor = UIColor.red.withAlphaComponent(0.4)
(view as! UITableViewHeaderFooterView).textLabel?.textColor = UIColor.yellow
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell1: NewTableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell1") as! NewTableViewCell
let text = data[indexPath.section][indexPath.row]
cell1.textLabel!.text = text
return cell1
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Resources