Swift: tableView always "found nil while unwr..." inside a viewcontroller - ios

In a viewController I've connected a tableView outlet in the SB with delegates and datasource set and when I'm accessing the reference outlet in code it always crashes with the message:
fatal error: unexpectedly found nil while unwrapping an Optional value
Here's the code:
#IBOutlet var resultTabView: UITableView!
Connection:
cellForRowAtIndexPath: -
I'm not even using resultsArray, though the array is returning data as seen in the logs
EDIT 2:
Data:
id = 2325;
results = (
{
city = Delhi;
company = "AIR CREATOR";
country = IN;
date = "Mon, 11 Jul 2016 04:51:34 GMT";
expired = 0;
formattedLocation = "Delhi, Delhi";
formattedLocationFull = "Delhi, Delhi";
formattedRelativeTime = "30+ days ago";
jobkey = 67ba422cc328a946;
jobtitle = "CSA for airport";
latitude = "28.664835";
longitude = "77.21429000000001";
onmousedown = "indeed_clk(this,'3586');";
snippet = "Providing boarding passes and luggage <b>labels</b>. Your daily tasks will usually include:....";
source = Indeed;
sponsored = 0;
state = DL;
url = "http://www.indeed.co.in/viewjob?jk=67ba422cc328a946&qd=SHSUhPNSBYpqaNu9AVdfqv81wDpAwFE36uyNVXcxl8VAUThqIJkZA1l3w1yQl8AJg05AJq_aODrwRlOUKXT92UBk4SDqDqM7LhTReglYsV0&indpubnum=4148406673078092&atk=1aqbfaj6kb958c17";
},
{
city = Delhi;
company = "Letmedrive India Private Limited";
country = IN;
date = "Wed, 10 Aug 2016 13:02:51 GMT";
expired = 0;
formattedLocation = "Delhi, Delhi";
formattedLocationFull = "Delhi, Delhi";
formattedRelativeTime = "6 days ago";
jobkey = 89c4f63f1b841720;
jobtitle = "Graphics Designer";
latitude = "28.664835";
longitude = "77.21429000000001";
onmousedown = "indeed_clk(this,'3586');";
snippet = "Graphic designers/artists design graphics for use in media products such as magazines, <b>labels</b> & advertising....";
source = Indeed;
sponsored = 0;
state = DL;
url = "http://www.indeed.co.in/viewjob?jk=89c4f63f1b841720&qd=SHSUhPNSBYpqaNu9AVdfqv81wDpAwFE36uyNVXcxl8VAUThqIJkZA1l3w1yQl8AJg05AJq_aODrwRlOUKXT92UBk4SDqDqM7LhTReglYsV0&indpubnum=4148406673078092&atk=1aqbfaj6kb958c17";
};
Class:
class JobListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate{
#IBOutlet var searchField: UITextField!
#IBOutlet var resultTabView: UITableView!
var resultsArray = [JSON]()
var resultsDictionary = [String:String]()
override func viewDidLoad() {
super.viewDidLoad()
}
func focusSearchField(search:Bool, keyword:String){
if(search){
callAPI(keyword)
}else{
searchField?.becomeFirstResponder()
}
}
func callAPI(keyword:String){
var params = [String:String]()
params["publisher"] = "2341411"
params["q"] = keyword
params["l"] = "dubai"
params["latlong"] = "1"
params["v"] = "2"
params["format"] = "json"
params["co"] = "uae"
let svcMgr = ServiceManager()
svcMgr.httpRequest(self, params: params)
}
func updateUI(result: [String:AnyObject]){
if let val = result["results"] {
let json = JSON(val)
let array = json.arrayValue
dispatch_async(dispatch_get_main_queue(), {
for a in array{
self.resultsArray.append(a)
}
self.resultTabView.reloadData()
})
}else{
}
}
// MARK: - Table view data source
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return resultsArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier("JobCell", forIndexPath: indexPath) as UITableViewCell
let labl2 = cell.viewWithTag(2) as! UILabel
labl2.text = (resultsArray[indexPath.row]["jobtitle"].stringValue)+" #"+(resultsArray[indexPath.row]["source"].stringValue)
let labl3 = cell.viewWithTag(3) as! UILabel
labl3.text = (resultsArray[indexPath.row]["state"].stringValue)+", "+(resultsArray[indexPath.row]["country"].stringValue)
let labl4 = cell.viewWithTag(4) as! UILabel
labl4.text = (resultsArray[indexPath.row]["snippet"].stringValue)+" "
tableView.estimatedRowHeight = 80
tableView.rowHeight = UITableViewAutomaticDimension
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
UIApplication.sharedApplication().openURL(NSURL(string: resultsArray[indexPath.row]["url"].stringValue)!)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - TextField delegate
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
callAPI(textField.text!)
return true
}

Follow the following steps,
Please remove all connection with table view and add that again.
Make sure tableViewCellIdentifier have same name set in storyboard as given in code.
remove "!" mark after AnyObject, i beleive you need this
var resultsArray = (val as? [AnyObject])
This is not table view reloading error, its parsing issue. Please check all your declare properties.If possible show all your table view data source methods.

Make sure the viewDidLoad method is called before attempting to access the table view. Your connection from XIB is probably not established by the time this code runs.

Related

Activity Indicator doesn't disappear when load array in background with JSON, with election in a tableview

I'm new in swift and the problem is this, is a database of careers and want to load the classes according to the selected career in the table, this with the link that appears in the code, but in case of a bad connection, the array "Courses" is loading after finishing the function, for which I wanted to add a sentence to keep the function running while the array isn`t load, but I realized that the Activity Indicator should appear while the array is loading, actually appears at the end of the function although I call it at the start of function, then my problem is how to make the activity indicator is present since career pressed on the table, until the array of "Courses" load all courses of career. Help Me please :(! This is the code...
[import UIKit
class CarreraTableViewController: UIViewController {
//Var to InicioApp ViewController
var CarreraSeleccionada = String()
//OK BarButton
#IBOutlet weak var Listo: UIBarButtonItem!
//Courses array
var Cursos = \[String\]()
//The Activity Indicator is in a UIView in the storyboard to more easily customize
#IBOutlet weak var ActivityIndicatorView: UIActivityIndicatorView!
#IBOutlet weak var ActivityContainer: UIView!
//Careers Array
var Carreras = \["Enlace Escolar - Antofagasta", "Enlace Escolar - Coquimbo", "Geología", "Ingeniería Civil Ambiental", "ICCI – Antofagasta", "ICCI – Coquimbo", "Ingeniería Civil", "Ingeniería Civil Industrial – Antofagasta", "Ingeniería Civil Industrial – Coquimbo", "Ingeniería Civil Metalúrgica", "Ingeniería Civil de Minas", "Ingeniería Civil Química", "Ingeniería en Construcción", "Ingeniería en Computación e Informática", "Ingeniería en Metalurgia", "Ingeniería en Procesos Químicos", "IPRYMA - Antofagasta", "IPRYMA - Coquimbo"\]
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Carreras.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "MyCell"
let showData = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! CustomCellCarrera
let Carrera = Carreras\[indexPath.row\]
showData.CarreraLabel.text = Carrera
return showData
}
// Identifies which table row the user selected
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// ================= At this point I have my question, because ActivityContainer and ActivityIndicatorView does not appear until the end function
Listo.enabled = false
ActivityContainer.hidden = false
ActivityIndicatorView.startAnimating()
let currentCell = tableView.cellForRowAtIndexPath(indexPath)! as! CustomCellCarrera
print(currentCell.CarreraLabel.text)
var arregloArea = \[String\]()
var arregloID = \[Int\]()
Cursos.removeAll()
var i = -1
if let CR = currentCell.CarreraLabel.text {
switch CR {
case "ICCI – Coquimbo":
i = 1
case "Ingeniería Civil Industrial – Coquimbo":
i = 2
case "ICCI – Antofagasta":
i = 10
case "Enlace Escolar - Antofagasta":
i = 15
case "Enlace Escolar - Coquimbo":
i = 14
case "Geología":
i = 16
case "Ingeniería Civil Ambiental":
i = 12
case "Ingeniería Civil":
i = 5
case "Ingeniería Civil Industrial – Antofagasta":
i = 4
case "Ingeniería Civil Metalúrgica":
i = 7
case "Ingeniería Civil de Minas":
i = 11
case "Ingeniería Civil Química":
i = 6
case "Ingeniería en Construcción":
i = 3
case "Ingeniería en Computación e Informática":
i = 13
case "Ingeniería en Metalurgia":
i = 17
case "Ingeniería en Procesos Químicos":
i = 18
case "IPRYMA - Antofagasta":
i = 8
case "IPRYMA - Coquimbo":
i = 9
default:
i = -1
}
}
//Varia i según carrera
let url = NSURL(string: "http://146.83.128.64/tongoy/g.php?&sala=-1&curso=-1&profesor=-1&semestre=-1&semestrec=-1&carrera=\(i)&area=-1")!
print(url)
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in
if let urlContent = data {
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(urlContent, options: NSJSONReadingOptions.MutableContainers)
for json in jsonResult as! Array<AnyObject> {
if let area = json\["area"\] as? String {
arregloArea.append(area)
} else {
}
if let id = json\["id"\] as? Int {
arregloID.append(id)
}else{
}
if let curso = json\["curso"\] as? String {
self.Cursos.append(curso)
}else{
}
}
} catch {
print("JSON serialization failed")
}
dump(self.Cursos)
}
}
task.resume()
CarreraSeleccionada = currentCell.CarreraLabel.text!
while Cursos.isEmpty{
}
if !Cursos.isEmpty{
Listo.enabled = true
ActivityIndicatorView.stopAnimating()
ActivityContainer.hidden = true
}
}
}
//To InicioApp VC
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
let DestViewController : InicioApp = segue.destinationViewController as! InicioApp
//Llamado a funcion de eliminar ramos duplicados
var RepCurso = removeDuplicates(Cursos)
RepCurso.sortInPlace(before)
print(RepCurso)
DestViewController.CursosCarrera = RepCurso
DestViewController.TableText = CarreraSeleccionada
}
func before(value1: String, value2: String) -> Bool {
// One string is alphabetically first.
// ... True means value1 precedes value2.
return value1 < value2;
}
func removeDuplicates(array: \[String\]) -> \[String\] {
var encountered = Set<String>()
var result: \[String\] = \[\]
for value in array {
if encountered.contains(value) {
// Do not add a duplicate element.
}
else {
// Add value to the set.
encountered.insert(value)
// ... Append the value.
result.append(value)
}
}
return result
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}][1]
Your loop while Cursos.isEmpty{ will block the main thread, preventing any updates to the UI.
You need to perform the tasks you need in the completion closure, not at the end of the function. Also, since you are updating the UI, you need to dispatch these on the main queue, as the completion closure will be executing on another thread.
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in
if let urlContent = data {
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(urlContent, options: NSJSONReadingOptions.MutableContainers)
for json in jsonResult as! Array<AnyObject> {
if let area = json\["area"\] as? String {
arregloArea.append(area)
} else {
}
if let id = json\["id"\] as? Int {
arregloID.append(id)
}else{
}
if let curso = json\["curso"\] as? String {
self.Cursos.append(curso)
}else{
}
}
} catch {
print("JSON serialization failed")
}
dispatch_async(dispatch_get_main_queue(), ^{
Listo.enabled = true
ActivityIndicatorView.stopAnimating()
ActivityContainer.hidden = true
});
dump(self.Cursos)
}
}

How to iterate through Dictionary of Dictionary Values in UITableViewCell?

This is my first post and I hope its a great question because iv been stuck on this for days. (Literally searched every related question and found nothing that I could add up for a solution.)
Basically I'm building a pure Swift application. My problem is that I cant figure out how to place each dictionary values into each UITableCell that is created.
Also the JSON response from the GET creates a NSCFDictionary type.
UITableViewCell Properties
UILabel - Holds the Offer "name"
UI Label #2 - Holds Offer "description"
UIImage - Holds the Offer "thumb_url"
So basically I need to store each offer object's (name, description, thumb_url) in every UITableViewCell that the ViewController creates.
GET Request
import UIKit
class freeIAPCell: UITableViewCell {
#IBOutlet weak var aName: UILabel!
#IBOutlet weak var aDescription: UILabel!
#IBOutlet weak var aImage: UIImageView!
}
class FreeIAPViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// Everbadge Request
var apiURL = "apiurlhere"
var parsedArray: [[String:String]] = []
func queryEB() {
// Query DB Here
// Store the API url in a NSURL Object
let nsURL = NSURL(string: apiURL)
let request = NSMutableURLRequest(URL: nsURL!)
request.HTTPMethod = "GET"
// Execute HTTP Request
let task = NSURLSession.sharedSession().dataTaskWithURL(nsURL!) {
data, response, error in
if error != nil {
print("Error = \(error)")
return
}
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments) as! NSDictionary
print(json.isKindOfClass(NSDictionary)) // true
let data = json.objectForKey("data") as! NSDictionary
//print(data)
let m = data.objectForKey("offers") as! NSArray
print(m)
print(m.valueForKeyPath("name"))
self.parsedArray = m as! [[String : String]]
} catch {
print("THERE WAS AN ERROR PARSING JSON FOR EVERBADGE")
}
}
task.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
queryEB()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
}
// MARK: UITableView method implementation
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 100
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("freeAppCell", forIndexPath: indexPath) as! freeIAPCell
let obj = parsedArray[indexPath.row] // fatal error: Index out of range
cell.aName.text = obj["name"]
return cell
}
}
API Request Data Structure (print(m))
(
{
"name" = "Mate";
id = 23941;
description = "10-20 word description goes here"
"url" = "google.com
"ver" = "0.12"
price = "0.17"
"public_name" = "Good Ole Mate"
"thumb_url" = "http://google.com/mate.jpg
};
{
"name" = "Mate";
id = 23941;
description = "10-20 word description goes here"
"url" = "google.com
"ver" = "0.12"
price = "0.17"
"public_name" = "Good Ole Mate"
"thumb_url" = "http://google.com/mate.jpg
};
{
"name" = "Mate";
id = 23941;
description = "10-20 word description goes here"
"url" = "google.com
"ver" = "0.12"
price = "0.17"
"public_name" = "Good Ole Mate"
"thumb_url" = "http://google.com/mate.jpg
};
{
"name" = "Mate";
id = 23941;
description = "10-20 word description goes here"
"url" = "google.com
"ver" = "0.12"
price = "0.17"
"public_name" = "Good Ole Mate"
"thumb_url" = "http://google.com/mate.jpg
};
{
"name" = "Mate";
id = 23941;
description = "10-20 word description goes here"
"url" = "google.com
"ver" = "0.12"
price = "0.17"
"public_name" = "Good Ole Mate"
"thumb_url" = "http://google.com/mate.jpg
};
);
First create public arrays for your 3 items...
var nameArray = [String]()
var descriptionArray = [String]()
var thumbnailArray = [String]()
Then loop through your json parse like this....
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding) as! NSDictionary
if responseString != nil
{
let items: AnyObject! = responseString["items"] as AnyObject!
if items != nil
{
// Loop through all search results and keep just the necessary data.
for var i=0; i<items.count; ++i
{
if let names = items[i]["name"] as? NSDictionary
{
let name = names as! String
self.nameArray.append(name)
}
if let descriptions = items[i]["description"] as? NSDictionary
{
let description = descriptions as! String
self.descriptionArray.append(description)
}
if let thumbnails = items[i]["thumb_url"] as? NSDictionary
{
let thumbnail = thumbnails as! String
self.thumbnailArray.append(thumbnail)
}
}
}
self.resultsTableView.reloadData()
Create an OfferTableViewCell Class with a nameLabel:UILabel, descriptionLabel:UILabel, and thumbnailImage:UIImage. Finally in your cellForRowAtIndexPath.....
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("offer", forIndexPath: indexPath) as! OfferTableViewCell
cell.nameLabel.text = self.nameArray[indexPath.row]
cell.descriptionLabel.text = self.descriptionArray[indexPath.row]
let url = NSURL(string: self.thumbnailArray[indexPath.row])
let data = NSData(contentsOfURL: url!)
cell.thumbnailImage.image = UIImage(data: data!)
return cell
}

How do I push Json data to labels in a UITableView?

I've got 3 labels in a custom UITableview cell and I'm trying to pass in json data I've gotten from an api with Alamofire but I'm struggling to understand how to push the returned json into the tableview. Any help would be greatly appreciated.
Code below:
import UIKit
import Parse
import Alamofire
class LeagueTableController: UIViewController, UITableViewDataSource, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(.GET, "https://api.import.io/store/connector/88c66c----9b01-6bd2bb--d/_query?input=webpage/url:----") .responseJSON { response in // 1
print(response.request) // original URL request
print(response.response) // URL response
print(response.data) // server data
print(response.result) // result of response serialization
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
return cell
}
}
Returned json like this:
{
connectorGuid = "88c66cb4-e64f-4316-9b01-6bd2bb2d762d";
connectorVersionGuid = "8aedfe43-948a-4559-b279-d3c3c28047a4";
cookies = (
);
offset = 0;
outputProperties = (
{
name = team;
type = URL;
},
{
name = played;
type = DOUBLE;
},
{
name = points;
type = DOUBLE;
}
);
pageUrl = "http://www.extratime.ie/leagues/2024/100/premier-division/";
results = (
{
played = 9;
"played/_source" = 9;
points = 22;
"points/_source" = 22;
team = "http://www.extratime.ie/squads/17/";
"team/_source" = "/squads/17/";
"team/_text" = Dundalk;
},
{
played = 9;
"played/_source" = 9;
points = 20;
"points/_source" = 20;
team = "http://www.extratime.ie/squads/7/";
"team/_source" = "/squads/7/";
"team/_text" = "Derry City";
},
{
played = 9;
"played/_source" = 9;
points = 17;
"points/_source" = 17;
team = "http://www.extratime.ie/squads/100504/";
"team/_source" = "/squads/100504/";
"team/_text" = "Galway United FC";
},
{
played = 9;
"played/_source" = 9;
points = 16;
"points/_source" = 16;
team = "http://www.extratime.ie/squads/29/";
"team/_source" = "/squads/29/";
"team/_text" = "St. Patrick's Ath";
},
{
played = 8;
"played/_source" = 8;
points = 15;
"points/_source" = 15;
team = "http://www.extratime.ie/squads/30/";
"team/_source" = "/squads/30/";
"team/_text" = "Cork City";
},
{
played = 8;
"played/_source" = 8;
points = 15;
"points/_source" = 15;
team = "http://www.extratime.ie/squads/3/";
"team/_source" = "/squads/3/";
"team/_text" = "Shamrock Rovers";
},
{
played = 9;
"played/_source" = 9;
points = 10;
"points/_source" = 10;
team = "http://www.extratime.ie/squads/13/";
"team/_source" = "/squads/13/";
"team/_text" = "Finn Harps";
},
{
played = 9;
"played/_source" = 9;
points = 10;
"points/_source" = 10;
team = "http://www.extratime.ie/squads/2/";
"team/_source" = "/squads/2/";
"team/_text" = Bohemians;
},
{
played = 9;
"played/_source" = 9;
points = 7;
"points/_source" = 7;
team = "http://www.extratime.ie/squads/8/";
"team/_source" = "/squads/8/";
"team/_text" = "Sligo Rovers";
},
{
played = 9;
"played/_source" = 9;
points = 7;
"points/_source" = 7;
team = "http://www.extratime.ie/squads/6/";
"team/_source" = "/squads/6/";
"team/_text" = "Bray Wanderers";
},
{
played = 9;
"played/_source" = 9;
points = 5;
"points/_source" = 5;
team = "http://www.extratime.ie/squads/109/";
"team/_source" = "/squads/109/";
"team/_text" = "Wexford Youths";
},
{
played = 9;
"played/_source" = 9;
points = 5;
"points/_source" = 5;
team = "http://www.extratime.ie/squads/15/";
"team/_source" = "/squads/15/";
"team/_text" = "Longford Town";
}
);
}
I'm trying to just push the "played", "points" and "team/_text" results out to each of the labels.
Since the question is very broad and doesn't specify what exactly is the problem here, The general steps are:
1) map your json to dictionary/nsdictionary. Suppose the JSON snippet you posted is a chunk of JSONArray in following format [{}], all you need to do is:
var arrayOfDictionaries:NSArray = NSJSONSerialization.JSONObjectWithData(yourData, options: nil, error: nil) as! NSArray
where yourData variable is data downloaded from network casted to NSData format
2) create outlets to these three labels in your custom tableViewCell
3) for each cell, set these labels inside
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
method as follows:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:YourCustomCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as YourCustomCell
cell.firstLabel.text = yourDictionary["played"]
cell.secondLabel.text = yourDictionary["points"]
cell.thirdLabel.text = yourDictionary["team"]
return cell
}
4) I suppose you will need more cells than just one, then store many dictionaries in array and access each element like this:
cell.firstLabel.text = arrayOfDictionaries[indexPath.row]["played"]
You'll need to create a subclass of UITableViewCell, say MyTableViewCell and add a property named JSON.
Now, since you're probably using Interface Builder to define your cell and its reuse identifier ("Cell"), set that cell's class to your newly created MyTableViewCell and connect the labels to some IBOutlets in your newly defined class.
Then, when you call 'dequeueReusableCellWithIdentifier', cast the cell to MyTableViewCell and set its JSON property to the value you want to have in the cell.
You'll probably want to react to the change, so add the didSet property observer.
var JSON:[String: AnyObject] = [String: AnyObject]() {
didSet {
print("populate your labels with your new data");
}
}
First, you should create a model that contain 3 properties that you want to save, like:
class Data {
var team = ""
var point = 0
var teamText = ""
init(fromJSON json: NSDictionary) {
team = json["team"] as! String
point = json["points"] as! Int
teamText = json["team/_text"] as! String
}
}
In your LeagueTableController, create an array to hold the data and show it to tableView:
var data = [Data]()
Config the tableView to show the data:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! YourCustomCell
let row = indexPath.row
cell.lblA.text = data[row].team
cell.lblB.text = "\(data[row].point)"
cell.lblC.text = data[row].teamText
return cell
}
Finally, parse your response json to our data array to display onto tableView
#IBOutlet weak var tableView: UITableView!
var data = [Data]()
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(.GET, "https://api.import.io/store/connector/88c66c----9b01-6bd2bb--d/_query?input=webpage/url:----") .responseJSON { response in // 1
print(response.request) // original URL request
print(response.response) // URL response
print(response.data) // server data
print(response.result) // result of response serialization
if let JSON = response.result.value {
print("JSON: \(JSON)")
// parse JSON to get array of data
let array = JSON["results"] as! [NSDictionary]
// map an item to a data object, and add to our data array
for item in array {
self.data.append(Data(fromJSON: item))
}
// after mapping, reload the tableView
self.tableView.reloadData()
}
}
}
Hope this help!

How to sort A - z in table view swift 2.0

i have one table view, which will dipslay the json data from one url.Its all working fine. What i need is?. in my table view i have one lable called "name label ".Which will display the name in my table view.
And i have one menu option with one button name called" sort data A - z ".When i click that, my table view data should reload with sorting the date from A-z alphabets order ( My name title ).
This is my button action :
#IBAction func sortByAZBtnPress(sender: AnyObject) {
}
My viewcontroller.swift
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var RightMenu: UIView!
#IBOutlet weak var tableView: UITableView! // UITable view declaration
#IBOutlet weak var Resultcount: UILabel! // count label
var arrDict :NSMutableArray=[] // array to store the value from json
let cellSpacingHeight: CGFloat = 5 // cell spacing from each cell in table view
override func viewDidLoad() {
super.viewDidLoad()
self.jsonParsingFromURL() // call the json method
let nib = UINib(nibName:"customCell", bundle: nil)
tableView.registerNib(nib, forCellReuseIdentifier: "cell")
RightMenu.layer.borderWidth = 1
}
// web services method
func jsonParsingFromURL () {
let url = NSURL(string: "http://sampleUrl.com”)
let session = NSURLSession.sharedSession()
let request = NSURLRequest(URL: url!)
let dataTask = session.dataTaskWithRequest(request) { (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
// print("done, error: \(error)")
if error == nil
{
dispatch_async(dispatch_get_main_queue()){
self.arrDict=(try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! NSMutableArray
//print(self.arrDict)
if (self.arrDict.count>0)
{
self.Resultcount.text = "\(self.arrDict.count) Results"
self.tableView.reloadData()
}}
// arrDict.addObject((dict.valueForKey("xxxx")
}
}
dataTask.resume()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return self.arrDict.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
// calling each cell based on tap and users ( premium / non premium )
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:customCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! customCell
cell.vendorName.text=arrDict[indexPath.section] .valueForKey("name") as? String
cell.vendorAddress.text=arrDict[indexPath.section] .valueForKey("address") as? String
return cell
}
// MARK:
// MARK: Sort Method
#IBAction func sortByRevBtnPress(sender: AnyObject) {
self.indicator.startAnimating()
self.indicator.hidden = false
RightMenu.hidden = true
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (Int64)(1 * NSEC_PER_SEC)), dispatch_get_main_queue()){
self.indicator.stopAnimating()
self.indicator.hidden = true
};
self.tableView.reloadData()
}
#IBAction func sortByAZBtnPress(sender: AnyObject) {
}
}
Please help me out. I am not getting clear idea to do that...Thanks
Updated :
(
{
ad = 0;
address = "900 York Mills Rd, Toronto, ON M3B 3H2, Canada";
"category_id" = 1;
latitude = "43.7563";
longitude = "-79.3495";
name = "Honeybee Restaurant";
phone = 9876543210;
rating = "5.0";
},
{
ad = 1;
address = "1677 Wilson Ave, Toronto, ON M3L 1A5, Canada";
"category_id" = 1;
latitude = "43.7194";
longitude = "-79.5153";
name = "Maki My Way";
phone = 9875463254;
rating = "4.0";
},
{
ad = 1;
address = "75 Bremner Blvd, Toronto, ON M5J 0A1, Canada";
"category_id" = 1;
latitude = "43.6429";
longitude = "-79.3814";
name = "Blow Fish Restaurant";
phone = 9873245610;
rating = "5.0";
},
{
ad = 0;
address = "4150 Yonge St, Toronto, ON M2P 2C6, Canada";
"category_id" = 1;
latitude = "43.747";
longitude = "-79.4079";
name = "SaigonFlower Restaurant";
phone = 7892345621;
rating = "3.0";
},
{
ad = 1;
address = "604 King St W, Toronto, ON M5V 1M6, Canada";
"category_id" = 1;
latitude = "43.6445";
longitude = "-79.4004";
name = "Sushi Gen";
phone = 7456321065;
rating = "2.0";
},
)
Assuming that your array is an array of dictionaries and your dictionary has a "Name" key you can do this. Change the code if your dict has different keys that contains the name.
#IBAction func sortByAZBtnPress(sender: AnyObject) {
arrDict.sortUsingComparator { (dict1, dict2) -> NSComparisonResult in
if let name1 = dict1["name"] as? String, name2 = dict2["name"] as? String {
return name1.compare(name2)
}
return .OrderedAscending
}
self.tableView.reloadData()
}
For order by rating (High to low) you can use this.
arrDict.sortUsingComparator { (dict1, dict2) -> NSComparisonResult in
if let name1 = dict1["rating"] as? String, name2 = dict2["rating"] as? String {
return name2.compare(name1)
}
return .OrderedAscending
}
For sorting of the tableview you should sort youre dataSource firstly and than reload tableview.
In you're case:
Firstly sort by A to z:
arrDict
For sorting use functional programming:
arrDict = arrDict.sort({ $0.name > $1.name })
Secondly reload tableview by:
self.tableView.reloadData()
sort arrDict using below code then self.tableView.reloadData():
sortedArray = [anArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];

Refactor cellForRowIndexPath in UITableView Swift

I have a rather long cellForRowAtIndexPath function. I am using parse as my backend and have a lot going on. I want to extract a lot of these conditions and put them in their own functions. Especially the PFUser query, but unfortunately I don't know whats the best way to go about it since I don't know how I can access the elements of each cell in those functions I want to write.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("PostCells", forIndexPath: indexPath) as! NewsFeedTableCellTableViewCell
// Configure the cell...
// A drive is a post
let drive: PFObject = self.timelineData[indexPath.row] as PFObject
var driverId = drive.objectForKey("driver")!.objectId!
var currentUserObjectId = PFUser.currentUser()!.objectId
if(driverId != currentUserObjectId){
cell.requestButton.layer.borderWidth = 1
cell.requestButton.titleLabel!.font = UIFont.systemFontOfSize(11)
cell.requestButton.tintColor = UIColor.orangeColor()
cell.requestButton.layer.borderColor = UIColor.orangeColor().CGColor
cell.requestButton.setTitle("REQUEST", forState: UIControlState.Normal)
}
else {
cell.requestButton.layer.borderWidth = 1
cell.requestButton.titleLabel!.font = UIFont.systemFontOfSize(11)
cell.requestButton.tintColor = UIColor.grayColor()
cell.requestButton.layer.borderColor = UIColor.lightGrayColor().CGColor
cell.requestButton.setTitle("REQUEST", forState: UIControlState.Normal)
cell.requestButton.enabled = false
}
// Setting up the attributes of the cell for the news feed
cell.driveTitleTextField.text = drive.objectForKey("title") as! String
cell.wayTextField.text = drive.objectForKey("way") as! String
var departureDate = NSDate()
departureDate = drive.objectForKey("departureDate") as! NSDate
var dateFormat = NSDateFormatter()
dateFormat.dateFormat = "M/dd hh:mm a"
cell.departureDateTextField.text = dateFormat.stringFromDate(departureDate)
if((drive.objectForKey("way")!.isEqualToString("Two Way")))
{
var returnDate = NSDate()
returnDate = drive.objectForKey("returnDate") as! NSDate
cell.returningDateTextField.text = dateFormat.stringFromDate(returnDate)
}
else if((drive.objectForKey("way")!.isEqualToString("One Way")))
{
cell.returningDateTextField.enabled = false
cell.returningDateTextField.userInteractionEnabled = false
cell.returningDateTextField.hidden = true
cell.returningLabel.hidden = true
}
var seatNumber = NSNumber()
seatNumber = drive.objectForKey("seatNumber") as! NSInteger
var numberFormat = NSNumberFormatter()
numberFormat.stringFromNumber(seatNumber)
cell.seatNumberTextField.text = numberFormat.stringFromNumber(seatNumber)
// this is a PFUser query so we can get the users image and name and email from the User class
var findDrive = PFUser.query()
var objectId: AnyObject? = drive.objectForKey("driver")!.objectId!
findDrive?.whereKey("objectId", equalTo: objectId!)
findDrive?.findObjectsInBackgroundWithBlock{
(objects:[AnyObject]?, error:NSError?)->Void in
if (error == nil){
if let actualObjects = objects {
let possibleUser = (actualObjects as NSArray).lastObject as? PFUser
if let user = possibleUser {
cell.userProfileNameLabel.text = user["fullName"] as? String
cell.userEmailLabel.text = user["username"] as? String
//Profile Image
cell.profileImage.alpha = 0
if let profileImage = user["profilePicture"] as? PFFile {
profileImage.getDataInBackgroundWithBlock{
(imageData:NSData? , error:NSError?)-> Void in
if(error == nil) {
if imageData != nil{
let image:UIImage = UIImage (data: imageData!)!
cell.profileImage.image = image
}
}
}
}
UIView.animateWithDuration(0.5, animations: {
cell.driveTitleTextField.alpha = 1
cell.wayTextField.alpha = 1
cell.profileImage.alpha = 1
cell.userProfileNameLabel.alpha = 1
cell.userEmailLabel.alpha = 1
cell.seatNumberTextField.alpha = 1
cell.returningDateTextField.alpha = 1
cell.departureDateTextField.alpha = 1
})
}
}
}
}
return cell
}
EDIT 1
I came up with a way to refactor my code that I would like critiqued!
1. I extracted a lot of the cells configurations and put them into to functions, one for the button on the cell and the other for all the data from parse.
func configureDataTableViewCell(cell:NewsFeedTableCellTableViewCell, drive: PFObject)
{
cell.driveTitleTextField.text = drive.objectForKey("title") as! String
cell.wayTextField.text = drive.objectForKey("way") as! String
cell.userEmailLabel.text = drive.objectForKey("username") as? String
cell.userProfileNameLabel.text = drive.objectForKey("name") as? String
var departureDate = NSDate()
departureDate = drive.objectForKey("departureDate") as! NSDate
var dateFormat = NSDateFormatter()
dateFormat.dateFormat = "M/dd hh:mm a"
cell.departureDateTextField.text = dateFormat.stringFromDate(departureDate)
if((drive.objectForKey("way")!.isEqualToString("Two Way")))
{
var returnDate = NSDate()
returnDate = drive.objectForKey("returnDate") as! NSDate
cell.returningDateTextField.text = dateFormat.stringFromDate(returnDate)
}
else if((drive.objectForKey("way")!.isEqualToString("One Way")))
{
cell.returningDateTextField.enabled = false
cell.returningDateTextField.userInteractionEnabled = false
cell.returningDateTextField.hidden = true
cell.returningLabel.hidden = true
}
var seatNumber = NSNumber()
seatNumber = drive.objectForKey("seatNumber") as! NSInteger
var numberFormat = NSNumberFormatter()
numberFormat.stringFromNumber(seatNumber)
cell.seatNumberTextField.text = numberFormat.stringFromNumber(seatNumber)
}
func configureButtonTableViewCell(cell:NewsFeedTableCellTableViewCell, userID: String)
{
var currentUserObjectId = PFUser.currentUser()!.objectId
if(userID != currentUserObjectId){
cell.requestButton.layer.borderWidth = 1
cell.requestButton.titleLabel!.font = UIFont.systemFontOfSize(11)
cell.requestButton.tintColor = UIColor.orangeColor()
cell.requestButton.layer.borderColor = UIColor.orangeColor().CGColor
cell.requestButton.setTitle("REQUEST", forState: UIControlState.Normal)
println("orange")
}
else {
cell.requestButton.layer.borderWidth = 1
cell.requestButton.titleLabel!.font = UIFont.systemFontOfSize(11)
cell.requestButton.tintColor = UIColor.grayColor()
cell.requestButton.layer.borderColor = UIColor.lightGrayColor().CGColor
cell.requestButton.setTitle("REQUEST", forState: UIControlState.Normal)
cell.requestButton.enabled = false
println("gray")
}
}
2. I then passed in the functions from step 1 and into my cellForRowIndexPath
// A drive is a post
let drive: PFObject = self.timelineData[indexPath.row] as PFObject
var driverId : String = drive.objectForKey("driver")!.objectId!!
configureButtonTableViewCell(cell, userID: driverId)
configureDataTableViewCell(cell, drive: drive)
3. I stored all my PFUser data into my object when its saved instead of querying the user class. So I get the PFUser.currentUser() username, full name, and profile picture when they save a post.
My load data has been modified. I store all the profile pictures in there own array.
func loadData(){
var findItemData:PFQuery = PFQuery(className:"Posts")
findItemData.addDescendingOrder("createdAt")
findItemData.findObjectsInBackgroundWithBlock{
(objects:[AnyObject]? , error:NSError?) -> Void in
if error == nil
{
self.timelineData.removeAll(keepCapacity: false)
self.profilePictures.removeAll(keepCapacity: false)
self.timelineData = objects as! [PFObject]
for object in objects! {
self.profilePictures.append(object.objectForKey("profilePicture") as! PFFile)
}
self.newsFeedTableView.reloadData()
}
}
}
And finally, here is my updated cellForRowIndexPath
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("PostCells", forIndexPath: indexPath) as! NewsFeedTableCellTableViewCell
// Configure the cell...
// A drive is a post
let drive: PFObject = self.timelineData[indexPath.row] as PFObject
var driverId : String = drive.objectForKey("driver")!.objectId!!
configureButtonTableViewCell(cell, userID: driverId)
configureDataTableViewCell(cell, drive: drive)
println(PFUser.currentUser()?.objectForKey("username"))
if let profileImage = drive["profilePicture"] as? PFFile {
profileImage.getDataInBackgroundWithBlock{
(imageData:NSData? , error:NSError?)-> Void in
if(error == nil) {
if imageData != nil{
let image:UIImage = UIImage (data: imageData!)!
cell.profileImage.image = image
}
}
}
}
return cell
}
Let me know what you guys think, I want to do make my code much more readable, fast, and memory efficient.
You shouldn't be doing any heavy model stuff inside cellForRow.
What you're currently trying to do will greatly slow down your UI.
In most cases you will want your model objects setup, and ready to go before you even get to cellForRow.
This means performing your Parse queries somewhere like in viewDidLoad, keep those results in an array, and when it comes time to do so, apply them to your cells in cellForRow. This way, when a user scrolls, a new query won't be dispatched for every new cell that comes into view. It will already be available.
In addition to this, should you need to make any changes to these items once they have been fetched, you can do so, and have them remain unchanged even when the user is scrolling.
Refactor so you have some data type or group of instance variables to serve as a view model. Avoid making asynchronous calls that mutate the cell in cellForRowAtIndexPath. Instead have your data access method mutate or recreate the view model and at the end of your callback, dispatch_async to the main queue. Give it a closure that tells your table view to reloadData and whatever else you need to do for views to show the new data.
Here's a little pseudocode to describe what I mean:
func loadData() {
parseQueryWithCallback() { data in
self.viewModel = doWhateverTransformsAreNeeded(data)
dispatch_async(dispatch_get_main_queue(), self.tableView.reloadData)
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) {
let cell = dequeue(...)
cell.thingOne = self.viewModel.things[indexPath.row].thingOne
cell.thingTwo = self.viewModel.things[indexPath.row].thingTwo
return cell
}

Resources