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.
Related
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()
}
I have been trying to load facebook taggable_friends, I was able to get them but can't load them in UItableView. I tried fetching request in the method "cell for Row at" but it changes the order or refresh every 2-3 seconds. I tried declaring arrays and store the names and profile pictures but I get a crash.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "friendCell", for: indexPath) as! friendstvc
// The first try
/*FBSDKGraphRequest(graphPath: "me/taggable_friends?limit=5000", parameters: nil).start { (connection, result, error) in
if error != nil
{
print(error?.localizedDescription ?? "")
cell.friendName.text = ""
return
}
let dic = result as! Dictionary<String, Any>
let data = dic["data"] as! NSArray
if let name = dic["name"]
{
print(name as! String)
}
let valuedict = data[indexPath.row] as! Dictionary<String, Any>
let id = valuedict["id"] as! String
let name = valuedict["name"] as! String
cell.friendName.text = name
cell.profpic.profileID = id
}*/
// The second try
cell.friendName.text = friendnames[indexPath.row] // crashes here
cell.profpic.image = friendpictures[indexPath.row]
return cell
}
The below method is called in the viewDidLoad for the second try
// for the second try
func loadtaggableFriends()
{
// Called in the viewDidLoad
FBSDKGraphRequest(graphPath: "me/taggable_friends?limit=5000", parameters: nil).start { (connection, result, error) in
if error != nil{
print(error?.localizedDescription ?? "")
return
}
let dic = result as! Dictionary<String, Any>
let data = dic["data"] as! NSArray
if let name = dic["name"]
{
print(name as! String)
}
print(data.count)
for i in 0...data.count - 1
{
let valuedict = data[i] as! Dictionary <String, Any>
let name = valuedict["name"] as! String
var dataurl: Data?
if let picture = valuedict["picture"] as? NSDictionary, let data = picture["data"] as? NSDictionary, let url = data["url"] as? String
{
let loadurl = URL(string: url)
dataurl = try? Data(contentsOf: loadurl!)
}
friendnames.append(name)
friendpictures.append(UIImage(data: dataurl!)!)
}
}
}
// declaring the arrays
var friendnames = [String]()
var friendpictures = [UIImage]()
Problem is that you are using two array instead of two array use one array of dictionaries like : [[String:AnyObject]] .And way of using dictionary and inserting data into array is :
let dict = [String:AnyObject]() //create dictionary
dict["friendpicture"] = "" //
dict["friendnames"] = "" //enter your data
arrOfDictionaries.append(dict) //insert data into array
Access data inside cellForRowAt method like this :
let dict = arrOfDictionaries[indexPath.item]
friendpicture = dict["friendpicture"]
friendnames = dict["friendnames"]
In this order detail array i am having 10 dictionaries but i need to display only first dictionary can any one help me how to implement this ?
http://www.json-generator.com/api/json/get/bUKEESvnvS?indent=2
here is my code shown below
func downloadJsonWithURL() {
let url = NSURL(string: self.url)
URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
self.orderdetailsArray = (jsonObj!.value(forKey: "Orders detail") as? [[String: AnyObject]])!
for array in self.orderdetailsArray {
let key = "OrderId"
let value = "#1000501"
for (key,value) in array{
if let addressDict = array as? NSDictionary{
if let orderid = addressDict.value(forKey: "OrderId"){
self.orderid.append(orderid as! String)
}
if let orderdate = addressDict.value(forKey: "OrderDate"){
self.orderdate.append(orderdate as! String)
}
if let subtotal = addressDict.value(forKey: "SubTotal"){
self.subTotal.append(subtotal as! Int)
}
if let Shipping = addressDict.value(forKey: "Shipping"){
self.shippingPrice.append(Shipping as! Int)
}
if let tax = addressDict.value(forKey: "Tax"){
self.tax.append(tax as! Int)
}
if let grandtotal = addressDict.value(forKey: "GrandTotal"){
self.grandTotal.append(grandtotal as! Int)
}
if let shippingAddress = addressDict.value(forKey: "ShippingAddress"){
self.shippingAddress.append(shippingAddress as AnyObject)
}
if let shippingMethod = addressDict.value(forKey: "ShippingMethod"){
self.shippingMethod.append(shippingMethod as AnyObject)
}
if let billingAddress = addressDict.value(forKey: "BillingAddress"){
self.billingAddress.append(billingAddress as AnyObject)
}
if let paymentMethod = addressDict.value(forKey: "PayMentMethod"){
self.paymentMethod.append(paymentMethod as AnyObject)
}
self.itemsArray = addressDict.value(forKey: "Items detail") as! [[String : AnyObject]]
}
}
}
OperationQueue.main.addOperation({
self.tableDetails.reloadData()
})
}
}).resume()
}
Do this. :
let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary
guard let Ordersdetail = jsonObj["Orders detail"] as? [NSDictionary] else {
print("Cannot find key 'Orderdetails' in \(jsonObj)")
return
}
To access the contents of the first dictionary do this:
var orderid = Ordersdetail[0]["OrderId"]!
var shippingadress = Ordersdetail[0]["ShippingAddress"]!
var total = Ordersdetail[0]["GrandTotal"]!
var subtotal = Ordersdetail[0]["SubTotal"]!
var tax = Ordersdetail[0]["Tax"]!
var shipping = Ordersdetail[0]["Shipping"]!
Hi if you want first dictionary of that
self.orderdetailsArray
then
if let firstDictInfo = self.orderdetailsArray.first as? [String:Any] {
// Do your stuff here
print(firstDictInfo["OrderId"])
}
Instead of looping through the whole dictionary is dictionaries, you should just take the first dictionary and only parse that. There was also quite a few other conceptual problems with your code. In Swift, don't use NSDictionary, but use the native Swift version, Dictionary, which keeps the type information of its contents. Also, use conditional casting to make sure your program doesn't crash even if the received data is wrong/unexpected and don't use force unwrapping of optionals.
Also, when parsing a JSON response in Swift, in general it is not necessary and not a good idea to iterate through the key-value pairs of the dictionaries in the response. You should know what data structure you expect, otherwise you can't parse it properly and since you can directly access dictionary values in Swift if you know the key it corresponds to, there's no need to iterate through the dictionary in a loop.
func downloadJsonWithURL() {
let url = URL(string: self.url)
URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = (try? JSONSerialization.jsonObject(with: data!, options: .allowFragments)) as? [String:Any] {
guard let self.orderdetailsArray = jsonObj["Orders detail"] as? [[String: AnyObject]] else {return}
guard let firstOrderDetails = self.orderdetailsArray.first else {return}
let key = "OrderId"
let value = "#1000501"
if let ordered = firstOrderDetails["OrderId] as? String {
self.orderid.append(orderid)
}
if let orderdate = firstOrderDetails["OrderDate"] as? String{
self.orderdate.append(orderdate)
}
if let subtotal = firstOrderDetails["SubTotal"] as? Int{
self.subTotal.append(subtotal)
}
if let shipping = firstOrderDetails["Shipping"] as? Int{
self.shippingPrice.append(shipping)
}
if let tax = firstOrderDetails["Tax"] as? Int{
self.tax.append(tax)
}
if let grandtotal = firstOrderDetails["GrandTotal"] as? Int{
self.grandTotal.append(grandtotal)
}
if let shippingAddress = firstOrderDetails[ "ShippingAddress"] as? AnyObject{ //why don't you store it as a String?
self.shippingAddress.append(shippingAddress)
}
if let shippingMethod = firstOrderDetails[ "ShippingMethod"] as? AnyObject{
self.shippingMethod.append(shippingMethod)
}
if let billingAddress = firstOrderDetails[ "BillingAddress"] as? AnyObject {
self.billingAddress.append(billingAddress)
}
if let paymentMethod = firstOrderDetails ["PayMentMethod"] as? AnyObject{
self.paymentMethod.append(paymentMethod)
}
guard let itemDetails = firstOrderDetails["Items detail"] as? [[String : AnyObject]] else {return}
self.itemsArray = itemDetails
}
}
}
OperationQueue.main.addOperation({
self.tableDetails.reloadData()
})
}
}).resume()
}
I haven't compiled and run the code, so make sure you check for any typos/inconsistencies. Also, make sure you change the types of the objects you store are AnyObjects to specific types.
I want to parse JSON in UICollectionviewCell. I have a collectionViewController with two UICollectionviewCell. In collectionViewController first cell made to background scrolling and in the second I want to parse JSON. There is no error in the code, this is my JSON code.
var oCategoryFilter: CategoryFilter? {
didSet {
if let name = oCategoryFilter?.totalItem {
totalItemLabel.text = name
}
appsCollectionView.reloadData()
}
}
var arrProduct: [Product]?
func getPropductListByCategory(){
let category_id:String;
category_id = "21"
let url = URL(string: UtilityController.BASE_URL+"/products/"+category_id)
URLSession.shared.dataTask(with:url!) { (urlContent, response, error) in
if error != nil {
print(error)
}
else {
do {
let json = try JSONSerialization.jsonObject(with: urlContent!) as! [String:Any]
print(json)
let items = json["categories"] as? [[String: Any]] ?? []
items.forEach { item in
let oProduct = Product()
//oProduct.id = item["id"] as? String
oProduct.image = item["image"] as? String
oProduct.name = item["name"] as? String
oProduct.ar_name = item["ar_name"] as? String
//oProduct.description = item["description"] as? String
oProduct.ar_description = item["ar_description"] as? String
oProduct.price = item["price"] as? String
oProduct.quantity = item["quantity"] as? String
oProduct.is_featured = item["is_featured"] as? String
oProduct.seller_id = item["seller_id"] as? String
oProduct.payment_required = item["payment_required"] as? String
oProduct.is_editors_choice = item["is_editors_choice"] as? String
oProduct.created_at = item["created_at"] as? String
oProduct.updated_at = item["updated_at"] as? String
self.arrProduct?.append(oProduct)
}
print(url)
} catch let error as NSError {
print(error)
}
}
DispatchQueue.main.async(execute: {
self.appsCollectionView.reloadData()
})
}.resume()
}
When are you calling your functions ? You should call the method in the CollectionView, when it is loading every cell, but doing that is really bad, because each time you will scroll or reload your CollectionView it will parse again.
You should parse in a special class, call by the collection view and this last send the parse object to the cell.
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
}