I have a tableview loads data from php json using Alamofire, it loads perfect on the tableview, now I tried to pass the data to a second viewController to show more details, I faced this error which says Cannot find the data in scope
func getUsers() {
AF.request(SiteUrl).validate().responseJSON { response in
switch response.result {
case .success:
print("Validation Successful)")
if let json = response.data {
do{
let jsonData = try JSON(data: json)
self.data = jsonData.arrayValue
self.tableView.reloadData() // we are already on the main thread
//print("DATA PARSED: \(jsonData)")
}
catch {
print("JSON Error", error)
}
}
case .failure(let error):
print(error)
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let customCell = tableView.dequeueReusableCell(withIdentifier: TableViewCell.identifier, for: indexPath) as! TableViewCell
let item = data[indexPath.row]
customCell.AdTitle?.text = item["title"].string
customCell.AdDate?.text = item["time"].string
let imageUrl = item["document"].string
let url = NSURL(string:("https://qateef-ads.co/uploads/" + imageUrl!))
customCell.AdImage.sd_setImage(with: url as URL?, placeholderImage: UIImage(named: "placeholder.png"))
return customCell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(withIdentifier: "DetailsViewController") as? DetailsViewController
self.navigationController?.pushViewController(vc!, animated: true)
vc?.imageView = UIImage(named: url)
vc?.AdTitle = item["title"].string
}
how can I pass the data from to the second view?
I'm not sure I understood what exactly is your problem. But if you need to access that data on the second view controller, the simplest way would be to inject that data into the DetailsViewController you're instantiating when the cell is selected.
But first you need to create that property:
class DetailsViewController: UIViewController {
var data: JSON?
...
}
Then when instantiating that view controller:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let vc = storyboard?.instantiateViewController(withIdentifier: "DetailsViewController") as? DetailsViewController else { return }
vc.data = data[indexPath.row]
self.navigationController?.pushViewController(vc, animated: true)
}
Now you can use that data on your DetailsViewController to pull any information you need from the json response:
class DetailsViewController: UIViewController {
...
override viewDidLoad() {
super.viewDidLoad()
let title = data["title"]
...
}
}
You pass your alamofire result to "data" variable. Therefore, you should use "data" while passing the value to another VC. In your case, it could be like;
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(withIdentifier: "DetailsViewController") as? DetailsViewController
vc?.imageView = UIImage(named: url)
let item = data[indexPath.row]
vc?.AdTitle = item["title"].string
self.navigationController?.pushViewController(vc!, animated: true)
}
Ok
**
if let json = response.data {
let JSON22 = json as! NSDictionary
do{
let jsonData = try JSON(data: JSON22)
self.data = jsonData.arrayValue
self.tableView.reloadData() // we are already on the main thread
//print("DATA PARSED: \(jsonData)")
}
catch {
print("JSON Error", error)
}
**
If you want to pass your data from Controller1 to Controller2.
In Controller1
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = data[indexPath.row]
let vc = storyboard?.instantiateViewController(withIdentifier: "DetailsViewController") as? DetailsViewController
vc!.dictGetData = item
self.navigationController?.pushViewController(vc!, animated: true)
}
In Controller2
Create a Dictionary variable
var dictGetData:NSDictionary!
then print dictionary on view did load
override func viewDidLoad() {
super.viewDidLoad()
print(dictGetData as Any)
}
Related
I am not able to display the JSON data in my table view. I don't know why. I tried to get the JSON data, but I am not able to display it on screen in a table format.
This is the model:
class PastTripsVC: UIViewController {
var past = [PastRide]()
#IBOutlet weak var mTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let nibCell = UINib(nibName: "PastTableView", bundle: nil)
mTable.register(nibCell, forCellReuseIdentifier: "cell")
apiCalling()
}
func apiCalling(){
if let url = URL(string: "https://pincood.com/pincood/public/api/user/trips") {
var request = URLRequest(url: url)
request.allHTTPHeaderFields = [
"Content-Type": "application/json",
"Session": "fb4e7f9b-0f31-4709-",
"AUthorization":"<some key>"
]
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard error == nil else { return }
guard let data = data else { return }
do{
let codabledata = try JSONDecoder().decode([PastRide].self, from: data)
print(codabledata)
DispatchQueue.main.async {
self.past = codabledata
self.mTable.reloadData()
}
} catch {
print(error)
}
}.resume()
}
}
In the extension we try:
extension PastTripsVC : UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return past.count
print(past.count)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! PastTableView
cell.usernm.text = past[indexPath.row].provider.firstName
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let details : PastDetailView = self.storyboard?.instantiateViewController(withIdentifier: "PastDetailView") as! PastDetailView
navigationController?.pushViewController(details, animated: true)
}
}
You need to set table view's delegate and datasource properties in viewDidLoad.
Update your viewDidLoad to look like this:
override func viewDidLoad() {
super.viewDidLoad()
let nibCell = UINib(nibName: "PastTableView", bundle: nil)
mTable.register(nibCell, forCellReuseIdentifier: "cell")
mTable.delegate = self
mTable.datasource = self
apiCalling()
}
The view shows duplicate rows for each record in CoreData which keep multiplying on each reload. The code is as under.
What happen is whenever I add record then I view record it shows me the record. Then I click back for the homepage after that when I click on view record I see the copy of same record. So now I have 2 same records. Can anyone please help me with and I think the problem is in table view so here is my table view controller code
import UIKit
import CoreData
var Rec = [Records]()
class TableViewController: UITableViewController {
var firstLoad = true
func nondel() -> [Records]
{
var nodellist = [Records]()
for note in Rec
{
if(note.del == nil)
{
nodellist.append(note)
}
}
return nodellist
}
override func viewDidLoad() {
super.viewDidLoad()
if(firstLoad)
{
firstLoad = false
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context:NSManagedObjectContext = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Records")
do{
let results: NSArray = try context.fetch(request) as NSArray
for result in results {
let note = result as! Records
Rec.append(note)
}
}
catch
{
print("Fetch Failed")
}
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell") as! TableViewCell
let thisrec: Records!
thisrec = nondel()[indexPath.row]
cell.idLB.text = thisrec.id
cell.nameLB.text = thisrec.name
cell.lastLB.text = thisrec.last
cell.genderLB.text = thisrec.gender
cell.ageLB.text = thisrec.age
cell.addressLB.text = thisrec.address
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return nondel().count
}
override func viewDidAppear(_ animated: Bool) {
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
self.performSegue(withIdentifier: "editNote", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "editNote")
{
let indexPath = tableView.indexPathForSelectedRow!
let recDetail = segue.destination as? AddViewController
let selectedCell: Records!
selectedCell = nondel()[indexPath.row]
recDetail!.selectedCell = selectedCell
tableView.deselectRow(at: indexPath, animated: true)
}
}
}
Your code is unbelievable cumbersome.
First of all never declare a data source outside of any class.
Second of all never use a function to build an array as table view data source.
Third of all firstRun is pointless because viewDidLoad is called only once anyway.
Fourth of all rather than filtering the received records manually apply a predicate to the fetch request
Further it's highly recommended to name Core Data entities always in singular form (Record) and to use the specific generic fetch request of this entity.
class TableViewController: UITableViewController {
var records = [Record]()
override func viewDidLoad() {
super.viewDidLoad()
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let request : NSFetchRequest<Record> = Record.fetchRequest()
request.predicate = NSPredicate(format: "del != nil")
do {
records = try context.fetch(request)
} catch { print("Fetch Failed", error) }
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell") as! TableViewCell
let thisrec = records[indexPath.row]
cell.idLB.text = thisrec.id
cell.nameLB.text = thisrec.name
cell.lastLB.text = thisrec.last
cell.genderLB.text = thisrec.gender
cell.ageLB.text = thisrec.age
cell.addressLB.text = thisrec.address
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return records.count
}
...
Hi everybody I'm new to Swift and I need help.
So I have three different JSON that will show in different moments, The first JSON has been loading perfectly, but when I clicking on the item to reload another JSON and show the detail nothing appears.
I'm confused about details:
Need I have three differents table Views for each JSON? or the only one is enough?
When I working with data (JSON) need I use a specific function to prepare the new JSON that will appear as "prepare"?
In my project I have:
Two view controllers: ViewController(default) and DetailViewController.
In my Main.Storyboard: Tab Bar Controller --> Navigation --> Table View
The code of the first View controller:
import UIKit
class ViewController: UITableViewController {
var categories = [Category]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let urlString = "https://www.themealdb.com/api/json/v1/1/categories.php"
if let url = URL(string: urlString) {
if let data = try? Data(contentsOf: url) {
parse(json: data)
} else {
print("error connecting")
}
}
}
func parse(json: Data) {
let decoder = JSONDecoder()
print("parse called")
do {
let jsonCategories = try decoder.decode(Categories.self, from: json)
categories = jsonCategories.categories
tableView.reloadData()
} catch {
print("error parsin: \(error)")
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return categories.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let category = categories[indexPath.row]
cell.textLabel?.text = category.idCategory
cell.detailTextLabel?.text = category.strCategoryDescription
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let newViewController = DetailViewController()
self.navigationController?.pushViewController(newViewController, animated: true)
}
}
The code of the second view controller:
import UIKit
import WebKit
class DetailViewController: UIViewController {
var webView: WKWebView!
var meals = [Meal]()
override func loadView() {
webView = WKWebView()
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
let urlString = "https://www.themealdb.com/api/json/v1/1/filter.php?c=Beef"
if let url = URL(string: urlString) {
if let data = try? Data(contentsOf: url) {
parse(json: data)
} else {
print("error connecting")
}
}
}
func parse(json: Data) {
let decoder = JSONDecoder()
print("parse called")
do {
let jsonMeals = try decoder.decode(Meals.self, from: json)
meals = jsonMeals.meals
print(String(format:"read %d meals", meals.count))
} catch {
print("error parsin: \(error)")
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return meals.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let meal = meals[indexPath.row]
cell.textLabel?.text = meal.idMeal
cell.detailTextLabel?.text = meal.strMeal
return cell
}
}
The function parse(json: Data) on DetailViewController does not call tableView.reloadData() so the new data cannot be loaded to UITableView
Need I have three differents table Views for each JSON? or the only one is enough?
If you're loading different JSON files but want to show in the same view. You only need 1 UITableViewCell.
When you load & decode JSON, consider moving it to background queue to avoid blocking main thread.
var list = [String]()
#IBOutlet weak var TableView: UITableView!
override func viewDidLoad() {
self.title = "Routines"
TableView.delegate = self
TableView.dataSource = self
super.viewDidLoad()
}
//refresh view when going back to this viewcontroller
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("Test Worked")
TableView.reloadData()
}
//generating rows
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return (list.count)
}
//returning text in UITableViewCell
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = UITableViewCell(style:
UITableViewCell.CellStyle.default, reuseIdentifier:
"prototype1")
print("printed")
cell.textLabel?.text = list[indexPath.row]
return cell
}
//deleting rows
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath)
{
if editingStyle == UITableViewCell.EditingStyle.delete{
deleteAllData("ToDo")
self.list.remove(at: indexPath.row)
TableView.reloadData()
}
}
#IBAction func didAdd() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(identifier: "addRoutinePage")as! addRoutinePage
self.navigationController?.pushViewController(vc, animated: true)
}
//function to get data from core data
func getData()
{
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "ToDo")
request.returnsObjectsAsFaults = false
do{
//fetching data from coredata
let result = try context.fetch(request)
for data in result as! [NSManagedObject]
{
//appending the list from the value in coredata (attribute) or entity
self.list.append(data.value(forKey: "title")as! String)
print("append success")
}
}catch {
print("failed")
}
}
What is wrong with my code? Everything seems to work except for the UITableViewCell, the print command I entered just to check if the function is executed didn't even work. I tried TableView.reloadData() but it still didn't work. Logically if the problem is with the public function or data source or delegate it won't even generate any rows, but rows are generated. I tried resizing the cell height size too but it still won't work. Please help!
There are a few errors with the code:
You need to reload once the data fetching from CoreData is complete.
func getData() {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "ToDo")
request.returnsObjectsAsFaults = false
do {
let result = try context.fetch(request)
for data in result as! [NSManagedObject] {
self.list.append(data.value(forKey: "title")as! String)
}
self.TableView.reloadData()
} catch {
print(error)
}
}
Also, don't forget to call the getData function.
override func viewDidLoad() {
super.viewDidLoad()
title = "Routines"
TableView.delegate = self
TableView.dataSource = self
getData()
}
I have a TabBar with various Tabs in my RestaurantApp, When I click the addToCart and goes to the CartViewContorller, the added item don't show I have to relaunch the App to see the item there. I have seen similar questions with various answer on this question here but non of the solutions seems to work in my case I don't really know whats wrong. Below is my code for the CartViewContorller I want to reload tableview anytime it is loaded. Thanks all for your help
import UIKit
import Alamofire
import os.log
class CartViewController: UITableViewController {
var cartData = [CartResponse.Cart]()
override func viewDidLoad() {
super.viewDidLoad()
cart()
tableView.delegate = self
tableView.dataSource = self
let nib = UINib(nibName: "viewCartCell", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "cartCustomCell")
let footerView = UIView()
footerView.backgroundColor = UIColor.red
footerView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 60)
tableView.tableFooterView = footerView
}
override func viewDidAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tableView.reloadData()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cartData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell: CartTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "cartCustomCell", for: indexPath) as? CartTableViewCell else {
os_log("Dequeue cell isn't an instance of CustomTableCell", log: .default, type: .debug)
fatalError()
}
cell.recipeNameLbl?.text = cartData[indexPath.row].recipeName
cell.restaurantNameLbl?.text = cartData[indexPath.row].restaurantName
cell.addtionalNoteLbl?.text = cartData[indexPath.row].additionalNote
cell.quantityLbl?.text = cartData[indexPath.row].recipeQuantity
cell.totalLbl?.text = cartData[indexPath.row].recipePrice
cell.totalCostLbl?.text = cartData[indexPath.row].totalCost
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
guard editingStyle == .delete else {return}
//getting userId from defaults
let CartId = cartData[indexPath.row].cartId
let cartId = CartId
//creating parameters for the post request
let parameters: Parameters=[
"cartId":Int(cartId)
]
//Constant that holds the URL for web service
let URL_SELECT_FROM_CART = "http://localhost:8888/restaurant/deleteFromCart.php?"
Alamofire.request(URL_SELECT_FROM_CART, method: .post, parameters: parameters).responseJSON {
response in
//printing response
print(response)
}
cartData.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
}
//Fetching from Cart Method
func cart(){
//getting userId from defaults
let defaultValues = UserDefaults.standard
let userId = defaultValues.string(forKey: "userid")
//creating parameters for the post request
let parameters: Parameters=[
"userId":Int(userId!)!
]
//Constant that holds the URL for web service
let URL_SELECT_FROM_CART = "http://localhost:8888/restaurant/cart.php?"
Alamofire.request(URL_SELECT_FROM_CART, method: .post, parameters: parameters).responseJSON {
(response) in
let result = response.data
do{
let decoder = JSONDecoder()
let downloadedCart = try decoder.decode(CartResponse.self, from: result!)
self.cartData = downloadedCart.cartItem
DispatchQueue.main.async {
self.tableView.reloadData()
}
}catch {
print(error)
}
}.resume()
}
}
You can use :
import UserNotifications
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "loadCart"), object: nil)
see more in this answer
You have to call cart() this method in viewWillAppear instead of calling viewDidload
override func viewWillAppear(_ animated: Bool) {
self.cart()
}