Execute code first! Swift - ios

guys I am getting data from Foursquare APi and here is my code below.
But I am getting a nil error at cellForRowAtIndexPath that venueItems is nil
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// Table View
self.tableView = UITableView()
// Location Manager Stuff
self.locationManager = CLLocationManager()
self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
self.locationManager.delegate = self
let status = CLLocationManager.authorizationStatus()
if status == .notDetermined {
self.locationManager.requestWhenInUseAuthorization()
} else if status == CLAuthorizationStatus.authorizedWhenInUse
|| status == CLAuthorizationStatus.authorizedAlways {
self.locationManager.startUpdatingLocation()
} else {
showNoPermissionsAlert()
}
exploreVenues()
}
// Func's
func exploreVenues() {
guard let location = self.locationManager.location else {
return
}
var parameters = [Parameter.query: "Pubs"]
parameters += location.parameters()
let task = self.session.venues.explore(parameters) {
(result) -> Void in
if self.venueItems != nil {
return
}
if !Thread.isMainThread {
fatalError("!!!")
}
if let response = result.response {
if let groups = response["groups"] as? [[String: AnyObject]] {
var venues = [[String: AnyObject]]()
for group in groups {
if let items = group["items"] as? [[String: AnyObject]] {
venues += items
}
}
self.venueItems = venues
}
self.tableView.reloadData()
} else if let error = result.error, !result.isCancelled() {
self.showErrorAlert(error)
}
}
task.start()
}
// Table View Data source
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let venueItems = self.venueItems {
return venueItems.count
}
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! VenueTableViewCell
// This is where the error occurs
let item = self.venueItems![(indexPath as NSIndexPath).row] as JSONParameters!
self.configureCellWithItem(cell, item: item!)
return cell
}
func configureCellWithItem(_ cell: VenueTableViewCell, item: JSONParameters) {
if let venueInfo = item["venue"] as? JSONParameters {
cell.nameLabel.text = venueInfo["name"] as? String
}
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let cell = cell as! VenueTableViewCell
let tips = self.venueItems![(indexPath as NSIndexPath).row]["tips"] as? [JSONParameters]
guard let tip = tips?.first, let user = tip["user"] as? JSONParameters,
let photo = user["photo"] as? JSONParameters else {
return
}
let URL = photoURLFromJSONObject(photo)
if let imageData = session.cachedImageDataForURL(URL) {
cell.venueImageView.image = UIImage(data: imageData)
} else {
cell.venueImageView.image = nil
session.downloadImageAtURL(URL) { (imageData, error) -> Void in
let cell = tableView.cellForRow(at: indexPath) as? VenueTableViewCell
if let cell = cell, let imageData = imageData {
let image = UIImage(data: imageData)
cell.venueImageView.image = image
}
}
}
}
}
I am quite new to programming personally I think that the venueItems is nil because the cellForRowAtIndexPath is being executed first. If this is the error how can I fix it so the code in cellForRowAtIndexpath runs after my venueItems has a value.. or any other more efficient Way?

Your numberOfRowsInSection returns 10 when self.venueItems is nil. self.venueItems appears to be nil until your network request finishes so the table view, having been told it has 10 rows to display asks for a cell for each row. You then attempt to force unwrap an optional property (self.venueItems!) and crash.
It looks like your self.venueItems is an optional for good reason, don't discard that information with a force unwrap (!). You could either return 0 rows when this property is nil or initialize it to a non-optional empty array which you could then always ask for its count.
In general with this sort of problem you don't want to focus on preventing cellForRowAtIndexPath from being called but rather plan for it to be called at any point and return a reasonable result (like reporting that the table has 0 rows) when your background tasks haven't finished yet.

Related

How to Implement pagination in table view cell

My app retrieves json from the newsAPI.com . When I look at the json returned from the web service , it shows 2000 values however it returns 20 values loaded into my tableview controller. How do I increase the value so that when the user scrolls down the table view, they are presented with more values loaded into the table view controller?
class LatestNewsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let newsData = Articles() //Model object
let urlRequest = "https://newsapi.org/v2/everything?q=coronavirus&apiKey=d32071cd286c4f6b9c689527fc195b03" //Website API
var urlSelected = ""
var articles: [Articles]? = [] // holds array of Articles data
var indexOfPageToRequest = 1
#IBOutlet weak var table_view: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
table_view.cellLayoutMarginsFollowReadableWidth = true
navigationController?.navigationBar.prefersLargeTitles = true
retriveData( )
}
func retriveData( )->Int{
guard let aritcleUrl = URL(string: urlRequest) else { //send a request to the server
return n
}
let request = URLRequest(url: aritcleUrl)
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) -> Void in //collects content from website
if error != nil { // checks if content is available
print(error ?? 0)
return
}
if let data = data { // converts data to an array of Article objects
self.articles = self.parseData(data: data)
}
})
task.resume()
return n
}
func parseData(data:Data)-> [Articles] {
var articles: [Articles]? = [] // holds parsed data
do {
let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
let jsonArticles = jsonResult?["articles"] as? [AnyObject] ?? [] // gets first head of json file and converts it to dictionary
for jsonArticle in jsonArticles{ // captures data and stores it in the model object
let article = Articles()
article.author = jsonArticle["author"] as? String
article.title = jsonArticle["description"] as? String
article.publishedAt = jsonArticle["publishedAt"] as? String
article.urlImage = jsonArticle["urlToImage"] as? String
article.urlWebsite = jsonArticle["url"] as? String
articles?.append(article) //put article data in the array
}
print(jsonArticles)
DispatchQueue.main.async {
if(articles!.count > 0)
{
self.table_view.reloadData()
}
}
} catch {
print("Nothing my guy\(error)")
}
return articles ?? [] // returns an array of articles
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return articles?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! NewsTableViewCell
cell.authorName.text = articles?[indexPath.row].author
cell.headLine.text = articles?[indexPath.row].title
cell.newsImage.downloadImage(from:(self.articles?[indexPath.item].urlImage ?? "nill"))
cell.timePublication.text = articles?[indexPath.row].publishedAt
return cell
}
[1]: https://i.stack.imgur.com/OY5G5.png
First, I would check the constraints of the table view, because this is a common issue (usually 0,0,0,0)
And also I would check the 'scrolling enabled' in the Attribute inspector and 'reuse Identifier'

Index out of range when i use to celll( Swift)

people, I have this issue when I try back image from different cell
(Thread 1: Fatal error: Index out of range)
what I'm doing here ?
I'm trying to build an Instagram clone and in my home view controller that what should posts show up. I make navigation with a table view and that table view has 2 cell with the different identifier. cell number 1 it's a header that brings data from users table to my username label and profile image. and cell number 2 its for posts its should bring post data like image and caption. I use firebase database.
my code :
import UIKit
import FirebaseAuth
import FirebaseDatabase
class HomeViewController: UIViewController ,UITableViewDelegate {
#IBOutlet weak var tableview: UITableView!
var posts = [Post]()
var users = [UserD]()
override func viewDidLoad() {
super.viewDidLoad()
tableview.dataSource = self
loadposts()
userDetal()
// var post = Post(captiontxt: "test", photoUrlString: "urll")
// print(post.caption)
// print(post.photoUrl)
}
func loadposts() {
Database.database().reference().child("posts").observe(.childAdded){ (snapshot: DataSnapshot)in
print(Thread.isMainThread)
if let dict = snapshot.value as? [String: Any]{
let captiontxt = dict["caption"] as! String
let photoUrlString = dict["photoUrl"] as! String
let post = Post(captiontxt: captiontxt, photoUrlString: photoUrlString)
self.posts.append(post)
print(self.posts)
self.tableview.reloadData()
}
}
}
func userDetal() {
Database.database().reference().child("users").observe(.childAdded){ (snapshot: DataSnapshot)in
print(Thread.isMainThread)
if let dict = snapshot.value as? [String: Any]{
let usernametxt = dict["username"] as! String
let profileImageUrlString = dict["profileImageUrl"] as! String
let user = UserD(usernametxt: usernametxt, profileImageUrlString: profileImageUrlString)
self.users.append(user)
print(self.users)
self.tableview.reloadData()
}
}
}
#IBAction func logout(_ sender: Any) {
do {
try Auth.auth().signOut()
}catch let logoutErrorr{
print(logoutErrorr)
}
let storyboard = UIStoryboard(name: "Start", bundle: nil)
let signinVC = storyboard.instantiateViewController(withIdentifier: "SigninViewController")
self.present(signinVC, animated: true, completion: nil)
}
}
extension HomeViewController: UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return users.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0{
let cell = tableview.dequeueReusableCell(withIdentifier: "imagecell", for: indexPath) as! PostCellTableViewCell
cell.postimage.image = nil
cell.tag += 1
let tag = cell.tag
cell.captionLabel.text = posts[indexPath.row].caption
let photoUrl = posts[indexPath.row].photoUrl
getImage(url: photoUrl) { photo in
if photo != nil {
if cell.tag == tag {
DispatchQueue.main.async {
cell.postimage.image = photo
}
}
}
}
return cell
} else if indexPath.row == 1 {
let cell = tableview.dequeueReusableCell(withIdentifier: "postcell", for: indexPath) as! HeaderTableViewCell
cell.userimage.image = nil
cell.tag += 1
let tag = cell.tag
cell.usernamelabel.text = users[indexPath.row].username
//Error showing here????????????????????????????????????
let profileImageUrl = users[indexPath.row].profileImageUrl
getImage(url: profileImageUrl) { photo in
if photo != nil {
if cell.tag == tag {
DispatchQueue.main.async {
cell.userimage.image = photo
}
}
}
}
return cell
}
return UITableViewCell()
}
func getImage(url: String, completion: #escaping (UIImage?) -> ()) {
URLSession.shared.dataTask(with: URL(string: url)!) { data, response, error in
if error == nil {
completion(UIImage(data: data!))
} else {
completion(nil)
}
}.resume()
}
}
try this one.
cell.tag = indexpath.row
What is the content of users array ?
Are you sure you want to define as many sections as users or as many rows ?
In this case use
func numberOfRows(in tableView: NSTableView) -> Int {
return users.count
}
As explained, you need to rewrite completely cellForRowAt
It should look like this :
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
if row < users.count {
let user = users[row]
if let cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "CellID"), owner: self) {
(cellView as! NSTableCellView).textField?.stringValue = user.name
// do the same for all the fields you need to set
return cellView
} else {
return nil
}
}
return nil
}
thanx, my friend, I found a good way to contain my cell. for post cell, i just use cellForRowAt and but the post data. for header cell i use viewForHeaderInSection
and but my user data with heightForHeaderInSection. to make the high for a view

how to make checkmark to be selected depending on the array in swift 3?

I am having array in which selected name will be stored and passed to before view controller and when ever i need to go previous view controller then the previously selected check mark needs to be selected but here it is enabling the last selected element only the problem is if i select three then it is not selecting three it is check marking only the last element but i need the three selected can anyone help me how to make the check mark to be selected for three elements ?
protocol ArrayToPass: class {
func selectedArrayToPass(selectedStrings: [String])
}
class FilterSelectionViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
var productName = [String]()
var productprice = [String]()
var imageArray = [String]()
var idArray = [Int]()
let urlString = "http://www.json-generator.com/api/json/get/bOYOrkIOSq?indent=2"
var values = [String]()
var selected: Bool?
var delegate: ArrayToPass?
var nameSelection: Bool?
var namesArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.downloadJsonWithURL()
tableDetails.separatorInset = UIEdgeInsets.zero
activityIndicator.startAnimating()
tableDetails.isHidden = true
tableDetails.dataSource = self
tableDetails.delegate = self
let rightBarButton = UIBarButtonItem(title: "Apply", style: UIBarButtonItemStyle.plain, target: self, action: #selector(applyBarButtonActionTapped(_:)))
self.navigationItem.rightBarButtonItem = rightBarButton
tableDetails.estimatedRowHeight = UITableViewAutomaticDimension
tableDetails.rowHeight = 60
// Do any additional setup after loading the view.
}
func applyBarButtonActionTapped(_ sender:UIBarButtonItem!){
self.delegate?.selectedArrayToPass(selectedStrings: values)
navigationController?.popViewController(animated: true)
}
func downloadJsonWithURL() {
let url = NSURL(string: urlString)
URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSArray {
for item in jsonObj! {
if let itemDict = item as? NSDictionary{
if let name = itemDict.value(forKey: "name") {
self.productName.append(name as! String)
}
if let price = itemDict.value(forKey: "value") {
self.productprice.append(price as! String)
}
if let image = itemDict.value(forKey: "img") {
self.imageArray.append(image as! String)
}
if let id = itemDict.value(forKey: "id") {
self.idArray.append(id as! Int)
}
}
}
OperationQueue.main.addOperation({
self.tableDetails.reloadData()
})
}
}).resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return productName.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "filterSelectionCell", for: indexPath) as! FilterSelectionCell
activityIndicator.stopAnimating()
activityIndicator.hidesWhenStopped = true
tableDetails.isHidden = false
cell.brandProductName.text = productName[indexPath.row]
if nameSelection == true{
if namesArray.count != 0 {
print(namesArray)
for name in namesArray{
if productName[indexPath.row].contains(name){
print(productName[indexPath.row])
cell.accessoryType = .checkmark
}
else {
cell.accessoryType = .none
}
}
}
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
selected = false
if let cell = tableView.cellForRow(at: indexPath as IndexPath) {
if cell.accessoryType == .checkmark{
cell.accessoryType = .none
print("\(productName[indexPath.row])")
values = values.filter{$0 != "\(productName[indexPath.row])"}
selected = true
}
else{
cell.accessoryType = .checkmark
}
}
if selected == true{
print(values)
}
else{
getAllTextFromTableView()
}
print(values)
}
func getAllTextFromTableView() {
guard let indexPaths = self.tableDetails.indexPathsForSelectedRows else { // if no selected cells just return
return
}
for indexPath in indexPaths {
values.append(productName[indexPath.row])
}
}
here is the image for this
Basically do not manipulate the view (the cell). Use a data model.
struct Product {
let name : String
let value : String
let img : String
let id : Int
var selected = false
init(dict : [String:Any]) {
self.name = dict["name"] as? String ?? ""
self.value = dict["value"] as? String ?? ""
self.img = dict["img"] as? String ?? ""
self.id = dict["id"] as? Int ?? 0
}
}
And never use multiple arrays as data source . That's a very bad habit.
Declare the data source array as
var products = [Product]()
Parse the JSON data and do a (better) error handling
func downloadJsonWithURL() {
let url = URL(string: urlString)!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil { print(error!); return }
do {
if let jsonObj = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] {
self.products = jsonObj.map{ Product(dict: $0) }
DispatchQueue.main.async {
self.tableDetails.reloadData()
}
}
} catch {
print(error)
}
}
task.resume()
}
in cellForRow... assign the name to the label and set the checkmark depending on selected
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "filterSelectionCell", for: indexPath)
let product = products[indexPath.row]
cell.textLabel!.text = product.name
cell.accessoryType = product.selected ? .checkmark : .none
return cell
}
In didSelect... toggle selected and reload the row
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selected = products[indexPath.row].selected
products[indexPath.row].selected = !selected
tableView.reloadRows(at: [indexPath], with: .none)
}
To get all selected items is very easy, too.
let selectedItems = products.filter{ $0.selected }
or get only the names
let selectedNames = products.filter{ $0.selected }.map{ $0.name }
There is no need at all to get any information from the view. The controller gets the information always from the model and uses tableview data source and delegate to update the view.
If you want to pass data to another view controller pass Product instances. They contain all relevant information.

Swift: Multiple Custom TableViewCells from XIB files

I am following this tutorial from Jared Davidson to implement multiple CustomTableViewCells with XIB files in my app. I have these files in my Xcode project:.
I have a TextElement: and
I have an ImageElement:
I want to test this with offline data to implement Firebase after this is working. This is my Home.swift data struct:
import Foundation
import FirebaseDatabase
struct Home {
var key:String!
let itemRef:FIRDatabaseReference?
var userUID:String!
var user:String!
// Home Element Cell Content
var elementSortNumber:Int!
var elementCellType:String!
var referenceElementID:String!
var databaseVersion:String!
init (key:String = "",
uid:String,
user:String,
elementSortNumber:Int,
elementCellType:String,
referenceElementID:String,
databaseVersion:String) {
// General (Security tracking)
self.key = key
self.itemRef = nil
self.userUID = uid
self.user = user
// Home Element Cell Content
self.elementSortNumber = elementSortNumber
self.elementCellType = elementCellType
self.referenceElementID = referenceElementID
}
init (snapshot:FIRDataSnapshot) {
// General (Security tracking)
key = snapshot.key
itemRef = snapshot.ref
if let addedByUser = snapshot.value as? NSDictionary, let _temp = addedByUser["User"] as? String {
user = _temp
} else {
user = ""
}
// Home Element Cell Content
if let homeElementSortNumber = snapshot.value as? NSDictionary, let _temp = homeElementSortNumber["Title"] as? Int {
elementSortNumber = _temp
} else {
elementSortNumber = 50
}
if let homeElementCellType = snapshot.value as? NSDictionary, let _temp = homeElementCellType["Content"] as? String {
elementCellType = _temp
} else {
elementCellType = ""
}
if let homeElementID = snapshot.value as? NSDictionary, let _temp = homeElementID["Ref Element ID"] as? String {
referenceElementID = _temp
} else {
referenceElementID = ""
}
if let textDatabaseVersion = snapshot.value as? NSDictionary, let _temp = textDatabaseVersion["DB Version"] as? String {
databaseVersion = _temp
} else {
databaseVersion = ""
}
}
}
This is the code of my TableViewController:
import UIKit
class HomeTableViewController: UITableViewController {
var arrayOfCellData = [Home]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
arrayOfCellData =
[Home(key: "",
uid:"",
user:"",
elementSortNumber:1,
elementCellType:"TextElement",
referenceElementID:"123ABC",
databaseVersion:"1"),
Home(key: "",
uid:"",
user:"",
elementSortNumber:1,
elementCellType:"ImageElement",
referenceElementID:"QWERTZ",
databaseVersion:"1"),
Home(key: "",
uid:"",
user:"",
elementSortNumber:1,
elementCellType:"TextElement",
referenceElementID:"XYZ789",
databaseVersion:"1")]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// If I return 1 the app crashes and if I comment this function it also crashes.
return 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayOfCellData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if arrayOfCellData[indexPath.row].elementCellType == "TextElement" {
let textElementCell = Bundle.main.loadNibNamed("TextElementTableViewCell", owner: self, options: nil) as! TextElementTableViewCell
textElementCell.textElementTitleLabel.text = arrayOfCellData[indexPath.row].referenceElementID
return textElementCell
}
else if arrayOfCellData[indexPath.row].elementCellType == "ImageElement" {
let imageElementCell = Bundle.main.loadNibNamed("ImageElementTableViewCell", owner: self, options: nil) as! ImageElementTableViewCell
imageElementCell.imageElementImageView.image = UIImage(named: "placeholder")
return imageElementCell
}
else {
let textElementDefaultCell = Bundle.main.loadNibNamed("TextElementTableViewCell", owner: self, options: nil) as! TextElementTableViewCell
textElementDefaultCell.textElementTitleLabel.text = arrayOfCellData[indexPath.row].referenceElementID
return textElementDefaultCell
}
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if arrayOfCellData[indexPath.row].elementCellType == "TextElement" {
return 116
}
else if arrayOfCellData[indexPath.row].elementCellType == "ImageElement" {
return 275
}
else {
return 116
}
}
}
This is the problem: The simulator is empty as you can see in this image Why? How can I fix that?
I would really appreciate some help. Thank you.
Register the xib files as below in viewdidload:
tableView.register(UINib(nibName: "TextElementTableViewCell", bundle: Bundle.main), forCellReuseIdentifier: "TextElementTableViewCellIdentifier")
Then in cellForRowIndex path:Access cell using their identifier
let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "TextElementTableViewCellIdentifier", for: indexPath) as! TextElementTableViewCell

How to populate my tableView in Swift 2 from JSON?

A new programmer here. How would I populate my tableView from this JSON?
My first problem is the JSON Serialization and then plugging it in the tableView.
Code
import UIKit
class LegislatorsTableVC: UITableViewController {
// MARK: Variables & Outlets
private let cellIdentifer = "cellReuse"
// MARK: View Did Load
override func viewDidLoad() {
super.viewDidLoad()
// Creating Congfiguration Object // Session Is Created // Getting Info/Data
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: configuration)
let apiKey = "https://congress.api.sunlightfoundation.com/legislators?apikey=xxxxxxxxxxxxxxxxxxxxx&all_legislators=true&per_page=all"
if let url = NSURL(string: apiKey) {
// Spawning Task To Retrieve JSON Data
session.dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
// Checking For Error
if let error = error {
print("The error is: \(error)")
return
}
// Response
if let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode == 200, let data = data {
print("Status Code: \(httpResponse.statusCode)")
// self.JSONSerialization(data)
}
}).resume()
}
} // End Of View Did Load
// JSON Serialization Function With SwiftyJSON.swift
private func JSONSerialization(data: NSData){
// I See this Gets A Status Code 200 And Then I'm Lost.
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers) as! [String: AnyObject]
} catch {
print("Error Serializing JSON Data: \(error)")
}
} // End Of JSONSerialization
// MARK: - Table view data source
// Number Of Sections
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
} // End Of Number Of Sections
// Number Of Rows In Section
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 15
} // End Of Number Of Rows In Section
// Cell For Row At Index Path
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifer, forIndexPath: indexPath) as! LegislatorTVCell
// Configure the cell...
cell.name.text = "Name"
cell.title.text = "Title"
cell.party.text = "Party"
return cell
} // End Of Cell For Row At Index Path
}
Create a custom class Person outside the view controller
class Person {
var firstName = ""
var lastName = ""
var title = ""
var party = ""
}
Create an array of Person in the view controller
var people = [Person]()
The JSON has a key results which contains an array of dictionaries.
In viewDidLoad parse the JSON and create Person instances. Finally reload the table view.
override func viewDidLoad() {
super.viewDidLoad()
// Creating Congfiguration Object // Session Is Created // Getting Info/Data
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: configuration)
let apiKey = "https://congress.api.sunlightfoundation.com/legislators?apikey=xxxxxxxxxxxxxxxxxx&all_legislators=true&per_page=all"
if let url = NSURL(string: apiKey) {
// Spawning Task To Retrieve JSON Data
session.dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
// Checking For Error
if error != nil {
print("The error is: \(error!)")
return
} else if let jsonData = data {
do {
let parsedJSON = try NSJSONSerialization.JSONObjectWithData(jsonData, options: []) as! [String:AnyObject]
guard let results = parsedJSON["results"] as? [[String:AnyObject]] else { return }
for result in results {
let person = Person()
person.firstName = result["first_name"] as! String
person.lastName = result["last_name"] as! String
person.party = result["party"] as! String
person.title = result["title"] as! String
self.people.append(person)
}
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
} catch let error as NSError {
print(error)
}
}
}).resume()
}
} // End Of View Did Load
The table view delegate methods look very clear when using a custom class.
Since cellForRowAtIndexPath is called very often the code is quite effective.
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return people.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifer, forIndexPath: indexPath) as! LegislatorTVCell
let person = people[indexPath.row]
cell.name.text = person.firstName + " " + person.lastName
cell.title.text = person.title
cell.party.text = person.party
return cell
} // End
Of course I couldn't test the code but this might be a starting point.
Basically what you want to do is introduce a new variable to your class, for example jsonDict like so:
class LegislatorsTableVC: UITableViewController {
var jsonDict:Dictionary<String,AnyObject>?
// further code
And then - you almost got it right already - save your JSON serialization into that in your JSONSerialization function. (which I would rename to parseJSON or something like that to avoid confusion) like so:
do {
jsonDict = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers) as! [String: AnyObject]
} catch {
print("Error Serializing JSON Data: \(error)")
}
So then you can return the right values to your tableView data source:
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return jsonDict["your JSON key"].count ?? 0
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return jsonDict["your JSON key"]["items"].count ?? 0
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifer, forIndexPath: indexPath) as! LegislatorTVCell
let item = jsonDict["your JSON key"][indexPath.row]
// Configure the cell...
cell.name.text = item["name"]
cell.title.text = item["title"]
cell.party.text = item["party"]
return cell
}
Naming is a little confusing, as I don't know the layout of your JSON, but replace your JSON key with your path to the data of course.

Resources