viewmodel in app delegate ion mmvm in swift - ios

I am doing in mvvm .
my code as below:-
private var url = URL(string: "http://www.example.com")!
typealias JSONDictionary = [String:Any]
class Webservice{
static func fetchHostels(completion: #escaping ([JSONDictionary]) -> ()) {
URLSession.shared.dataTask(with: url) { data, _, _ in
if let data = data {
let json = try! JSONSerialization.jsonObject(with: data, options: [])
let dictionaries = json as! [JSONDictionary]
completion(dictionaries)
}
}.resume()
}
}
My hostelmodel:-
class hostelmodel: NSObject {
var name: String?
var city: String?
init(_ dictionary: [String: Any]) {
if let name = dictionary["name"] as? String, let location = dictionary["location"] as? [String: Any], let city = location["city"] as? String {
self.name = name
self.city = city
}
}
}
my hostelviewmodel:-
class hostelviewmodel: NSObject {
private var hostels: [hostelmodel] = []
func fetchData(completion: (() -> Void)?) {
Webservice.fetchHostels { [weak self] dictionaries in
self?.hostels = dictionaries.flatMap(hostelmodel.init)
completion?()
}
}
func numberOfSections() -> Int {
//your number of section
return 1
}
func numberOfRows() -> Int {
//your number of rows
return hostels.count
}
func hostel(atIndex index: Int) -> hostelmodel {
return hostels[index]
}
}
my hostellist:-
#IBOutlet private weak var tableView: UITableView!
private var viewModel: hostelviewmodel
init(hostelViewModel: hostelviewmodel) {
self.viewModel = hostelViewModel
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
fetchData()
}
private func fetchData() {
viewModel.fetchData { [weak self] in
DispatchQueue.main.async {
self?.tableView.reloadData()
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return viewModel.numberOfSections()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.numberOfRows()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)as! QM_RestaurtantCell
cell.setRestaurtantData(restaurtant: QM_RestaurtantModel)
return cell
}
}
}
my hosteltablecell:-
#IBOutlet weak var name: UILabel!
#IBOutlet weak var city: UILabel!
func setRestaurtantData(restaurtant:hostelmodel)
{
self.name.text = restaurtant.name
self.city.text = restaurtant.city
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
I am doing without storyboard, so here how to set in the appdelegate. What should I set as root view controller in app delegate class. And also how the name and city value display in the tableview cell for row index in swift.How to deo

For Migrating to App Delegate to your HostelList View Controller :
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Transferring Control to First Controller
window = UIWindow(frame: UIScreen.main.bounds)
let nav1 = UINavigationController()
let vc = HostelList()
nav1.viewControllers = [vc]
window?.rootViewController = vc
window?.makeKeyAndVisible()
return true
}
To set your values in tableview, Use this function :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
}

Ok. Try this refactored code pls:
AppDelegate didFinish:
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let vm = HostelViewModel()
let vc = HostelViewController(hostelViewModel: vm)
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = vc
window?.makeKeyAndVisible()
return true
}
HostelViewController:
class HostelViewController: UIViewController {
private let tableView = UITableView(frame: .zero, style: .plain)
private var viewModel: HostelViewModel
init(hostelViewModel: HostelViewModel) {
self.viewModel = hostelViewModel
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
createUI()
tableView.dataSource = viewModel
fetchData()
}
func createUI() {
tableView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(tableView)
tableView.register(HostelCell.self, forCellReuseIdentifier: cellIdentifier)
tableView.estimatedRowHeight = 60
tableView.rowHeight = UITableViewAutomaticDimension
let tableViewConstraints: [NSLayoutConstraint] = [
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
tableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor)
]
NSLayoutConstraint.activate(tableViewConstraints)
}
private func fetchData() {
viewModel.fetchData { [weak self] in
DispatchQueue.main.async {
self?.tableView.reloadData()
}
}
}
}
HostelViewModel:
class HostelViewModel: NSObject, UITableViewDataSource {
private var hostels: [HostelModel] = []
func fetchData(completion: (() -> Void)?) {
Webservice.fetchHostels { [weak self] dictionaries in
self?.hostels = dictionaries.flatMap(HostelModel.init)
completion?()
}
}
func hostel(atIndex index: Int) -> HostelModel {
return hostels[index]
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return hostels.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let hostel = hostels[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! HostelCell
cell.setRestaurtantData(restaurtant: hostel)
return cell
}
}
HostelCell file:
let cellIdentifier = "HostelTableCell"
class HostelCell: UITableViewCell {
private var name: UILabel = UILabel()
private var city: UILabel = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
createUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setRestaurtantData(restaurtant: HostelModel)
{
self.name.text = restaurtant.name
self.city.text = restaurtant.city
}
func createUI() {
[city, name].forEach {
contentView.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = true
}
city.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8).isActive = true
city.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
name.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -8).isActive = true
name.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
HostelModel:
class HostelModel {
var name: String?
var city: String?
init(_ dictionary: [String: Any]) {
if let name = dictionary["name"] as? String, let location = dictionary["location"] as? [String: Any], let city = location["city"] as? String {
self.name = name
self.city = city
}
}
}

Related

Add Animation on TableView Cells

I have made a tableView with cells that take the data from an API. I have imported ViewAnimator Package because I want to add some animation when the cells appear but the animation starts while the tableview had already be presented with data.
Maybe I have made a mistake at the logic but I can't find the solution.
The OpeningViewController is this :
import UIKit
import ViewAnimator
class OpeningViewController: UIViewController {
//MARK: - IBProperties
#IBOutlet var openingImg: UIImageView!
#IBOutlet var startButton: UIButton!
//MARK: - Properties
var nft : Nft?
//MARK: - Life Cyrcle
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let animation = AnimationType.from(direction: .top, offset: 50)
openingImg.animate(animations: [animation] , delay: 0.3, duration: 2)
openingImg.layer.shadowColor = UIColor.black.cgColor
openingImg.layer.shadowOffset = CGSize(width: 0, height: 0)
openingImg.layer.shadowOpacity = 0.65
openingImg.layer.shadowRadius = 10
}
//MARK: - Methods
#IBAction func startApp(_ sender: Any) {
HapticsManager.shared.selectionVibrate()
let storyBoard = UIStoryboard(name: "Lobby", bundle: nil)
let controller = storyBoard.instantiateViewController(withIdentifier: "LobbyViewController") as! LobbyViewController
controller.modalTransitionStyle = .flipHorizontal
self.navigationController?.pushViewController(controller, animated: true)
}
}
The presentation happens in LobbyViewController :
import UIKit
import ViewAnimator
class LobbyViewController: UIViewController {
// MARK: - IBProperties
#IBOutlet weak var tableView: UITableView!
// MARK: - Properties
var data: [DataEnum] = []
var likes:[Int] = []
var numlikes: Int = 0
var nfts: [Nft] = []
let creators : [Creator] = []
var icons: [Icon] = []
var loadData = APICaller()
// MARK: - Life Cyrcle
override func viewDidLoad() {
super.viewDidLoad()
let nib = UINib(nibName: "AssetTableViewCell", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "AssetTableViewCell")
let nib2 = UINib(nibName: "CreatorsTableViewCell", bundle: nil)
tableView.register(nib2, forCellReuseIdentifier: "CreatorsTableViewCell")
tableView.dataSource = self //method to generate cells,header and footer before they are displaying
tableView.delegate = self //method to provide information about these cells, header and footer ....
downloadJSON {
self.tableView.reloadData()
print("success")
}
loadData.downloadData { (result) in
print(result)
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let animation = AnimationType.from(direction: .top, offset: 300)
UIView.animate(views: tableView.visibleCells,
animations: [animation], delay: 1, duration: 2)
}
//stelnei ta dedomena apo to kathe row ston PresentViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? PresentViewController {
if tableView.cellForRow(at: tableView.indexPathForSelectedRow!) is AssetTableViewCell {
destination.nft = nfts[tableView.indexPathForSelectedRow!.row-1]
destination.delegate = self
} else {
//add alert action
let alert = UIAlertController(title: "Invalid Touch", message: "You press wrong row. Choose one of the following list.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(alert, animated: true, completion: {
return
})
}
}
}
// MARK: - Methods
func downloadJSON(completed: #escaping () -> ()) {
let url = URL(string: "https://public.arx.net/~chris2/nfts.json")
URLSession.shared.dataTask(with: url!) { [self] data, response, error in
if error == nil {
do {
self.nfts = try JSONDecoder().decode([Nft].self, from: data!)
let creators = nfts.map { nft in
nft.creator
}
self.data.append(.type1(creators: creators))
self.nfts.forEach { nft in
self.data.append(.type2(nft: nft))
}
DispatchQueue.main.async {
completed()
}
}
catch {
print("error fetching data from api")
}
}
}.resume()
}
}
// MARK: - Extensions
extension LobbyViewController : UITableViewDelegate , UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
indexPath.row == 0 ? 100 : UITableView.automaticDimension
}
//gemizo ta rows tou table
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch self.data[indexPath.item] {
case .type1(let creators):
print("--->", creators)
let cell = tableView.dequeueReusableCell(withIdentifier: "CreatorsTableViewCell",
for: indexPath) as! CreatorsTableViewCell
cell.layer.cornerRadius = 15
cell.layer.shadowColor = UIColor.black.cgColor
cell.layer.shadowOffset = CGSize(width: 0, height: 0)
cell.layer.shadowOpacity = 0.8
cell.layer.shadowRadius = 15
cell.layer.cornerRadius = cell.frame.height/2
cell.updateCreators(creators)
return cell
case .type2(let nft):
let cell = tableView.dequeueReusableCell(withIdentifier: "AssetTableViewCell",
for: indexPath) as! AssetTableViewCell
cell.nameLabel?.text = nft.name
cell.nameLabel.layer.cornerRadius = cell.nameLabel.frame.height/2
cell.likesLabel?.text = "\((numlikes))"
let imgUrl = (nft.image_url)
print(imgUrl)
cell.iconView.downloaded(from: imgUrl)
cell.iconView.layer.cornerRadius = cell.iconView.frame.height/2
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showDetails", sender: self)
}
}
extension LobbyViewController : TestDelegate{
func sendBackTheLikess(int: Int) {
numlikes = int
tableView.reloadData()
}
}
// MARK: - Enums
enum DataEnum {
case type1(creators: [Creator])
case type2(nft: Nft)
}
// MARK: - Struct
struct Constants {
static let url = "https://public.arx.net/~chris2/nfts.json"
}
The APICaller :
import Foundation
final class APICaller {
static let shared = APICaller()
public struct Constants {
static let url = "https://public.arx.net/~chris2/nfts.json"
}
public func downloadData(completion:#escaping (Result<[Nft], Error>) -> Void )
{
guard let url = URL(string:Constants.url)else{
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
//print(response)
print("here")
guard let data = data , error == nil else{
print("something went wrong with data")
return
}
print("here4")
//mexri edo exoume parei ta data kai tora me to do-catch tha ta kanoume convert se object
do{
//Decode the response
let nfts = try JSONDecoder().decode([Nft].self, from: data)
completion(.success(nfts))
print(nfts)
}catch{
completion(.failure(error))
}
}
task.resume()
}
}
and here is a video as a gif of what happen fro better understanding
https://gifyu.com/image/SEGkZ

Why is my Swift programmatic tableview not populating?

I have been practicing doing Swift programmatic UI and MVVM. I tried to follow the following tutorial: https://medium.com/flawless-app-stories/mvvm-in-ios-swift-aa1448a66fb4 and also I added a tab bar and settings view controller just for practice. My issue is in my HomeView controller I see the data is being obtained by my table is displaying empty cells.
HomeViewController:
import Foundation
import UIKit
class HomeViewController: UIViewController{
private var employeeViewModel: EmployeeViewModel!
var employeeTableView: UITableView = UITableView()
private var datasource: EmployeeTableViewDataSource<EmployeeTableViewCell, EmployeeData>!
override func viewDidLoad() {
// some code here
configure()
callToViewModelForUIUpdate()
}
func configure(){
self.title = "Home"
self.view.backgroundColor = .lightGray
let width = self.view.frame.width
let navigationBar: UINavigationBar = UINavigationBar(frame: CGRect(x: 0, y: 0, width: width, height: 44))
let navigationItem = UINavigationItem(title: "Home")
navigationBar.setItems([navigationItem], animated: false)
self.view.addSubview(navigationBar)
let screenSize: CGRect = UIScreen.main.bounds
// get the screens width and height
let screenWidth = screenSize.width
let screenHeight = screenSize.height
// set the table to the screens width and height
employeeTableView.frame = CGRect(x: 0,y: 0, width: screenWidth, height: screenHeight)
//register the cell to the table
employeeTableView.register(EmployeeTableViewCell.self, forCellReuseIdentifier: "EmployeeTableViewCell")
//add the table to the view
self.view.addSubview(employeeTableView)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
}
func callToViewModelForUIUpdate(){
self.employeeViewModel = EmployeeViewModel()
self.employeeViewModel.bindEmployeeViewModelToController = {
self.updateDataSource()
}
}
func updateDataSource(){
self.datasource = EmployeeTableViewDataSource(cellIdentifier: "EmployeeTableViewCell", items: self.employeeViewModel.empData.data, configureCell: { (cell, evm) in
cell.employeeIdLabel.text = "\(evm.id)"
cell.employeeNameLabel.text = evm.employeeName
})
DispatchQueue.main.async {
self.employeeTableView.dataSource = self.datasource
self.employeeTableView.reloadData()
}
}
}
EmployeeViewModel:
import Foundation
class EmployeeViewModel: NSObject{
private var apiService: APIService!
private(set) var empData: Employee!{
didSet{
self.bindEmployeeViewModelToController()
}
}
var bindEmployeeViewModelToController: (() -> ()) = {}
override init(){
super.init()
self.apiService = APIService()
callFuncToGetEmpData()
}
func callFuncToGetEmpData(){
self.apiService.apiToGetEmployeeData{(empData) in
self.empData = empData
}
}
}
EmployeeTableViewCell:
import Foundation
import UIKit
class EmployeeTableViewCell: UITableViewCell{
var employeeIdLabel = UILabel()
var employeeNameLabel = UILabel()
var employee: EmployeeData?{
didSet{
employeeIdLabel.text = String(describing: employee?.id)
employeeNameLabel.text = employee?.employeeName
}
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
APIService:
import Foundation
class APIService: NSObject{
private let source = URL(string: "http://dummy.restapiexample.com/api/v1/employees")!
func apiToGetEmployeeData(completion: #escaping (Employee)-> ()){
URLSession.shared.dataTask(with: source){(data, urlResponse, error) in
if let data = data{
let jsonDecoder = JSONDecoder()
let empData = try! jsonDecoder.decode(Employee.self, from: data)
completion(empData)
}
}.resume()
}
}
EmployeeTableViewDataSource:
import Foundation
import UIKit
class EmployeeTableViewDataSource<CELL : UITableViewCell,T> : NSObject, UITableViewDataSource {
private var cellIdentifier : String!
private var items : [T]!
var configureCell : (CELL, T) -> () = {_,_ in }
init(cellIdentifier : String, items : [T], configureCell : #escaping (CELL, T) -> ()) {
self.cellIdentifier = cellIdentifier
self.items = items
self.configureCell = configureCell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! CELL
let item = self.items[indexPath.row]
self.configureCell(cell, item)
return cell
}
}
EmployeeModel:
import Foundation
// MARK: - Welcome
struct Employee: Decodable {
let status: String
let data: [EmployeeData]
}
// MARK: - Datum
struct EmployeeData: Decodable {
let id, employeeSalary, employeeAge: Int
let employeeName: String
let profileImage: String
enum CodingKeys: String, CodingKey {
case id
case employeeName = "employee_name"
case employeeSalary = "employee_salary"
case employeeAge = "employee_age"
case profileImage = "profile_image"
}
}
ViewController:
import UIKit
class ViewController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
configureTabBar()
}
func configureTabBar(){
//createFirstTab view controller
let firstTab = HomeViewController()
// create nav controller to embed view controller in
let navVc = UINavigationController(rootViewController: firstTab)
// set the title and image at the bottom tab bar for the 1st VC
let firstItem = UITabBarItem(title: "Home", image: UIImage(systemName: "pencil"), selectedImage: UIImage(systemName: "pencil"))
//set the item to pertain to the first tab
firstTab.tabBarItem = firstItem
//createSecondTab
let secondTab = SettingsViewController()
let secondNavVC = UINavigationController(rootViewController: secondTab)
let secondItem = UITabBarItem(title: "Settings", image: UIImage(systemName: "More"), selectedImage: UIImage(systemName: "More"))
secondTab.tabBarItem = secondItem
//add the VCs as VCs of the tab bar
self.viewControllers = [navVc, secondNavVC]
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
print("Selected \(viewController.title)")
}
}
Everything populates and navigation is fine but the tableview is just empty.
In the code you posted, I don't see anywhere that you are adding the employeeIdLabel and employeeNameLabel labels to the cell's view hierarchy.
Try using this cell class:
class EmployeeTableViewCell: UITableViewCell{
var employeeIdLabel = UILabel()
var employeeNameLabel = UILabel()
var employee: EmployeeData?{
didSet{
employeeIdLabel.text = String(describing: employee?.id)
employeeNameLabel.text = employee?.employeeName
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
employeeIdLabel.backgroundColor = .green
employeeNameLabel.backgroundColor = .yellow
contentView.addSubview(employeeIdLabel)
contentView.addSubview(employeeNameLabel)
employeeIdLabel.translatesAutoresizingMaskIntoConstraints = false
employeeNameLabel.translatesAutoresizingMaskIntoConstraints = false
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
employeeIdLabel.topAnchor.constraint(equalTo: g.topAnchor),
employeeIdLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor),
employeeIdLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
employeeNameLabel.topAnchor.constraint(equalTo: employeeIdLabel.bottomAnchor, constant: 8.0),
employeeNameLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor),
employeeNameLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
employeeNameLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor),
])
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
Result:
As a side note, make sure you gracefully handle a failed attempt to get the data:
class APIService: NSObject{
private let source = URL(string: "http://dummy.restapiexample.com/api/v1/employees")!
func apiToGetEmployeeData(completion: #escaping (Employee)-> ()){
URLSession.shared.dataTask(with: source){(data, urlResponse, error) in
if let data = data{
let jsonDecoder = JSONDecoder()
do {
let empData = try jsonDecoder.decode(Employee.self, from: data)
completion(empData)
} catch {
print("Failed to get data!")
// show an alert or something...
}
}
}.resume()
}
}

Passing data from UIView to UIViewController using protocol

I'm trying to pass data from UIView contains UITableView to UIViewController but unfortunately, it doesn't work.
Model:
struct Recipes: Codable {
let recipes: [Recipe]
}
struct Recipe: Codable {
let title: String?
let image: String?
let pricePerServing: Double?
let readyInMinutes, servings: Int?
}
The data should be passed from this view
HomeView:
protocol RecipesDetailsSelectActionDelegate: class {
func recipeDetails(recipeTitle: String)
}
class HomeView: UIView {
var recipes: Recipes?
var recipesDetails = [Recipe]()
let indicator = ActivityIndicator()
weak var homeViewDidSelectActionDelegate: HomeViewDidSelectActionDelegate?
weak var recipeDetailsViewSelectActionDelegate: RecipesDetailsSelectActionDelegate?
override init( frame: CGRect) {
super.init(frame: frame)
layoutUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var foodTableView: UITableView = {
let foodTableView = UITableView()
foodTableView.translatesAutoresizingMaskIntoConstraints = false
foodTableView.backgroundColor = .customVeryLightGray()
foodTableView.delegate = self
foodTableView.dataSource = self
foodTableView.register(HomeTableViewCell.self, forCellReuseIdentifier: "HomeTableViewCell")
foodTableView.rowHeight = UITableView.automaticDimension
foodTableView.estimatedRowHeight = 100
foodTableView.showsVerticalScrollIndicator = false
foodTableView.separatorStyle = .none
return foodTableView
}()
}
extension HomeView: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return recipesDetails.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
let url = URL(string: recipesDetails[indexPath.row].image ?? "Error")
cell.foodImage.kf.setImage(with: url)
cell.foodTitle.text = recipesDetails[indexPath.row].title
if let readyInMin = recipesDetails[indexPath.row].readyInMinutes {
cell.cookingTimeInfoLabel.text = "\(readyInMin) Minutes"
}
if let pricePerServing = recipesDetails[indexPath.row].pricePerServing {
cell.priceInfoLabel.text = String(format: "%.2f", pricePerServing / 100)
}
if let serving = recipesDetails[indexPath.row].servings {
cell.servesInfoLabel.text = "\(serving)"
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
recipeDetailsViewSelectActionDelegate?.recipeDetails(recipeTitle: recipesDetails[indexPath.row].title ?? "Error")
}
}
HomeViewController:
class HomeViewController: UIViewController {
lazy var mainView: HomeView = {
let view = HomeView(frame: self.view.frame)
view.recipeDetailsViewSelectActionDelegate = self
return view
}()
override func loadView() {
super.loadView()
view = mainView
}
}
extension HomeViewController: RecipesDetailsSelectActionDelegate {
func recipeDetails(recipeTitle: String) {
let vc = RecipesDetailsViewController()
vc.mainView.recipeTitle = recipeTitle
self.show(vc, sender: nil)
}
}
The data should be passed to this view
RecipesDetailsView:
class RecipesDetailsView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
layoutUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var recipeTitle: String?
lazy var tableView: UITableView = {
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 100
tableView.showsVerticalScrollIndicator = false
tableView.separatorStyle = .none
tableView.backgroundColor = .white
tableView.register(IngredientsTableViewCell.self, forCellReuseIdentifier: "IngredientsTableViewCell")
return tableView
}()
extension RecipesDetailsView: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "IngredientsTableViewCell", for: indexPath) as! IngredientsTableViewCell
cell.recipeTitleLabel.text = recipeTitle
return cell
}
}
RecipesDetailsViewController:
class RecipesDetailsViewController: UIViewController {
lazy var mainView: RecipesDetailsView = {
let view = RecipesDetailsView(frame: self.view.frame)
view.backgroundColor = .white
return view
}()
override func loadView() {
super.loadView()
view = mainView
}
}

How to display the data from JSON in tableviewcell

I need to list the data in the tableview.
{"data":[{"id":33,"question":"sqws","options":["option ,A","option ,A"],"button_type":"2","option_count":"2"}]}
This is my api pattern .
My code as follow:-
Model:-
class Model: NSObject {
var question:String?
var options:[String]?
init?(dictionary :JSONDictionary) {
guard
let question = dictionary["question"] as? String
else {
return
}
if let options = dictionary["options"] as? [String]{
print(options)
self.options = options
}
self.question = question
}
}
datasourceModel:-
class DataSourceModel: NSObject {
var dataListArray:Array<Model>? = []
init(array :Array<[String:Any]>?) {
super.init()
var newArray:Array<[String:Any]> = []
if array == nil{
// newArray = self.getJsonDataStored22()
}
else{
newArray = array!
}
var datalist:Array<Model> = []
for dict in newArray{
let model = Model(dictionary: dict)
datalist.append(model!)
}
self.dataListArray = datalist
}
}
viewmodel:-
class ViewModel: NSObject {
var datasourceModel:DataSourceModel
var filteredListArray:Array<DataSourceModel>? = []
init(withdatasource newDatasourceModel:DataSourceModel) {
datasourceModel = newDatasourceModel
print(datasourceModel.dataListArray)
}
func numberOfSections(tableView: UITableView) -> Int{
print((datasourceModel.dataListArray?.count)!)
return (datasourceModel.dataListArray?.count)!
}
func titleForHeaderInSection(atsection section: Int) -> ListModel {
return datasourceModel.dataListArray![section]
}
func numberOfRowsInSection(section:Int) -> Int {
return (datasourceModel.dataListArray?.count)!
}
func datafordisplay(atindex indexPath: IndexPath) -> Model{
//print(datasourceModel.dataListArray![indexPath.row])
return datasourceModel.dataListArray![indexPath.row]
}
func loadData(completion :#escaping (_ isSucess:Bool) -> ()){
loadFromWebserviceData { (newDataSourceModel) in
if(newDataSourceModel != nil)
{
self.datasourceModel = newDataSourceModel!
completion(true)
}
else{
completion(false)
}
}
}
func loadFromWebserviceData(completion :#escaping (DataSourceModel?) -> ()){
Alamofire.request("http://www.example.com").validate(statusCode: 200..<300).validate(contentType: ["application/json"]).responseJSON{ response in
print(response)
switch response.result{
case .success(let data):
print("success",data)
let result = response.result
print(result)
if let wholedata = result.value as? [String:Any]{
print(wholedata)
if let data = wholedata["data"] as? Array<[String:Any]>{
print(data)
print(response)
let newDataSource:DataSourceModel = DataSourceModel(array: data)
completion(newDataSource)
}
}
case .failure(let encodingError ):
print(encodingError)
// if response.response?.statusCode == 404{
print(encodingError.localizedDescription)
completion(nil)
}
}}
}
viewcontroller:-
class ViewController: UIViewController ,UITableViewDelegate,UITableViewDataSource{
private var reviewViewModel :ViewModel!
var nameArray = [String]()
#IBOutlet weak var tableview: UITableView!
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?, withViewModel viewModel:ViewModel) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
reviewViewModel = viewModel
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
nameArray = ["Home","Message","Map","Setting"]
reviewViewModel.loadData { (isSuccess) in
if(isSuccess == true)
{
self.tableview.reloadData()
}
else{
self.viewDidLoad()
}
// self.viewDidLoad()
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return (reviewViewModel.datasourceModel.dataListArray?.count)!
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let identifier = "HeaderCell"
var headercell: =questionheader! = tableView.dequeueReusableCell(withIdentifier: identifier) as? = questionheader
if headercell == nil {
tableView.register(UINib(nibName: "questionheader", bundle: nil), forCellReuseIdentifier: identifier)
headercell = tableView.dequeueReusableCell(withIdentifier: identifier) as? questionheader
}
headercell.setReviewData(reviews:reviewViewModel.titleForHeaderInSection(atsection:section))
headercell.setReviewData(reviews:reviewViewModel.datafordisplay(atindex: section))
return headercell
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// 2
//return 1
return reviewViewModel.numberOfRowsInSection(section: section)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "Cell"
var cell: NH_QuestionListCell! = tableView.dequeueReusableCell(withIdentifier: identifier) as? NH_QuestionListCell
if cell == nil {
tableView.register(UINib(nibName: "QuestionListCell", bundle: nil), forCellReuseIdentifier: identifier)
cell = tableView.dequeueReusableCell(withIdentifier: identifier) as? QuestionListCell
}
cell.setReviewData(reviews:reviewViewModel.datafordisplay(atindex: indexPath))
return cell
}
}
questionheader:-
class questionheader: UITableViewCell {
#IBOutlet weak var lblName: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
func setReviewData(reviews:QuestionListModel)
{
self.lblName.text = reviews.question
print(self.lblName.text)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
QuestionListCell:-
class QuestionListCell: UITableViewCell {
#IBOutlet weak var question: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
func setReviewData(reviews:QuestionListModel)
{
print(reviews.options)
print(reviews.options?[0])
self.question.text = reviews.options?[0]
print(self.question.text)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
So,here i got questions at sections .But according to my api i need to list the data contain in key option.
{"data":[{"id":33,"question":"sqws","options":["option ,A","option ,B"],"button_type":"2","option_count":"2"}]}
According to this i got the question in section and i need to display the options in tableviewcell That means option ,A and option ,B should display in the tableviewcell.How to do?
Just like thisJust like this
First thing is:
You should return the option count in this method
func numberOfRowsInSection(section:Int) -> Int {
let objectAtSection: [String: String] = datasourceModel.dataListArray?[section]
return Int(objectAtSection["option_count"])!
}
second, for showing the options in cell, update this method.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "Cell"
var cell: NH_QuestionListCell! = tableView.dequeueReusableCell(withIdentifier: identifier) as? NH_QuestionListCell
if cell == nil {
tableView.register(UINib(nibName: "QuestionListCell", bundle: nil), forCellReuseIdentifier: identifier)
cell = tableView.dequeueReusableCell(withIdentifier: identifier) as? QuestionListCell
}
let objectAtSection: [String: String] = datasourceModel.dataListArray?[section]
let optionsAtRow: [String] = objectAtSection["options"]
cell.question.text = optionsAtRow[indexPath.row]
return cell
}
Try and share the results

can we pass data from table cell to table view where both are xib files?

I want to pass table cell data (xib file) to table view (also a xib file). I have tried passing the data using the following piece of code but did not get an appropriate result.
PropertyCell.swift
import UIKit
class PropertyCell: UITableViewCell {
#IBOutlet weak var propertyCodeLbl: UILabel!
#IBOutlet weak var addressLbl: UILabel!
}
I have attached the screenshot for PropertyCell below -
PropertyCell.xib
PropertyCell.xib file
PropertyTableVC.swift
import UIKit
import Alamofire
class PropertyTableVC: UITableViewController {
#IBOutlet var propertyTabel: UITableView!
let URL_Landlord_Property_List = "http://127.0.0.1/source/api/LandlordPropertyList.php"
var count: Int = 0
var landlordPropertyArray: [PropertyList]? = []
override func viewDidLoad() {
super.viewDidLoad()
fetchData()
propertyTabel.dataSource = self
propertyTabel.delegate = self
let nibName = UINib(nibName: "PropertyCell", bundle:nil)
self.propertyTabel.register(nibName, forCellReuseIdentifier: "Cell")
}
func fetchData(){
let urlRequest = URLRequest(url: URL(string: URL_Landlord_Property_List)!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if error != nil{
print(error!)
return
}
print(data!)
self.landlordPropertyArray = [PropertyList]()
self.count = (self.landlordPropertyArray?.count)!
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]
if let datafromjson = json["landlords_property_list"] as? [[String: AnyObject]] {
print(datafromjson)
for data in datafromjson{
var property = PropertyList()
if let id = data["ID"] as? Int,let code = data["Code"] as? String, let address1 = data["Address"] as? String
{
property.id = id
property.code = code
property.address1 = address1
}
self.landlordPropertyArray?.append(property)
}
print(self.landlordPropertyArray)
}
DispatchQueue.main.async {
self.propertyTabel.reloadData()
}
}catch let error {
print(error)
}
}
task.resume()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (landlordPropertyArray?.count)!
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Configure the cell...
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! PropertyCell
cell.propertyCodeLbl.text = self.landlordPropertyArray?[indexPath.item].code
cell.addressLbl.text = self.landlordPropertyArray?[indexPath.item].address1
return cell
}
}
Attached the screenshot for Property Table below -
PropertyTableVC.xib
PropertyTableVC.xib file
Your TableViewCell :
import UIKit
protocol yourProtocolName { //add protocol here
func getDataFromTableViewCellToViewController (sender : self) //* as you need to pass the table view cell, so pass it as self
}
class PropertyCell: UITableViewCell {
#IBOutlet weak var propertyCodeLbl: UILabel!
#IBOutlet weak var addressLbl: UILabel!
var delegate : yourProtocolName? //set a delegate
override func awakeFromNib() {
super.awakeFromNib()
if delegate != nil {
delegate.getDataFromTableViewCellToViewController(sender :self) *
}
}
}
And your ViewController :
import UIKit
import Alamofire
class PropertyTableVC: UITableViewController,yourProtocolName { //conform to the protocol you created in tableViewCell
#IBOutlet var propertyTabel: UITableView!
let URL_Landlord_Property_List = "http://127.0.0.1/source/api/LandlordPropertyList.php"
var count: Int = 0
var landlordPropertyArray: [PropertyList]? = []
override func viewDidLoad() {
super.viewDidLoad()
fetchData()
propertyTabel.dataSource = self
propertyTabel.delegate = self
let nibName = UINib(nibName: "PropertyCell", bundle:nil)
self.propertyTabel.register(nibName, forCellReuseIdentifier: "Cell")
}
func fetchData(){
let urlRequest = URLRequest(url: URL(string: URL_Landlord_Property_List)!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if error != nil{
print(error!)
return
}
print(data!)
self.landlordPropertyArray = [PropertyList]()
self.count = (self.landlordPropertyArray?.count)!
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]
if let datafromjson = json["landlords_property_list"] as? [[String: AnyObject]] {
print(datafromjson)
for data in datafromjson{
var property = PropertyList()
if let id = data["ID"] as? Int,let code = data["Code"] as? String, let address1 = data["Address"] as? String
{
property.id = id
property.code = code
property.address1 = address1
}
self.landlordPropertyArray?.append(property)
}
print(self.landlordPropertyArray)
}
DispatchQueue.main.async {
self.propertyTabel.reloadData()
}
}catch let error {
print(error)
}
}
task.resume()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (landlordPropertyArray?.count)!
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Configure the cell...
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! PropertyCell
cell.propertyCodeLbl.text = self.landlordPropertyArray?[indexPath.item].code
cell.addressLbl.text = self.landlordPropertyArray?[indexPath.item].address1
return cell
}
func getDataFromTableViewCellToViewController(sender :UITableViewCell) {
//make a callback here
}
}
(*) Marked fields are updated code
Call fetchData() function after tableview delegate and datasource assigning
propertyTabel.dataSource = self
propertyTabel.delegate = self
Updated answer is
Create Cell Class like this
import UIKit
class YourTableViewCell: UITableViewCell {
#IBOutlet weak var profileImageView: UIImageView!
#IBOutlet weak var userNameLabel: UILabel!
#IBOutlet weak var timeDateLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
self.backgroundColor = UIColor.tableViewBackgroundColor()
self.selectionStyle = .none
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
class func cellForTableView(tableView: UITableView, atIndexPath indexPath: IndexPath) -> YourTableViewCell {
let kYourTableViewCell = "YourTableViewCellIdentifier"
tableView.register(UINib(nibName:"RRLoadQuestionsTableViewCell", bundle: Bundle.main), forCellReuseIdentifier: kYourTableViewCell)
let cell = tableView.dequeueReusableCell(withIdentifier: kYourTableViewCell, for: indexPath) as! YourTableViewCell
return cell
}
}
Then create UIViewController Class and place UITableView on it and link with outlet
class YourViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UITextViewDelegate {
#IBOutlet weak var tableView: UITableView!
var dataSource = [LoadYourData]()
// MARK: - Init & Deinit
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: "YourViewController", bundle: Bundle.main)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
NotificationCenter.default.removeObserver(self)
}
override func viewDidLoad() {
super.viewDidLoad()
setupViewControllerUI()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
}
// MARK: - UIViewController Helper Methods
func setupViewControllerUI() {
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension
tableView.delegate = self
tableView.dataSource = self
loadData()
}
func loadData() {
// Write here your API and reload tableview once you get response
}
// MARK: - UITableView Data Source
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = YourTableViewCell.cellForTableView(tableView: tableView, atIndexPath: indexPath)
// assign here cell.name etc from dataSource
return cell
}

Resources