Swift JSON Get into TableViewCell - ios

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
}

Related

iTunes API - JSON Array Error Swift4, Req. to show in Collection View as Array

My json response is ,
{ resultCount = 32;
results = (
{
artistId = 909253;
artistName = "Jack Johnson";
country = USA;
currency = USD;
kind = "music-video";
},
{
artistId = 909253;
artistName = "Jack Johnson";
country = UK;
currency = USD;
kind = "music-video";
}
I have written code for view load to call the get method,
Alamofire.request("https://itunes.apple.com/search?term=jackjohnson&entity=musicVideo").responseJSON { response in
debugPrint(response)
if let json = response.result.value //getting json
{
print(json)
let jobsArray : NSArray = json as! AnyHashable as! NSArray //converting json to NSArray
if jobsArray.count > 0
{
for object in jobsArray
{
if let singleDict = object as? NSDictionary
{
self.arrFavAdsList.add(singleDict)
print(self.arrFavAdsList)
}
}
DispatchQueue.main.async() {
self.collectionView.reloadData()
}
//displaying data in tableview
}
}
}
But its showing json array error.. I need to get the array response as dictionary and show it in my Collection View
First of all declare variables in your view controller or elsewhere
var mArray:[Music] = []
Try to use Codable/Decodable Json parse in order to fetch ApiResults,
struct ApiResults:Decodable {
let resultCount: Int
let results: [Music]
}
struct Music:Decodable {
let artistId: Int?
let artistName: String?
let country: String?
let currency: String?
let kind: String?
}
Now, try in your viewdidload or call it as function where ever wish to be...
func callAPI() {
guard let url = URL(string: "https://itunes.apple.com/search?term=jackjohnson&entity=musicVideo") else {return}
URLSession.shared.dataTask(with: url){(data, response, error) in
guard let data = data else {return}
do
{
let apiressults = try JSONDecoder().decode(ApiResults.self, from: data)
for item in apiressults.results
{if let track_Name = item.trackName, let artist_Name = item.artistName, let country = item.country, let currency = item.currency, let kind = item.kind
{let musics = Music(artistId: artist_Id, artistName: artist_Name, country: country, currency: currency, kind: kind)
self.mArray = apiressults.results
}
}
DispatchQueue.main.async
{
self.collectionView.reloadData()
} }
catch let jsonError
{
print("Error:", jsonError)
}
}.resume()
}
Now at last, as u mentioned in order to show it in collection view as array/list.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return mArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell // your cell identifier..
cell.label1.text = mArray[indexPath.row].trackName
cell.label2.text = mArray[indexPath.row].trackId
cell.label3.text = mArray[indexPath.row].currency.
///////like so for other stuffs which u need to show in collection view.
}
return cell
}
Your full JSON content is the dictionary, So you need to convert first JSON string to a dictionary, Use following code to convert JSON into a dictionary, and then extract result array from dictionary and load into your UICollectionview
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
}
Your JSON's content is a Dictionary and you cannot convert it that way. Here is a working version:
Alamofire.request("https://itunes.apple.com/search?term=jackjohnson&entity=musicVideo").responseJSON { response in
if let json = response.result.value as? [String: Any], let tracks = json["results"] as? Array<[String: Any]>, tracks.count > 0 {
for track in tracks {
// do your stuffs with track here.
}
}
}
However, I'd prefer you to use Codable/Decodable to parse JSON in Swift. You can take a look at following example for reference:
struct APIResult: Codable {
struct Track: Codable {
let kind: String
let artistName: String
let name: String
enum CodingKeys : String, CodingKey {
case kind
case artistName
case name = "trackName"
}
}
let resultCount: Int
let tracks: [Track]
enum CodingKeys : String, CodingKey {
case resultCount
case tracks = "results"
}
}
// and then use it like following:
Alamofire.request("https://itunes.apple.com/search?term=jackjohnson&entity=musicVideo").response { response in
let decoder = JSONDecoder()
let tracks = try! decoder.decode(APIResult.self, from: response.data!).tracks
for track in tracks {
// do your stuffs with track here.
}
}
Happy coding!

TableView Array Map

My code is swift 3 and has create two array in TableView, get JSON data into Array, if I map this two array will get error index out of range, How to right map Array ?
func trainType() {
do {
/// 取得所有列車車種資料
let trainTypeUrl = URL(string: "http://ptx.transportdata.tw/MOTC/v2/Rail/TRA/TrainClassification?$format=JSON")
let trainTypeData = try? Data(contentsOf: trainTypeUrl!)
let trainTypeJson = try? JSONSerialization.jsonObject(with: trainTypeData!, options: .mutableContainers)
if let trainTypes = trainTypeJson as? [[String : AnyObject]] {
for dataTrainType in trainTypes {
trainTypeArray.append(dataTrainType as AnyObject)
}
}
}
self.tableView.reloadData()
}
func trainInOutstatusData() {
do {
let trainStatusUrl = URL(string: "http://ptx.transportdata.tw/MOTC/v2/Rail/TRA/LiveBoard?$format=JSON")
let trainInOutstatusData = try? Data(contentsOf: trainStatusUrl!)
let trainInOutStatusjson = try? JSONSerialization.jsonObject(with: trainInOutstatusData!, options: .mutableContainers)
if let InOutStatus = trainInOutStatusjson as? [[String : AnyObject]] {
for dataInOutStatus in InOutStatus {
trainStatusArray.append(dataInOutStatus as AnyObject!)
}
}
}
self.tableView.reloadData()
}
map array
let stationClassID = trainStatusArray[indexPath.row]["TrainClassificationID"] as? String
let trainClassID = trainTypeArray[indexPath.row]["TrainClassificationID"] as? String
if stationClassID == trainClassID {
if let trainTypeID = trainTypeArray[indexPath.row]["TrainClassificationID"] as? [String : Any] {
let ZHtw = trainTypeID["Zh_tw"] as? String
cell.stationTrainClassID.text = ZHtw
}
}
Since you have different data counts in both array, you can't do as you tried. Because consider you have 5 count in trainStatusArray and 3 count trainTypeArray, your current indexPath.row is 4. Then
let stationClassID = trainStatusArray[4]["TrainClassificationID"] as? String // You will get some value here.
let trainClassID = trainTypeArray[4]["TrainClassificationID"] as? String // Here app will crash because, total count is 3 in but you are trying to access element at index 4.
Solution:
You can give an array which have higher count as data source to tableView, then you can use for loop in cellForRowAtIndexpath to check the values are matching or not.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let stationClassID = trainStatusArray[indexPath.row]["TrainClassificationID"] as? String
for trainType in trainTypeArray {
let trainClassID = trainType["TrainClassificationID"] as? String
if stationClassID == trainClassID {
if let trainTypeID = trainTypeArray[indexPath.row]["TrainClassificationID"] as? [String : Any] {
let ZHtw = trainTypeID["Zh_tw"] as? String
cell.stationTrainClassID.text = ZHtw
}
}
}
}
Thanks.

Table View not updating with data fetched by HTTP Request

I'm trying to populate a table view with data fetched from a http request (JSON) but it's not working. If I populate the Table View with static data everything works fine.. code snippet:
import UIKit
import Alamofire
//import SwiftyJSON
class ViewControllerNews : UITableViewController{
#IBOutlet var table: UITableView!
var posts = [FacebookPost]()
override func viewDidLoad() {
pullFacebookNews()
//loadSampleNews()
super.viewDidLoad()
}
func pullFacebookNews() -> Void {
let params = ["limit":"100", "access_token": myaccesstoken]
Alamofire.request( "https://graph.facebook.com/<page-id>/posts", parameters: params).responseJSON{ response in
if let responsejson = response.result.value {
let JSON = responsejson as! NSDictionary
let itemsArray: NSArray? = JSON.object(forKey: "data") as? NSArray
if(itemsArray != nil) {
for item in itemsArray! {
let thisitem = item as! NSDictionary
print(thisitem.object(forKey: "message") as? String)
print(thisitem.object(forKey:"created_time") as? String)
let title1 = thisitem.object(forKey: "message") as? String
let value1=thisitem.object(forKey:"created_time") as? String
if(title1 != nil && value1 != nil) {
let news = FacebookPost(title: title1!, value: value1!)
self.posts.append(news)
}
}}
}
}
do_table_refresh()
}
func do_table_refresh() {
DispatchQueue.global(qos: .background).async {
DispatchQueue.main.async {
self.table.reloadData()
}
}
}
func loadSampleNews() {
let news1 = FacebookPost(title: "ich bin ein datum", value: "Ich bin eine news")
let news2 = FacebookPost(title: "ich bin ein datum2", value: "Ich bin eine news2")
let news3 = FacebookPost(title: "ich bin ein datum3", value: "Ich bin eine news3")
posts += [news1, news2, news3]
}
override func numberOfSections(in: UITableView) -> Int {
return 1
}
override func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(_: UITableView, cellForRowAt: IndexPath) -> UITableViewCell {
let cellIdentifier = "NewsTableViewCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: cellForRowAt as IndexPath) as! NewsTableViewCell
let news = posts[cellForRowAt.row]
cell.date.text = news.title
cell.news_text.text = news.value
return cell
}
}
The loadSampeNews() works perfectly, but pullFacebookNews() does not populate the table. While debugging the array is filled..
If you set breakpoints, you will see that do_table_refresh() gets called before you hit if let responsejson = response.result.value. This is because the Alamofire call has a completion handler. You have to wait until the internet call is complete before you call do_table_refresh().
So you essentially just have to move do_table_refresh() if all of your data is loading correctly like you say it is.
func pullFacebookNews() -> Void {
let params = ["limit":"100", "access_token": myaccesstoken]
Alamofire.request( "https://graph.facebook.com/<page-id>/posts", parameters: params).responseJSON{ response in
if let responsejson = response.result.value {
let JSON = responsejson as! NSDictionary
let itemsArray: NSArray? = JSON.object(forKey: "data") as? NSArray
if(itemsArray != nil) {
for item in itemsArray! {
let thisitem = item as! NSDictionary
print(thisitem.object(forKey: "message") as? String)
print(thisitem.object(forKey:"created_time") as? String)
let title1 = thisitem.object(forKey: "message") as? String
let value1=thisitem.object(forKey:"created_time") as? String
if(title1 != nil && value1 != nil) {
let news = FacebookPost(title: title1!, value: value1!)
self.posts.append(news)
}
}
}
self.do_table_refresh()
}
}
}
Additionally, don't name your methods with that syntax. It is not best practice. Should be doTableRefresh() or something.

Swift Adding objects from on array to another array correctly?

So I have 2 NSMutableArrays, one called testArray and the other called jsonArray. jsonArray gets its objects from a mysql server using json and php. Then those same objects in jsonArray are inserted in testArray. I did print(jsonArray, testArray) and what showed in the logs were this.
I also have a NSObject class called Test, if that helps..
For the jsonArray
{
testName = GreenCorn;
testStatus1 = 12;
testStatus2 = 13;
testURL = "";
id = 1;
}
For the testArray
"<CustomCellSwift.Test: 0x17414df70>"
Now I'm new to iOS Swift but I don't know if I inserted the jsonArray into testArray correctly. Here is the code I used. Also, I'm using a custom tableview and its supposed to show testArray.count, its empty cells but its showing the several rows that I have in jsonArray.
var followedArray: NSMutableArray = []
var testArray: NSMutableArray = []
var jsonArray: NSMutableArray = []
var filteredArray: NSArray = []
var isFiltered: Bool = false
// Number of Rows in Section
internal func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if !isFiltered {
if section == 0 {
return followedArray.count
}
else if section == 1 {
return testArray.count
}
}
return filteredArray.count
}
internal func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let CellIdentifier = "Cell"
var cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier) as! CustomCell
if cell != cell {
cell = CustomCell(style: UITableViewCellStyle.default, reuseIdentifier: CellIdentifier)
}
// Coloring TableView
myTableView.backgroundColor = UIColor.white
// Configuring the cell
var testObject: Test
print("before ifFiltered")
if !isFiltered {
if indexPath.section == 0 {
print("isFiltered if")
testObject = followedArray[indexPath.row] as! Test
cell.populateCell(testObject, isFollowed: true, indexPath: indexPath, parentView: self)
}
else if indexPath.section == 1 {
print("isFiltered if 2")
testObject = testArray[indexPath.row] as! Test
cell.populateCell(testObject, isFollowed: false, indexPath: indexPath, parentView: self)
}
}
else {
print("isFiltered else")
testObject = filteredArray[indexPath.row] as! Test
cell.populateCell(testObject, isFollowed: false, indexPath: indexPath, parentView: self)
}
return cell
}
// Retrieving Data from Server
func retrieveData() {
let getDataURL = "http://exampleip.org/json.php"
let url: NSURL = NSURL(string: getDataURL)!
do {
let data: Data = try Data(contentsOf: url as URL)
jsonArray = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! NSMutableArray
// Setting up testArray
let testArray: NSMutableArray = []
// Looping through jsonArray
for i in 0..<jsonArray.count {
// Create Test Object
let tID: String = (jsonArray[i] as AnyObject).object(forKey: "id") as! String
let tName: String = (jsonArray[i] as AnyObject).object(forKey: "testName") as! String
let tStatus1: String = (jsonArray[i] as AnyObject).object(forKey: "testStatus1") as! String
let tStatus2: String = (jsonArray[i] as AnyObject).object(forKey: "testStatus2") as! String
let tURL: String = (jsonArray[i] as AnyObject).object(forKey: "testURL") as! String
// Add Test Objects to Test Array
testArray.add(Test(testName: tName, andTestStatus1: tStatus1, andTestStatus2: tStatus2, andTestURL: tURL, andTestID: tID))
print("retrieveData")
print(jsonArray, testArray)
}
}
catch {
print("Error: (Retrieving Data)")
}
myTableView.reloadData()
}
Am I doing this correctly? Why does my tableview have empty cells?
First, your networking/serialization code shouldn't be in your ViewController, but this is a better way to do things:
func retrieveData() {
let getDataURL = "http://exampleip.org/json.php"
let url: NSURL = NSURL(string: getDataURL)!
do {
let data: Data = try Data(contentsOf: url as URL)
guard let jsonArray = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [[String : AnyObject]] else {
print("Error Retrieving Data")
return
}
let testArray = jsonArray.flatMap(Test.init)
// make sure the add the result in your viewController
self.myTableView.models = testArray
}
catch {
print("Error: (Retrieving Data)")
}
myTableView.reloadData()
}
extension Test {
convenience init?(with jsonDictionary: [String : AnyObject]) {
guard let tID = jsonDictionary["id"] as? String, let tName = jsonDictionary["testName"] as? String, let tStatus1 = jsonDictionary["testStatus1"] as? String,
let tStatus2 = jsonDictionary["testStatus2"] as? String, let tURL = jsonDictionary["testURL"] as? String else {
return nil
}
self(testName: tName, andTestStatus1: tStatus1, andTestStatus2: tStatus2, andTestURL: tURL, andTestID: tID)
}
}
I couldn't really test it so there may be some errors, but that should point you in the right direction.
Removing this line of code worked.
let testArray: NSMutableArray = []

parsing JSON it's not working in Swift

I'm creating a simple app that shows the details of every character fo Star Wars using SWAPI. Now i'm trying to get the species but Xcode is telling me that found a nil while unwrapping an optional value when is trying to print _species. Here's the code:
func DownlaodCompleted(complete: DownloadComplete) {
let url = NSURL(string: _urlperson)!
Alamofire.request(.GET, url).responseJSON { (response: Response<AnyObject, NSError>) -> Void in
let result = response.result
if let dict = result.value as? Dictionary<String, AnyObject> {
if let height = dict["height"] as? String {
self._height = height
}
if let gender = dict["gender"] as? String {
self._gender = gender
}
if let birthYear = dict["birth_year"] as? String {
self._birthYear = birthYear
}
if let species = dict["species"] as? [Dictionary<String, String>] {
let urlSpecies = NSURL(string: self._urlSpecies)!
Alamofire.request(.GET, urlSpecies).responseJSON(completionHandler: { (response2: Response<AnyObject, NSError>) -> Void in
let result = response2.result
if let speciesDict = result.value as? Dictionary<String, AnyObject> {
if let name = speciesDict["name"] as? String {
self._species = name
}
}
})
}
print(self._species)
print(self._height)
}
}
}
and here the class of Constants:
let URL_BASE = "http://swapi.co"
let URL_PEOPLE = "/api/people/"
let URL_SPECIES = "/api/species/1/"
typealias DownloadComplete = () -> ()
Here's sample code that I used after creating an empty "Single View Project", and adding the 2 "pods" Alamofire, and SwiftyJSON to the project.
The sample code has one of your URLs and a commented out URL to another site.
import UIKit
import Alamofire
import SwiftyJSON // No such module "SwiftyJSON"
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tableView: UITableView!
var arrResults = [[String:AnyObject]]() //Array of dictionary
override func viewDidLoad() {
super.viewDidLoad()
self.view.frame = CGRect(x: 0, y: 0, width: 320, height: 480)
self.tableView = UITableView(frame:self.view!.frame)
self.tableView!.delegate = self
self.tableView!.dataSource = self
self.tableView!.registerClass(UITableViewCell.self, forCellReuseIdentifier: "jsonCell")
self.view?.addSubview(self.tableView)
Alamofire.request(.GET, "http://swapi.co/api/people/?format=json").responseJSON { (responseData) -> Void in
let swiftyJsonVar = JSON(responseData.result.value!)
for element in swiftyJsonVar["results"].arrayValue {
let name = element["name"].string!
let homeworld = element["homeworld"].string!
let object : Dictionary<String, String> = ["name": name, "homeworld": homeworld]
self.arrResults.append(object)
}
if self.arrResults.count > 0 {
self.tableView.reloadData()
}
}
// Alamofire.request(.GET, "http://api.androidhive.info/contacts/").responseJSON { (responseData) -> Void in
// let swiftyJsonVar = JSON(responseData.result.value!)
//
// if let resData = swiftyJsonVar["contacts"].arrayObject {
// self.arrResults = resData as! [[String:AnyObject]]
// }
// if self.arrResults.count > 0 {
// self.tableView.reloadData()
// }
// }
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "jsonCell")
var dict = arrResults[indexPath.row]
// Use these lines with: "http://swapi.co/api/people/?format=json"
cell.textLabel?.text = dict["name"] as? String
cell.detailTextLabel?.text = dict["homeworld"] as? String
// Use these lines with: "http://api.androidhive.info/contacts/"
// cell.textLabel?.text = dict["name"] as? String
// cell.detailTextLabel?.text = dict["email"] as? String
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrResults.count
}
}

Resources