Parse JSON Swift TableView - ios

I'd like to extract "Event", "Hasta" & "Location" from this JSON URL (https://www.kimonolabs.com/api/7flcy3qm?apikey=gNq3hB1j0NtBdAvXJLEFx8JaqtDG8y6Y), but I'm struggling with how to do it? Can anyone help me? Here's my code... I'd like to then populate a tableview with these 3.
override func viewDidLoad() {
super.viewDidLoad()
splitViewController!.preferredDisplayMode = UISplitViewControllerDisplayMode.AllVisible
UINavigationBar.appearance().barTintColor = UIColor(red: 52.0/255.0, green: 170.0/255.0, blue: 220.0/255.0, alpha: 1.0)
UINavigationBar.appearance().tintColor = UIColor.whiteColor()
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName : UIColor.whiteColor()]
let url = NSURL(string:"https://www.kimonolabs.com/api/7flcy3qm?apikey=gNq3hB1j0NtBdAvXJLEFx8JaqtDG8y6Y")!
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url) { (data, response, error) -> Void in
if error != nil {
print(error)
} else {
if let data = data {
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary
if jsonResult!.count > 0 {
if let results = jsonResult!["results"] as? NSDictionary, collection2 = results["collection2"] as? NSArray {
for entry in collection2 {
if let dict = entry["Event"] as? NSDictionary {
print(dict)
}
else if let array = entry as? NSArray {
} else {
}
}
if let items = jsonResult?["Date"] as? NSArray {
print(items)
}
}
}
} catch {
print("In catch block")
}
}
}
}
task.resume()
}

Parsing JSON with Swift is hell. You can do that easily with SwiftyJSON.
With your JSON:
// Get content of json url
let jsonString = try NSString.init(contentsOfURL: url!, encoding: NSUTF8StringEncoding)
// Create JSON object from data
let json = JSON(data: jsonString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!)
// Check if array for key "collection2" exists
if let collection2 = json["results"]["collection2"].array {
// Create JSON array from it and loop for each object
for (key, subJson):(String, JSON) in JSON(collection2) {
// Check if dictionary for key "Event" exists
if let event = subJson["Event"].dictionary {
print(event)
}
// Check if string for key "Hasta" exists
if let hasta = subJson["Hasta"].string {
print(hasta)
}
// Check if string for key "Location" exists
if let location = subJson["Location"].string {
print(location)
}
}
}

I created this online utility (http://www.json4swift.com) that transforms your json into swift representable models that you can manipulate as easily as this:
// Get content of json url
let jsonString = try NSString.init(contentsOfURL: url!, encoding: NSUTF8StringEncoding)
// Create JSON Dictionary from data
var jsonResult = NSJSONSerialization.JSONObjectWithData(jsonString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary
//Create instance for base model representation
let responseModel = Json4Swift_Base(dictionary: jsonResult)
//print name
print(responseModel!.name)
//Get the collection2 from result
let collection2 = responseModel?.results!.collection2
//Get the first object from collection 2
let firstObject = collection2?.first
//Print the event and hesta
print(firstObject?.event?.text)
print(firstObject?.hasta)
In case of a tableview, you'll implement the delegate method cellForRowAtIndexPath
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("YourCellIdentifier")
if cell == nil {
cell = UITableViewCell()
}
//Assuming you have responseModel instantiated earlier
let collection2 = responseModel?.results!.collection2!
//Get the n'th object from collection 2
let object = collection2[indexPath.row]
//Populate the cell the event and hesta
cell.textLabel?.text = object?.event?.text
cell.detailTextLabel?.text = object?.hasta
return cell
}
Disclaimer: Consider the above as more of a pseudo-code, not actually tested but gives you the idea about the handling.

Related

I got stuck in fetching data from JSON API, it's little bit complex and nested, I want to Get "resource_uri" from "abilities" Array

class ViewController:UIViewController,UITableViewDelegate,UITableViewDataSource {
//I got stuck in fetching data from JSON API, its little bit complex and //nested, anyone plz help me, I want to Get "resource_uri" from "abilities" //Array
#IBOutlet weak var tblData: UITableView!
final let urlString = "[https://pokeapi.co/api/v1/pokemon/][1]"
var lableArray = [String]()
var resource_uri = [String]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lableArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let ceL = tableView.dequeueReusableCell(withIdentifier: "CeL") as! Celll
ceL.lbl.text = lableArray[indexPath.row]
return ceL
}
override func viewDidLoad() {
super.viewDidLoad()
downloadJsonWithURL()
}
func downloadJsonWithURL() {
let url = NSURL(string: urlString)
URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in
if let jsonDict = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
//print(jsonDict!)
for _ in jsonDict!{
if let subDict = jsonDict?.value(forKey: "meta") as? NSDictionary {
if let name = subDict.value(forKey: "next") {
self.lableArray.append(name as! String)
//print(self.lableArray)
}
if let actorArray = subDict.value(forKey: "objects") as? NSArray {
if let name = actorArray.value(forKey: "abilities") as? NSDictionary {
if let name = name.value(forKey: "resource_uri") as? NSArray
{
self.resource_uri.append(name as! String)
print(self.resource_uri)
}
}
}
}
}
}
OperationQueue.main.addOperation({
self.tblData.reloadData()
})
}).resume()
}
}
This is a pretty complex response to parse, and pokeapi allows you to drill down and ge the data you need easier.
However, this part should be an array:
if let name = actorArray.value(forKey: "abilities") as? NSDictionary
Probably like this:
if let dict = actorArray.value(forKey: "abilities") as? [NSDictionary]
Then you need iterate through the dict and get the uri someway similar to this:
if let dict = actorArray.value(forKey: "abilities") as? NSDictionary {
for dictionary in dict {
if let uri = dict["resource_uri"] as? String {
// Do something with uri here
}
}
}
Two ways:
Past your desired URL(https://pokeapi.co/api/v1/pokemon/) to any browser and copy and Past your output (JSON) to Online JSON Editor and analyse what you can convert to model, then create model class(inspired from JSON) and convert and mapped.
Quick solution: Pass your result(JSON) to Object Mapper Github and it will finally give you model or array of models.
Hope this will help.
Happy coding.
Please use below code to get resource URI and abilities array
func downloadJsonWithURL() {
let url = NSURL(string: urlString)
URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in
if let jsonDict = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
print(jsonDict!)
if let subDict = jsonDict?.value(forKey: "meta") as? NSDictionary {
if let name = subDict.value(forKey: "next") {
self.lableArray.append(name as! String)
}
}
if let objectsArray = jsonDict?.value(forKey: "objects") as? NSArray {
if let actorArray = (objectsArray.firstObject as? NSDictionary)?.value(forKey: "abilities") as? [NSDictionary]{
for dictionary in actorArray {
if let uri = dictionary["resource_uri"] as? String {
self.resource_uri.append(uri)
}
}
}
}
}
OperationQueue.main.addOperation({
print("resource uri \(self.resource_uri)")
print("labelarray \(self.lableArray)")
})
}).resume()
}

How to show all data in table view during pagination in swift 3?

Here i had implemented pagination for the table view and items are loaded by using model class but here the loaded items are replacing with the new items and whenever it calls api it returns the new data and old data is overriding on it and displaying only 10 items at a time i am implementing it for first time can anyone help me how to resolve the issue ?
func listCategoryDownloadJsonWithURL(listUrl: String) {
let url = URL(string: listUrl)!
print(listUrl)
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.listClassModel = ModelClass(dict: jsonObj as [String : AnyObject])
DispatchQueue.main.async {
guard let obj = self.listClassModel else { return }
let itemsCount = obj.items.count
print(itemsCount)
for i in 0..<itemsCount {
let customAttribute = obj.items[i].customAttribute
for j in 0..<customAttribute.count {
if customAttribute[j].attributeCode == "image" {
let baseUrl = "http://192.168.1.11/magento2/pub/media/catalog/product"
self.listCategoryImageArray.append(baseUrl + customAttribute[j].value)
print(self.listCategoryImageArray)
}
}
}
self.activityIndicator.stopAnimating()
self.activityIndicator.hidesWhenStopped = true
self.collectionView.delegate = self
self.collectionView.dataSource = self
self.collectionView.reloadData()
self.collectionView.isHidden = false
self.tableView.reloadData()
}
}
} catch {
print(error)
}
}
task.resume()
}
You are assigning your result data to model array, each time you call your API. This is the reason that your old data is getting replaced with new one. Rather than assigning, you should append the new data to your datasource array.
if let jsonObj = try JSONSerialization.jsonObject(with: data!) as? [String:Any] {
self.listClassModel.append(contentsOf: ModelClass(dict: jsonObj as [String : AnyObject]))
Also make sure you initialize your array as an empty array first. (maybe in declaration or viewDidLoad) before calling API.

UITableCell value not passing to function within UIViewController Swift 3

I have a table that is populated by a search function. There are two buttons within the cell, a checkmark to say yes to a user and an X to say no. There is an insert function that inserts the selection into the database. Unfortunately the value from the table is not being passed to the insert function. Within the insert function, I'm using guestusername.text which is the name of the label in my cell. I'm getting the error 'Use of unresolved identifier guestusername'. I've tried everything I can think of, code below.
class MyShotsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var guest = [AnyObject]()
var avas = [UIImage]()
var valueToPass:String!
var revieweduser:String!
var age = [AnyObject]()
var city = [AnyObject]()
var state = [AnyObject]()
#IBOutlet var tableView: UITableView!
var cell: MyShotsCell?
var index = 0
override func viewDidLoad() {
super.viewDidLoad()
doSearch("")
}
// cell numb
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return guest.count
}
// cell config
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! MyShotsCell
// get one by one user related inf from users var
let guest2 = guest[indexPath.row]
let ava = avas[indexPath.row]
// shortcuts
let guestname = guest2["username"] as? AnyObject
let age = guest2["age"]
let city = guest2["city"] as? String
let state = guest2["state"] as? String
// refer str to cell obj
cell.guestusername.text = guestname as! String
cell.ageLbl.text = (NSString(format: "%#", age as! CVarArg) as String)
cell.cityLbl.text = city
cell.stateLbl.text = state
cell.avaImg.image = ava as? UIImage
return cell
}
// search / retrieve users
public func doSearch(_ guestusername : String) {
// shortcuts
let username = user?["username"] as! String
let url = URL(string: "http://www.xxxxx.com/xxxxx.php")!
var request = URLRequest(url: url) // create request to work with users.php file
request.httpMethod = "POST" // method of passing inf to users.php
let body = "revieweduser=\(username)" // body that passes inf to users.php
request.httpBody = body.data(using: .utf8) // convert str to utf8 str - supports all languages
// launch session
URLSession.shared.dataTask(with: request) { data, response, error in
// getting main queue of proceeding inf to communicate back, in another way it will do it in background
// and user will no see changes :)
DispatchQueue.main.async(execute: {
if error == nil {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
// clean up
self.guest.removeAll(keepingCapacity: false)
self.avas.removeAll(keepingCapacity: false)
self.tableView.reloadData()
// delcare new secure var to store json
guard let parseJSON = json else {
print("Error while parsing")
return
}
guard let parseUSERS = parseJSON["users"] else {
print(parseJSON["message"] ?? [NSDictionary]())
return
}
self.guest = parseUSERS as! [AnyObject]
print(self.guest)
// for i=0; i < users.count; i++
for i in 0 ..< self.guest.count {
// getting path to ava file of user
let ava = self.guest[i]["ava"] as? String
let revieweduser = self.guest[i]["username"] as? String
let age = (NSString(format: "%#", self.guest[i]["age"] as! CVarArg) as String)
let city = self.guest[i]["city"] as? String
let state = self.guest[i]["state"] as? String
self.tableView.reloadData()
} catch {
DispatchQueue.main.async(execute: {
let message = "\(error)"
appDelegate.infoView(message: message, color: colorSmoothRed)
})
return
}
} else {
DispatchQueue.main.async(execute: {
let message = error!.localizedDescription
appDelegate.infoView(message: message, color: colorSmoothRed)
})
return
}
})
} .resume()
}
// custom body of HTTP request to upload image file
func createBodyWithParams(_ parameters: [String: String]?, boundary: String) -> Data {
let body = NSMutableData();
if parameters != nil {
for (key, value) in parameters! {
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString("\(value)\r\n")
}
}
return body as Data
}
func insertShot(_ rating : String) {
self.tableView.reloadData()
let reviewer = user?["username"] as! String
// url path to php file
let url = URL(string: "http://www.xxxxxx.com/xxxxxxx.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
// param to be passed to php file
let param = [
"user" : reviewer,
"revieweduser" : cell?.guestusername.text,
"rating" : rating
] as [String : Any]
// body
let boundary = "Boundary-\(UUID().uuidString)"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
// ... body
request.httpBody = createBodyWithParams(param as? [String : String], boundary: boundary)
// launch session
URLSession.shared.dataTask(with: request) { data, response, error in
// get main queu to communicate back to user
DispatchQueue.main.async(execute: {
if error == nil {
do {
// json containes $returnArray from php
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
// declare new var to store json inf
guard let parseJSON = json else {
print("Error while parsing")
return
}
// get message from $returnArray["message"]
let message = parseJSON["message"]
//print(message)
// if there is some message - post is made
if message != nil {
// reset UI
// self.msgTxt.text = ""
// switch to another scene
//self.tabBarController?.selectedIndex = 3
_ = self.navigationController?.popViewController(animated: true)
}
} catch {
// get main queue to communicate back to user
DispatchQueue.main.async(execute: {
let message = "\(error)"
appDelegate.infoView(message: message, color: colorSmoothRed)
})
return
}
} else {
// get main queue to communicate back to user
DispatchQueue.main.async(execute: {
let message = error!.localizedDescription
appDelegate.infoView(message: message, color: colorSmoothRed)
})
return
}
})
}.resume()
return
}
#IBAction func yesBtn_clicked(_ sender: UIButton) {
self.insertShot("Yes")
}
#IBAction func noBtn_clicked(_ sender: UIButton) {
self.insertShot("No")
}
}

Swift JSON Get into TableViewCell

My Data format
{
"StationID": "1001",
"StationName": {
"Zh_tw": "基隆",
"En": "Keelung"
},
"TrainNo": "1281",
"Direction": 1,
"TrainClassificationID": "1131",
"TripLine": 0,
"EndingStationID": "1025",
"EndingStationName": {
"Zh_tw": "新竹",
"En": "Hsinchu"
},
"ScheduledArrivalTime": "22:02:00",
"ScheduledDepartureTime": "22:04:00",
"DelayTime": 0,
"Platform": "",
"SrcUpdateTime": "2017-01-24T22:14:29+08:00",
"UpdateTime": "2017-01-24T22:14:40+08:00"
},
My Code (Swift 3)
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TableViewCell
// Configure the cell...
cell.stationID.text = trainStatusArray[indexPath.row]["StationID"] as? String
let stationDirect = trainStatusArray[indexPath.row]["Direction"] as? Int
if stationDirect == 0 {
cell.stationdirection.text = "順行"
}else{
cell.stationdirection.text = "逆行"
}
cell.stationtrainNo.text = trainStatusArray[indexPath.row]["TrainNo"] as? String
let stationTripline = trainStatusArray[indexPath.row]["TripLine"] as? Int
if stationTripline == 0 {
cell.stationtripLine.text = "不經山線/海線"
}else if stationTripline == 1 {
cell.stationtripLine.text = "山線"
}else {
cell.stationtripLine.text = "海線"
}
//cell.stationtripLine.text = String(stationTripline!)
return cell
}
My question is how to get StationName, Zh_tw, and En transfer to TableView cell?
StationName and EndingStationName both are Dictionary so you can get value of Zh_tw like this way.
if let stationName = trainStatusArray[indexPath.row]["StationName"] as? [String:Any],
let zhTW = stationName ["Zh_tw"] as? String, let en = stationName ["En"] as? String {
cell.stationName.text = zhTW
}else {
cell.stationName.text = ""//Set default name
}
Same goes for EndingStationName also.
Note: Instead of using Array of dictionary if you create array of custom object from this dictionary it will lot easy for you assign label text in cellForRowAt method.
First convert JSON String into Data and again convert Data into Dictionary form using JSONSerialization
Swift 3
func convertToDictionary(text: String) -> [String: Any]? {
if let data = text.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
print(error.localizedDescription)
}
}
return nil
}
// pass your String JSON
let dict = convertToDictionary(text: str)
Swift 2
func convertStringToDictionary(text: String) -> [String:AnyObject]? {
if let data = text.dataUsingEncoding(NSUTF8StringEncoding) {
do {
return try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String:AnyObject]
} catch let error as NSError {
print(error)
}
}
return nil
}
get dictonary from String
let dict= convertStringToDictionary(str)
if let strStationName= result?["EndingStationName"] {
let zhTW = strStationName["Zh_tw"] as? String
cell.stationdirection.text=zhTW
}

How to populate my tableview with a mutablearray from json

So I'm fetching data from a url which is in a json format. I'm trying to display the data in my tableview but, even though it feels simple, I can't figure out how to do it.
class CompanyModel {
func getJSON() {
let companyArray: NSMutableArray = NSMutableArray()
let requestURL: NSURL = NSURL(string: "http://localhost/Companies/JSON.php")!
let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(urlRequest) {
(data, response, error) -> Void in
let httpResponse = response as! NSHTTPURLResponse
let statusCode = httpResponse.statusCode
if (statusCode == 200) {
print("Everyone is fine, file downloaded successfully.")
do{
let json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments)
if let companies = json["companies"] as? [[String: AnyObject]] {
for company in companies {
if let name = company["name"] as? String,
let phoneNumber = company["phone_number"] as? String,
let website = company["website"] as? String,
let email = company["email"] as? String,
let address = company["address"] as? String
{
let company = CompanyModel()
company.name = name
company.phoneNumber = phoneNumber
company.website = website
company.email = email
company.address = address
}
companyArray.addObject(company)
print(companyArray)
}
}
} catch {
print("Error with Json: \(error)")
}
}
print(companyArray) <- array is populated
}
print(companyArray) <- array is empty
task.resume()
}
}
I know i've done it before....I'm guessing in viewDidLoad() I'd call CompanyModel().getJSON() which would fetch the data, then store it in an empty array but my mind feels blank on how to do it.
I can't declare a variable of NSarray and store the data of it the variable directly for me to then populate the tableview. Nevertheless, I hope this explains what I'm trying to acheive.
Well first change the function to return your company array :
func getJSON() -> NSMutableArray {
}
By the end of the for loop return the company array
for company in companies {
}
After your array is populated, return the array inside this block:
dispatch_async(dispatch_get_main_queue(), {
return companyArray
})
And after task.resume() return the array:
return companyArray
From anywhere you wanna call this class and get the array :
Get a reference of the class
Let companyModal = CompanyModel()
And in anywhere you have your table view and the class let's say in viewDidLoad, you should first have NSMutableArray.
var arraySource = NSMutableArray()
And in viewDidLoad :
arraySource = companyModal.getJSON()
And to show the data in tableView do :
Mytableview.reloadData()
You can't use return within the closure of an asynchronous network request, you have to use a callback instead.
You need a NSMutableArray from the request, so first, let's make a callback for this:
completion: (array: NSMutableArray)->()
We add this callback to the method signature:
func getJSON(completion: (array: NSMutableArray)->())
And then at the location where the array will be available, we place this completion handler:
class CompanyModel {
func getJSON(completion: (array: NSMutableArray)->()) {
let companyArray: NSMutableArray = NSMutableArray()
let requestURL: NSURL = NSURL(string: "http://localhost/Companies/JSON.php")!
let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(urlRequest) {
(data, response, error) -> Void in
let httpResponse = response as! NSHTTPURLResponse
let statusCode = httpResponse.statusCode
if (statusCode == 200) {
do{
let json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments)
if let companies = json["companies"] as? [[String: AnyObject]] {
for company in companies {
if let name = company["name"] as? String,
let phoneNumber = company["phone_number"] as? String,
let website = company["website"] as? String,
let email = company["email"] as? String,
let address = company["address"] as? String {
let company = CompanyModel()
company.name = name
company.phoneNumber = phoneNumber
company.website = website
company.email = email
company.address = address
companyArray.addObject(company)
}
}
// CALLBACK HERE
completion(array: companyArray)
}
} catch {
print("Error with Json: \(error)")
}
}
}
task.resume()
}
}
Now to get the array from the network we use a trailing closure like this:
getJSON { (array) in
print(array)
}

Resources