Remote Data won't show on tableView - swift

I'm clueless as to what is wrong. My console doesn't give me any errors, my code seems fine but nothing is showing up. Could someone check my code, see why it doesn't want to work? My tableView is connected with its delegates and source. Not sure what is the problem.
Here is my code:
private let cellIdentifier = "cell"
private let apiURL = "api link"
class TableView: UITableViewController {
//TableView Outlet
#IBOutlet weak var LegTableView: UITableView!
//API Array
var legislatorArray = [congressClass]()
func getLegislators (fromSession session: NSURLSession) {
//Calling url
if let jsonData = NSURL(string: apiURL) {
// Requesting url
let task = session.dataTaskWithURL(jsonData) {(data, response, error) -> Void in
//Check for errors
if let error = error {print(error)
} else {
if let http = response as? NSHTTPURLResponse {
if http.statusCode == 200 {
//Getting data
if let data = data {
do {
let legislatorData = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers)
//Get API data
if let getData = legislatorData as? [NSObject:AnyObject],
findObject = getData["results"] as? [AnyObject]{
//Return data
for cellFound in findObject{
if let nextCell = cellFound["results"] as? [NSObject:AnyObject],
name = nextCell["first_name"] as? String,
lastName = nextCell["last_name"] as? String,
title = nextCell["title"] as? String,
partyRep = nextCell["party"] as? String,
position = nextCell ["position"] as? String,
id = nextCell ["bioguide_id"] as? String
{
//Add data to array
let addData = congressClass(name: name, lastName: lastName, title: title, party: partyRep, position: position, bioID: id)
self.legislatorArray.append(addData)
}
}//end cellFound
//Adding data to table
dispatch_async(dispatch_get_main_queue()) { () -> Void in
self.tableView.reloadData()
}
}
}
//end do
catch {print(error)}
}//end data
}//end statusCode
}//end http
}//else
}//end task
//Run code
task.resume()
}//end jsonData
}
override func viewDidLoad() {
super.viewDidLoad()
let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration()
let urlSession = NSURLSession(configuration: sessionConfig)
getLegislators(fromSession: urlSession)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
//TableView Rows
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return legislatorArray.count
//return 5
}
//Cell Configuration
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! CellTableView
cell.lesName?.text = legislatorArray[indexPath.row].name + " " + legislatorArray[indexPath.row].lastName
cell.lesTitle?.text = legislatorArray[indexPath.row].title
cell.lesParty?.text = legislatorArray[indexPath.row].party
//These tests worked fine.. the tableView is working. But the data doesn't seem to pass.
//cell.lesName.text = "Name" + " " + "lastName"
//cell.lesTitle.text = "Title goes here"
//cell.lesParty.text = "D"
return cell
}
}

You're not reloading the tableView
The problem is in this piece of code
//-----------------------------
//New empty array for api data
var indexPath:[NSIndexPath] = []
//Adding data to new array
for i in 0..<self.legislatorArray.count{
let secondIndexPath = NSIndexPath(forRow: i, inSection: 0)
indexPath.append(secondIndexPath)
}
//Adding data to table
dispatch_async(dispatch_get_main_queue()) { () -> Void in
self.tableView.insertRowsAtIndexPaths(indexPath, withRowAnimation: .Left)
}
You don't need any of that. You can just reload the tableView as follows:
//Adding data to table
dispatch_async(dispatch_get_main_queue()) { () -> Void in
//You only need to reload it and that should do the trick
self.tableView.reloadData()
}
I know you said your tableView is connected to the delegate and dataSource but it's not showing in your code.
You conformed the ViewController to the correct protocols but you need something like this in your viewDidLoad.
self.tableView.deletage = self
self.tableView.dataSource = self
//I don't know if this was a typo but in your cellForRowAtIndexPath you are using CellTableView
let nibName = UINib(nibName: "CellTableView", bundle:nil)
self.tableView.registerNib(nibName, forCellReuseIdentifier: cellIdentifier)
I created an example of a better design for your implementation
This is for the WebService and your Custom Class
https://github.com/phantomon/Stackoverflow/blob/master/SO1/MyTableView/MyTableView/Models/WebServiceManager.swift
This is for the ViewController with your tableView
https://github.com/phantomon/Stackoverflow/blob/master/SO1/MyTableView/MyTableView/ViewController.swift
You just need to modify the UITableViewCell with your custom one.
And of course review your custom class data.

Related

How to get value from the first 3 rows in TableView using swift?

I retrieve data from MySql via PHP file to get users information and scores to load them in a table. I need to get the value of the first 3 users and put them in a Label outside the Table, it is like game leaders list. I attached an image to explain the idea.
Here is the structure code:
import Foundation
protocol HomeModelProtocol: AnyObject {
func itemsDownloaded(items: NSArray)
}
class HomeModel: NSObject, URLSessionDataDelegate {
weak var delegate: HomeModelProtocol!
let urlPath = "https://mywebsite.com/folder/callUserList.php" //this will be changed to the path where service.php lives
func downloadItems() {
let url: URL = URL(string: urlPath)!
let defaultSession = Foundation.URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: url) { (data, response, error) in
if error != nil {
print("Failed to download data")
}else {
print("Data downloaded")
self.parseJSON(data!)
}
}
task.resume()
}
func parseJSON(_ data:Data) {
var jsonResult = NSArray()
do{
jsonResult = try JSONSerialization.jsonObject(with: data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
} catch let error as NSError {
print(error)
}
var jsonElement = NSDictionary()
let users = NSMutableArray()
for i in 0 ..< jsonResult.count
{
jsonElement = jsonResult[i] as! NSDictionary
let user = UsersModel()
//the following insures none of the JsonElement values are nil through optional binding
if let name = jsonElement["name"] as? String,
let email = jsonElement["email"] as? String,
let phoneNumber = jsonElement["phone"] as? String,
let userImage = jsonElement["image"] as? String
{
user.name = name
user.email = email
user.phoneNumber = phoneNumber
user.userImage = userImage
}
users.add(user)
}
DispatchQueue.main.async(execute: { () -> Void in
self.delegate.itemsDownloaded(items: users)
})
}
}
Here is the model:
import Foundation
class UsersModel: NSObject {
//properties
var name: String?
var email: String?
var phoneNumber: String?
var userImage: String?
//empty constructor
override init()
{
}
//construct with #name, #address, #latitude, and #longitude parameters
init(name: String, email: String, phoneNumber: String, userImage: String) {
self.name = name
self.email = email
self.phoneNumber = phoneNumber
self.userImage = userImage
}
//prints object's current state
override var description: String {
return "Name: \(String(describing: name)), Email: \(String(describing: email)), Phone Number: \(String(describing: phoneNumber)), User Image: \(String(describing: userImage))"
}
}
Here is the code in the TableView controller:
var feedItems: NSArray = NSArray()
override func viewDidLoad() {
super.viewDidLoad()
let homeModel = HomeModel()
homeModel.delegate = self
homeModel.downloadItems()
}
func itemsDownloaded(items: NSArray) {
feedItems = items
self.listTableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of feed items
return feedItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Retrieve cell
let cellIdentifier: String = "BasicCell"
let myCell: WinnerTableCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as! WinnerTableCell
// Get the location to be shown
let item: UsersModel = feedItems[indexPath.row] as! UsersModel
// Get references to labels of cell
myCell.lbTextName!.text = item.name
return myCell
}
The data shows in the Table but I have no idea how to fill the 3 label with the 3 first users from the Table.
How can I get these values from the table and pass it to a label in the same ViewController?
Thanks
When adding this code:
if feedItems.count >= 3 {
lblFirstWinner.text = feedItems[0].name // 1st winner
lblSecondWinner.text = feedItems[1].name // 2nd winner
lblThirdWinner.text = feedItems[2].name // 3rd winner
}
it shows error: Value of type 'Any' has no member 'name'
Change itemsDownloaded method as
func itemsDownloaded(items: NSArray) {
feedItems = items
self.listTableView.reloadData()
for (index, user) in items.enumerated() {
let user = user as! UserModel
switch index {
case 0: // 1st winner
lblFirstWinner.text = user.name
case 1: // 2nd winner
lblSecondWinner.text = user.name
case 2: // 3rd winner
lblThirdWinner.text = user.name
}
}
}
OR
Change your HomeModelProtocol method and feedItems type to [UsersModel]
protocol HomeModelProtocol: AnyObject {
func itemsDownloaded(items: [UsersModel]) // Changed
}
var feedItems =[UsersModel]() // Changed
override func viewDidLoad() {
super.viewDidLoad()
let homeModel = HomeModel()
homeModel.delegate = self
homeModel.downloadItems()
}
func itemsDownloaded(items: [UsersModel]) {
feedItems = items
self.listTableView.reloadData()
if feedItems.count >= 3 {
lblFirstWinner.text = feedItems[0].name // 1st winner
lblSecondWinner.text = feedItems[1].name // 2nd winner
lblThirdWinner.text = feedItems[2].name // 3rd winner
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of feed items
return feedItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Retrieve cell
let cellIdentifier: String = "BasicCell"
let myCell: WinnerTableCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as! WinnerTableCell
// Get references to labels of cell
myCell.lbTextName!.text = feedItems[indexPath.row].name // Changed
return myCell
}
Just you need to add a few lines in the below function and your solution will be done.
func itemsDownloaded(items: NSArray) {
feedItems = items
self.listTableView.reloadData()
if feedItems.count >= 3 {
lblFirstWinner.text = feedItems[0].name // 1st winner
lblSecondWinner.text = feedItems[1].name // 2nd winner
lblThirdWinner.text = feedItems[2].name // 3rd winner
}
}
Let me know... is it working for you? and please also refer to #vadian comment on your question.

Saving TableView cell using UserDefaults

I'm trying to get cell of tableView using UserDefaults, but after i reload app it is always empty
This is my Model:
struct Note: Codable {
var title: String
var description: String
}
class Notes {
var stock: [Note] = []
}
View contoller
var model = Notes()
This is how i get data
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "TableViewCell")
tableView.reloadData()
if let fetchedData = UserDefaults.standard.data(forKey: "notes") {
let fetchedBookies = try! PropertyListDecoder().decode([Note].self, from: fetchedData)
print(fetchedBookies)
} else {
model.stock = []
}
}
This is my cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as! TableViewCell
cell.titleOutlet.text = self.model.stock[indexPath.row].title
cell.descriptionOutlet?.text = self.model.stock[indexPath.row].description
return cell
}
How i save data
#IBAction func check(_ sender: Any) {
let newstock = Note(title: "check", description: "check2")
model.stock.append(newstock)
print(model.stock.count)
let bookiesData = try! PropertyListEncoder().encode(model.stock)
UserDefaults.standard.set(bookiesData, forKey: "notes")
tableView.reloadData()
}
Thank you very much!
I recommend you to use Json Encoder/Deocder.
First set your Notes class to conform to Codable:
class Notes: Codable {
var stock: [Note] = []
}
Here is an example of how to use Json Encoder / Decoder:
func save(notes: Notes) throws {
let encoder = JSONEncoder()
do {
let data = try encoder.encode(notes)
UserDefaults.standard.set(data, forKey: "notes")
} catch let error {
throw error
}
}
func load() -> Notes {
guard let data = UserDefaults.standard.data(forKey: "notes") else {
return Notes() // Default
}
let decoder = JSONDecoder()
do {
let object = try decoder.decode(Notes.self, from: data)
return object
} catch {
return Notes() // Default
}
}
In your code just call load() to get your notes from User Defaults
And save(notes:) to save them into User Defaults.

Stripe Alamofire JSON not populating array

I am trying to populate a UITableView with a list of credit cards in Stripe. I know it works for my test environment because I am able to see a JSON response from Postman. It for some reason is not populating my table.
Since this is an [Any Object] I do not need to create a separate class with init stings? I have other tables in my app populating data and updated UILabels after pulling info from FireBase.
Here is the code in the PaymentsVC.swift View Controller:
class PaymentVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var headerView: UIView!
#IBOutlet var cardsTableView: UITableView!
#IBOutlet var cardTextField: STPPaymentCardTextField!
var stripeTool = StripeTools()
static let sharedClient = MyAPIClient()
//var customerId: String?
let customerId = "mycusid"
var baseURLString: String? = "https://api.sripe.com/v1/customers"
var baseURL: URL {
if let urlString = self.baseURLString, let url = URL(string: urlString) {
return url
} else {
fatalError()
}
}
var stripeUtil = StripeUtil()
var cards = [AnyObject]()
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
//only one section
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.cards.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let url = self.baseURL.appendingPathComponent("\(self.customerId)/sources?object=card")
let headers = ["Authorization": self.stripeTool.getBasicAuth()]
Alamofire.request(url, headers: headers)
.validate(statusCode: 200..<300)
.responseJSON { response in
switch response.result {
case .success(let result):
if let cards = STPCustomer.decodedObject(fromAPIResponse: result as? [String: AnyObject]) {
print(cards)
// completion(cards, nil)
} else {
// completion(nil, NSError.customerDecodingError)
}
case .failure(let error): break
// nil, error
}
}
//get card cell with cardCell identifier don't forget it on your storyboard
let cell = tableView.dequeueReusableCell(withIdentifier: "cardCell") as! CardCell
//get the last4 value on the card json, create the string and pass it to the label
if let last4 = self.cards[indexPath.row]["last4"] {
cell.cardNumberLabel.text = "**** **** **** \(last4!)"
}
//get the month/year expiration values on the card json, create the string and pass it to the label
if let expirationMonth = self.cards[indexPath.row]["exp_month"], let expirationYear = self.cards[indexPath.row]["exp_year"] {
cell.expirationLabel.text = "\(expirationMonth!)/\(expirationYear!)"
}
return cell
}
Don't put api calls inside cellForRowAt as it'll be called every cell creation/dequeuing, you need to put this code inside viewDidLoad , with reloading the table
let url = self.baseURL.appendingPathComponent("\(self.customerId)/sources?object=card")
let headers = ["Authorization": self.stripeTool.getBasicAuth()]
Alamofire.request(url, headers: headers)
.validate(statusCode: 200..<300)
.responseJSON { response in
switch response.result {
case .success(let result):
if let cards = STPCustomer.decodedObject(fromAPIResponse: result as? [String: AnyObject]) {
print(cards)
self.cards = cards
self.cardsTableView.reloadData()
// completion(cards, nil)
} else {
// completion(nil, NSError.customerDecodingError)
}
case .failure(let error): break
// nil, error
}
}
don't forget to set in viewDidLoad
self.cardsTableView.delegate = self
self.cardsTableView.dataSource = self

Can any one help me to solve this error using Swift

would you please help me to solve this error .I'am trying to download an Image From Firebase Database, this is my code and I put a snapshot for the error . ThanksThis is a snapshot for the error in Xcode
import UIKit
import FirebaseDatabase
class ViewController: UIViewController , UITableViewDataSource , UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var ref:FIRDatabaseReference?
var Handle:FIRDatabaseHandle?
var myClass = [Post]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
ref=FIRDatabase.database().reference()
Handle = ref?.child("Posts").observe(.childAdded, with: { (snapshot) in
let post = snapshot.valueInExportFormat()
for url in post! as! [Post] { // Error Here
self.myClass.append(url)
self.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return myClass.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)as? TableViewCell{
cell.MyImage.alpha = 0
cell.textLabel?.text = PostData[indexPath.row]
DispatchQueue.main.async(execute: {
let imgurl = URL(string : self.myClass [(indexPath as NSIndexPath).row].url)
let imgdata = NSData(contentsOf: imgurl!)
cell.MyImage.image = UIImage(data: imgdata as! Data)
UIView.animate(withDuration: 0.5, animations: {
cell.MyImage.alpha = 1
})
})
return cell
} else {
let cell = TableViewCell()
DispatchQueue.main.async(execute: {
let imgurl = URL(string : self.myClass [(indexPath as NSIndexPath).row].url)
let imgdata = NSData(contentsOf: imgurl!)
cell.MyImage.image = UIImage(data: imgdata as! Data)
})
return cell
}
}
}
})
}
Sometimes simple is the way to go.
assume you have a Firebase structure
Planets
planet_4
some_text = "My post about Mars"
image_url = "images/mars.jpg"
planet_2
some_text = "My post about Venus"
image_url = "images/venus.jpg"
and suppose we want to load each text and image and display in a tableview. We can do it one of two ways, one at a time with .childAdded or all at once with .value. In this example, we'll walk through them one at a time.
let planetsRef = myRootRef.child("Planets")
planetsRef.observe(.childAdded, with: { snapshot in
let dict = snapshot.value as! [String: AnyObject]
let text = dict["text"]
let imageUrl = dict["image_url"]
// Create a reference to the file you want to download
let planetRef = storageRef.child(imageUrl) //storageRef is defined elsewhere
// Download in memory with a maximum allowed size
// of 1MB (1 * 1024 * 1024 bytes)
planetRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
if (error != nil) {
// Got an error so handle it
} else {
// Data for "images/some planet.jpg" is returned
// let planetImage: UIImage! = UIImage(data: data!)
// then add the text and the image to your dataSource array
// and reload your tableview.
}
})
})
This is not tested but will provide the general idea
Maybe you want:
for url in post! {
var wrappedPost = Post()
wrappedPost.url = url
... use wrappedPost for whatever you need a Post object for
}

Swift - Parse PFQueryTableViewController Error on loading the LocalDataStore

Good day! I'm using Parse in my swift project, Now my problem is loading and saving the query or objects to the localDataStore, I tried this method
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell") as CustomPFTableViewCell!
if cell == nil {
cell = CustomPFTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
}
// Extract values from the PFObject to display in the table cell
if let placeName = object?["placeName"] as? String {
cell.cellName.text = placeName
}
if let station = object?["station"] as? String {
cell.cellDetail.text = station
}
if let placeImg = object?["placeImg"] as? String {
let decodedData = NSData(base64EncodedString: placeImg, options: NSDataBase64DecodingOptions(rawValue: 0))
// var decodedimage = UIImage(data: decodedData!)
var finImage = UIImage(data: decodedData!)
cell.cellBgImg.image = finImage
}
PFObject.pinAllInBackground(self.objects, block: { (succeeded, error) -> Void in
if (error == nil) {
}else {
println(error!.userInfo)
}
})
return cell
}
now in my queryForTable method i have this
override func queryForTable() -> PFQuery {
// Start the query object
var query = PFQuery(className: "Places")
// Add a where clause if there is a search criteria
if searchBar.text != "" {
query.whereKey("filterKeyword", containsString: searchBar.text.lowercaseString)
}
// Order the results
query.orderByAscending("placeName")
var cReturn = PFQuery()
if (IJReachability.isConnectedToNetwork()) {
cReturn = query
} else {
cReturn = query.fromLocalDatastore()
}
return cReturn
}
As you can see, I'm using Reachability to check if the device is connected to the internet. If not, The query will return query.fromLocalDataStore and if the device is connected it will return the normal query to get the latest data.
Now, my problem is when I'm turning off the internet to test it, it gives me an error 'Method requires Pinning enabled.' which i already did in tableView method
PFObject.pinAllInBackground(self.objects, block: { (succeeded, error) -> Void in
if (error == nil) {
}else {
println(error!.userInfo)
}
})
What do you think I did wrong? Thanks!
I think you should put the method where you pin the objects inside your objectsDidLoad() method and not in your cellForRowAtindexPath() method.