JsonFile (parse json data)
[
{
"name": "rings",
"sub_category": [
{
"name": "engagement",
"display_name": "Engagement"
},
{
"name": "band-couple band",
"display_name": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed scelerisque aliquet arcu, sed placerat turpis. Sed vulputate finibus nisi, nec gravida turpis consectetur quis. Nullam quis vestibulum ex. Integer lacinia quam sed rutrum tempus. Sed quis metus mollis, euismod ipsum in, vulputate turpis. Fusce suscipit ligula in efficitur interdum. Sed ante velit, vulputate nec nibh commodo, tempus molestie eros. Etiam consequat enim nisi, a mollis nisi gravida et. Fusce scelerisque ex vitae turpis fermentum facilisis."
},
{
"name": "Navaratnam+Collection",
"display_name": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed scelerisque aliquet arcu, sed placerat turpis."
},
{
"name": "cocktail",
"display_name": "Lorem ipsum dolor sit amet"
},
{
"name": "below+15000",
"display_name": "Under 15,000/-"
},
{
"name": "hearts",
"display_name": "Heart Rings"
},
{
"name": "halo",
"display_name": "Halo Rings"
},
{
"name": "view all",
"display_name": "View All"
}
]
},
{
"name": "earrings",
"sub_category": [
{
"name": "studs",
"display_name": "Studs"
},
{
"name": "drops",
"display_name": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed scelerisque aliquet arcu, sed placerat turpis. Sed vulputate finibus nisi, nec gravida turpis consectetur quis. Nullam quis vestibulum ex. Integer lacinia quam sed rutrum tempus. Sed quis metus mollis, euismod ipsum in, vulputate turpis. Fusce suscipit ligula in efficitur interdum. Sed ante velit, vulputate nec nibh commodo, tempus molestie eros. Etiam consequat enim nisi, a mollis nisi gravida et. Fusce scelerisque ex vitae turpis fermentum facilisis."
},
{
"name": "hoops",
"display_name": "Hoops"
},
{
"name": "jhumki",
"display_name": "Jhumkis"
},
{
"name": "sui dhaga",
"display_name": "Sui-dhaga"
},
{
"name": "pearl",
"display_name": "Pearl"
},
{
"name": "below 15000",
"display_name": "Less Than 15,000/-"
},
{
"name": "view all",
"display_name": "View All"
}
]
},
{
"name": "pendants",
"sub_category": [
{
"name": "alphabet",
"display_name": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed scelerisque aliquet arcu, sed placerat turpis."
},
{
"name": "religious",
"display_name": "Religious"
},
{
"name": "hearts",
"display_name": "Hearts"
},
{
"name": "diamond and colored stones",
"display_name": "Gemstone Pendants"
},
{
"name": "single stone-only diamond",
"display_name": "Single Diamond Pendants"
},
{
"name": "floral",
"display_name": "Floral"
},
{
"name": "locket",
"display_name": "Lockets"
},
{
"name": "view all",
"display_name": "View All"
}
]
},
{
"name": "bangles",
"sub_category": [
{
"name": "round bangle",
"display_name": "Round"
},
{
"name": "oval bangle",
"display_name": "Oval"
},
{
"name": "eternity",
"display_name": "Eternity"
},
{
"name": "yellow gold",
"display_name": "Gold"
},
{
"name": "wedding",
"display_name": "Bridal"
},
{
"name": "view all",
"display_name": "View All"
}
]
},
{
"name": "bracelets",
"sub_category": [
{
"name": "yellow gold",
"display_name": "Gold"
},
{
"name": "only diamond",
"display_name": "Diamond"
},
{
"name": "floral",
"display_name": "Floral"
},
{
"name": "view all",
"display_name": "View All"
}
]
},
{
"name": "nose pins",
"sub_category": [
{
"name": "yellow gold",
"display_name": "Gold"
},
{
"name": "only diamond",
"display_name": "Diamond"
},
{
"name": "only coloured stone",
"display_name": "Gem Stone"
},
{
"name": "view all",
"display_name": "View All"
}
]
},
{
"name": "necklaces",
"sub_category": [
{
"name": "yellow gold",
"display_name": "Gold"
},
{
"name": "wedding",
"display_name": "Bridal"
},
{
"name": "view all",
"display_name": "View All"
}
]
},
{
"name": "tanmaniya",
"sub_category": [
{
"name": "only diamond",
"display_name": "Diamond"
},
{
"name": "ruby",
"display_name": "Ruby"
},
{
"name": "below 50000",
"display_name": "Under 50,000/-"
},
{
"name": "view all",
"display_name": "View All"
}
]
},
{
"name": "only diamond",
"sub_category": [
{
"name": "rings",
"display_name": "Rings"
},
{
"name": "earrings",
"display_name": "Earrings"
},
{
"name": "pendants",
"display_name": "Pendants"
},
{
"name": "bangles",
"display_name": "Bangles"
},
{
"name": "bracelets",
"display_name": "Bracelets"
},
{
"name": "necklaces",
"display_name": "Necklaces"
},
{
"name": "nose pins",
"display_name": "Nose Pins"
},
{
"name": "view all",
"display_name": "View All"
}
]
},
{
"name": "plain gold jewellery",
"sub_category": [
{
"name": "rings",
"display_name": "Rings"
},
{
"name": "earrings",
"display_name": "Earrings"
},
{
"name": "pendants",
"display_name": "Pendants"
},
{
"name": "bangles",
"display_name": "Bangles"
},
{
"name": "bracelets",
"display_name": "Bracelets"
},
{
"name": "necklaces",
"display_name": "Necklaces"
},
{
"name": "nose pins",
"display_name": "Nose Pins"
},
{
"name": "view all",
"display_name": "View All"
}
]
},
{
"name": "diamond and colored stones",
"sub_category": [
{
"name": "rings",
"display_name": "Rings"
},
{
"name": "earrings",
"display_name": "Earrings"
},
{
"name": "pendants",
"display_name": "Pendants"
},
{
"name": "bangles",
"display_name": "Bangles"
},
{
"name": "bracelets",
"display_name": "Bracelets"
},
{
"name": "necklaces",
"display_name": "Necklaces"
},
{
"name": "nosepins",
"display_name": "Nosepins"
},
{
"name": "tanmaniyas",
"display_name": "Tanmaniyas"
},
{
"name": "view all",
"display_name": "View All"
}
]
},
{
"name": "platinum",
"sub_category": [
{
"name": "single stone",
"display_name": "Single Stone"
},
{
"name": "multistone",
"display_name": "Multi Stone"
},
{
"name": "single stone",
"display_name": "Single Stone"
},
{
"name": "view all",
"display_name": "View All"
}
]
},
{
"name": "men",
"sub_category": [
{
"name": "rings",
"display_name": "Rings"
},
{
"name": "pendants",
"display_name": "Pendants"
},
{
"name": "bracelets",
"display_name": "Bracelets"
},
{
"name": "earrings",
"display_name": "Earrings"
},
{
"name": "view all",
"display_name": "View All"
}
]
},
{
"name": "kids",
"sub_category": [
{
"name": "earrings",
"display_name": "Earrings"
},
{
"name": "pendants",
"display_name": "Pendants"
},
{
"name": "view all",
"display_name": "View All"
}
]
}
]
DATA MODEL
//
// Category.swift
// Assignment
//
// Created by vishnu duggisetty on 02/November/2019 .
// Copyright © 2019 vishnu duggisetty. All rights reserved.
//
import Foundation
struct categoryDetails:Decodable {
var name:String
var sub_category:[subCategoryDetails]
}
struct subCategoryDetails:Decodable {
var name:String
var display_name:String
}
REQUEST
//
// CategoryRequest.swift
// Assignment
//
// Created by vishnu duggisetty on 02/November/2019 .
// Copyright © 2019 vishnu duggisetty. All rights reserved.
//
import Foundation
enum catError:Error{
case NoData
case ParseError
case NoFilePath
}
struct CategoryRequest {
func getCategories(completion:@escaping(Result<[categoryDetails], catError>) -> Void){
if let filePath = Bundle.main.path(forResource: "category", ofType: "json"){
do {
let jsonData = try Data(contentsOf: URL(fileURLWithPath: filePath), options: .mappedIfSafe)
let decoder = JSONDecoder()
let categoryResponse = try decoder.decode([categoryDetails].self, from: jsonData)
print(categoryResponse)
completion(.success(categoryResponse))
} catch let error{
print(error.localizedDescription)
completion(.failure(.ParseError))
}
}
else{
completion(.failure(.NoFilePath))
}
}
}
//
// CategoryTableViewController.swift
// Assignment
//
// Created by vishnu duggisetty on 02/November/2019 .
// Copyright © 2019 vishnu duggisetty. All rights reserved.
//
import UIKit
class CategoryTableViewController: UITableViewController {
var expandIndex: Int = -1
var expandSectionHead: UITableViewHeaderFooterView!
var listofCategories = [categoryDetails](){
didSet{
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.getData()
self.tableView.tableFooterView = UIView()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
}
func getData(){
let cateRequest = CategoryRequest()
cateRequest.getCategories{[weak self] result in
switch result{
case .failure(let error):
print(error)
case .success(let categoryList):
self?.listofCategories = categoryList
}
}
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if(self.expandIndex == section){
return self.listofCategories[section].sub_category.count
}
else{
return 0
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CategoryTableCell", for: indexPath)
let name = self.listofCategories[indexPath.section].sub_category[indexPath.row].name
let displayName = self.listofCategories[indexPath.section].sub_category[indexPath.row].display_name
cell.textLabel?.attributedText = makeAttributedString(title: name, subtitle: displayName)
cell.textLabel?.numberOfLines = 0;
return cell
}
}
//TableView Sections
extension CategoryTableViewController{
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
if(self.listofCategories.count > 0){
self.tableView.backgroundView = nil
return self.listofCategories.count
}
return 0
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if(self.listofCategories.count > 0){
return self.listofCategories[section].name.uppercased()
}
return ""
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 60
}
override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
let header: UITableViewHeaderFooterView = view as! UITableViewHeaderFooterView
header.contentView.backgroundColor = UIColor.white
header.textLabel?.textColor = UIColor.blue
let separator = UIView(frame: CGRect(x: 0, y: header.frame.size.height, width: header.frame.size.width, height: 1))
separator.backgroundColor = UIColor.lightGray
header.addSubview(separator)
header.tag = section
let headerGesture = UITapGestureRecognizer()
headerGesture.addTarget(self, action: #selector(self.sectionHeaderTap(_:)))
header.addGestureRecognizer(headerGesture)
}
}
//Expandable and collapsable methods
extension CategoryTableViewController{
@objc func sectionHeaderTap(_ sender: UITapGestureRecognizer) {
let headerView = sender.view as! UITableViewHeaderFooterView
let section = headerView.tag
//If no expanded cell, expandIndex will be -1
if (self.expandIndex == -1) {
self.expandIndex = section
tableViewExpandSection(section)
}
else {
//If same expand cell is selected, then collapse
if (self.expandIndex == section) {
tableViewCollapeSection(section)
}
else {
//First collapse then expand
tableViewCollapeSection(self.expandIndex)
tableViewExpandSection(section)
}
}
}
func tableViewExpandSection(_ section: Int) {
let subCat = self.listofCategories[section].sub_category
if (subCat.count == 0) {
self.expandIndex = -1;
return;
}
else {
var indexesPath = [IndexPath]()
for i in 0 ..< subCat.count {
let index = IndexPath(row: i, section: section)
indexesPath.append(index)
}
self.expandIndex = section
self.tableView!.beginUpdates()
self.tableView!.insertRows(at: indexesPath, with: UITableView.RowAnimation.fade)
self.tableView!.endUpdates()
}
//Scroll the tableview to selected cell
self.tableView.scrollToRow(at: IndexPath(item: 0, section: section), at: .top, animated: true)
}
func tableViewCollapeSection(_ section: Int) {
let subCat = self.listofCategories[section].sub_category
//Make expandIndex to -1 when collapse is selected
self.expandIndex = -1;
if (subCat.count == 0) {
return;
}
else {
var indexesPath = [IndexPath]()
for i in 0 ..< subCat.count {
let index = IndexPath(row: i, section: section)
indexesPath.append(index)
}
self.tableView!.beginUpdates()
self.tableView!.deleteRows(at: indexesPath, with: UITableView.RowAnimation.fade)
self.tableView!.endUpdates()
}
}
}
//Change the text format
extension CategoryTableViewController{
func makeAttributedString(title: String, subtitle: String) -> NSAttributedString {
let titleAttributes = [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .headline), NSAttributedString.Key.foregroundColor: UIColor.purple]
let subtitleAttributes = [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .subheadline)]
let titleString = NSMutableAttributedString(string: "\(title)\n", attributes: titleAttributes)
let subtitleString = NSAttributedString(string: subtitle, attributes: subtitleAttributes)
titleString.append(subtitleString)
return titleString
}
}