How to parse this json with Alamofire 4 in Swift 3? - json

I have the json below but unable to figure out how to parse it in Swift 3. My code is below. The json from the API has an array root. I am using Xcode 8.2.1 with Swift 4 and Alamofire 4.0.
["items": <__NSArrayM 0x608000248af0>(
{
currency = USD;
image = "https://cdn.myDomain.com/image.jpg";
"item_title" = "Antique Table";
"name:" = "";
price = 675;
},
{
currency = USD;
image = "https://cdn.mydomain.com/image2.jpg";
"name:" = "";
price = 950;
...
Here is my code. I have tried to get an array r dictionary from the results but it's always nil.
Alamofire.request(myURL)
.responseJSON(completionHandler: {
response in
self.parseData(JSONData: response.data!)
})
}
func parseData(JSONData: Data) {
do {
let readableJSON = try JSONSerialization.jsonObject(with: JSONData, options:.mutableContainers) as! [String: Any]
print(readableJSON)
}
catch {
print(error)
}
}
I have tried this let item = readableJSON["items"] as? [[String: Any]] as suggested here but it would not compile with an error [String:Any] has no subscript and let item = readableJSON["items"] as? [String: Any]! compiles with a warning Expression implicitly coerced from string but produces nil. Parsing this json is life or death for me.

Do something like
let responseJSON = response.result.value as! [String:AnyObject]
then you'll be able to access elements in that dictionary like so:
let infoElementString = responseJSON["infoElement"] as! String

This was the parse json function I eventually came up with. The problem for this json data is that it is a dictionary inside an array. I am a noob and most of the answers and how tos I saw would not fit my json data. Here is the function I finally came up with with worked.
var myItems = [[String:Any]]()
then in my view controller class
func loadMyItems() {
Alamofire.request(myItemsURL)
.responseJSON(completionHandler: {
response in
self.parseData(JSONData: response.data!)
self.collectionView.reloadData()
})
}
func parseData(JSONData: Data) {
do {
let readableJSON = try JSONSerialization.jsonObject(with: JSONData, options:.allowFragments) as! [String: Any]
let items = readableJSON["items"] as! [[String: Any]]
self.myItems = items
}
catch {
print(error)
}
}
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "myCell", for: indexPath) as? myCell
let dictionary = myItems[indexPath.row] as [String:Any]
if let item_title = dictionary["item_title"] as? String {
cell!.textLabel.text = item_title
print(item_title)
}
return cell!
}

Alamofire Example in Swift 3
1.First of all Use two cocoapods to your project.Use SwiftyJSON for json parse
pod 'Alamofire'
pod 'SwiftyJSON'
My Json is below
{"loginNodes":[{"errorMessage":"Welcome To Alamofire","name":Enamul Haque,"errorCode":"0","photo":null}]}
It may be done in different way. But I have done below Way. Note if you don't need any parameter to send the server then remove parameter option. It may work post or get method. You can use any way. My Alamofire code is below...which is working fine for me......
Alamofire.request("http://era.com.bd/UserSignInSV", method: .post,parameters:["uname":txtUserId.text!,"pass":txtPassword.text!]).responseJSON{(responseData) -> Void in
if((responseData.result.value != nil)){
let jsonData = JSON(responseData.result.value)
if let arrJSON = jsonData["loginNodes"].arrayObject {
for index in 0...arrJSON.count-1 {
let aObject = arrJSON[index] as! [String : AnyObject]
let errorCode = aObject["errorCode"] as? String;
let errorMessage = aObject["errorMessage"] as? String;
if("0"==errorCode ){
//Database Login Success Action
}else{
// //Database Login Fail Action
}
}
}
}
}
If You use Like table View Or Collection View or so on, you can use like that..
Declare A Array
var arrRes = [String:AnyObject]
Assign the value to array like
if((responseData.result.value != nil)){
// let jsonData = JSON(responseData.result.value)
if((responseData.result.value != nil)){
let swiftyJsonVar = JSON(responseData.result.value!)
if let resData = swiftyJsonVar["loginNodes"].arrayObject {
self.arrRes = resData as! [[String:AnyObject]]
}
if self.arrRes.count > 0 {
self.tableView.reloadData()
}
}
}
In taleView, cellForRowAt indexPath , Just use
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! customCell
cell.errorLabelName.text = arrRes[indexPath.row]["errorMessage"] as? String

Swift 3
Alamofire Example in Swift 3
import UIKit
import Alamofire
import SwiftyJSON
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource
{
var array = [[String:AnyObject]]()
#IBOutlet weak var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Alamofire.request("http://www.designer321.com/johnsagar/plumbingapp/webservice/list_advertise.php?zip=123456").responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil)
{
let swiftyJsonVar = JSON(responseData.result.value!)
print("Main Responce")
print(swiftyJsonVar)
}
if let result = responseData.result.value
{
if let Res = (result as AnyObject).value(forKey: "response") as? NSDictionary
{
if let Hallo = (Res as AnyObject).value(forKey: "advertise_list") as? NSArray
{
print("-=-=-=-=-=-=-")
print(Hallo)
self.array = Hallo as! [[String:AnyObject]]
print(self.array)
}
}
self.tableview.reloadData()
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell
var dict = array[indexPath.row]
cell.lbl1.text = dict["address"] as? String
cell.lbl2.text = dict["ad_created_date"] as? String
cell.lbl3.text = dict["phone_number"] as? String
cell.lbl4.text = dict["id"] as? String
cell.lbl5.text = dict["ad_zip"] as? String
let imageUrlString = dict["ad_path"]
let imageUrl:URL = URL(string: imageUrlString as! String)!
let imageData:NSData = NSData(contentsOf: imageUrl)!
cell.img.image = UIImage(data: imageData as Data)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
return 100
}
}

Related

Allow JSON fragments with Decodable

import UIKit
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
//#IBOutlet weak var ingredientText: UILabel!
struct Recipes: Decodable {
let recipe_id:String?
let image_url:String?
let source_url:String?
let f2f_url:String?
let title:String?
let publisher:String?
let social_rank:Float64?
let page:Int?
let ingredients:[String]?
private enum CodingKeys: String, CodingKey{
case recipe_id = "recipe_id"
case image_url = "image_url"
case source_url = "source_url"
case f2f_url = "f2f_url"
case title = "title"
case publisher = "publisher"
case social_rank = "social_rank"
case page = "page"
case ingredients = "ingredients"
}
}
var recipes = [Recipes]()
var food = "chicken"
var food2 = "peas"
var food3 = "onions"
//var recipeData = [Recipe]
#IBOutlet weak var tableView: UITableView!
fileprivate func getRecipes() {
let jsonURL = "http://food2fork.com/api/search?key=264045e3ff7b84ee346eb20e1642d9d9264045e3ff7b84ee346eb20e1642d9d9&food=chicken&food2=onions&food3=peas"
guard let url = URL(string: jsonURL) else{return}
URLSession.shared.dataTask(with: url) {(data, _ , err) in
DispatchQueue.main.async {
if let err = err{
print("failed to get data from URL",err)
return
}
guard let data = data else{return}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
self.recipes = try decoder.decode([Recipes].self, from: data)
self.tableView.reloadData()
}catch let jsonERR {
print("Failed to decode",jsonERR)
}
}
}.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return recipes.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cell")
let recipe = recipes[indexPath.row]
cell.textLabel?.text = recipe.title
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.title = "Ingredients"
getRecipes()
}
}
I am getting the error:
JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.})))
JSONDecoder doesn't provide any JSONSerialization.ReadingOptions.
You could make a manual check whether the first byte of the data is an opening square bracket <5b> or brace <7b>
guard let data = data, let firstByte = data.first else { return }
guard firstByte == 0x5b || firstByte == 0x7b else {
let string = String(data: data, encoding: .utf8)!
print(string)
return
}
However I'd recommend to use the response parameter to check for status code 200
URLSession.shared.dataTask(with: url) { (data, response , error) in
if let response = response as? HTTPURLResponse, response.statusCode != 200 {
print(response.statusCode)
return
}
...
Note: If the CodingKeys match exactly the struct members you can omit the CodingKeys and as you are explicitly using .convertFromSnakeCase you are encouraged to name the struct members recipeId, imageUrl, sourceUrl etc.
You want to decode [Recipe], that is, an Array of Recipe. That mean the first (non-whitespace) character in data has to be [ (to make it a JSON array), and it's not. So you need to figure out why you're getting the wrong response, and fix that problem. Try converting data to a String and printing it:
print(String(data: data, encoding: .utf8))

swift 4 Parse JSON without keys with Alamofire

Guys i want to get all names from JSON (screenshot below) and put them to tableView. The problem is...i got dictionary with this code. Now, how i can get each name value and put them on tableView.
func getDataFromApi(){
Alamofire.request("https://api.coinmarketcap.com/v2/listings/").responseJSON{ response in
if let locationJSON = response.result.value{
let locationObject: Dictionary = locationJSON as! Dictionary<String, Any>
for (key, value) in locationObject {
print("id:\(key), value:\(value)")
}
}
}
}
I would suggest convert the dictionaries response to a Currency object:
class Currency: NSObject {
var id: Int!
var name: String!
var symbol: String!
var websiteSlug: String!
init(id: Int, name: String, symbol: String, websiteSlug: String) {
super.init()
self.id = id
self.name = name
self.symbol = symbol
self.websiteSlug = websiteSlug
}
}
Then under the variables' section define the currencies array:
var currencies = [Currency]()
Finaly change the getDataFromApi implementation to this:
func getDataFromApi() {
Alamofire.request("https://api.coinmarketcap.com/v2/listings/").responseJSON{ response in
if let locationJSON = response.result.value as? [String: Any] {
let data = locationJSON["data"] as! [[String: Any]]
for dataItem in data {
let currency = Currency(id: dataItem["id"] as! Int,
name: dataItem["name"] as! String,
symbol: dataItem["symbol"] as! String,
websiteSlug: dataItem["website_slug"] as! String)
self.currencies.append(currency)
}
print(self.currencies)
}
}
}
I always suggest model the responses to objects because it allows you to do a better managing of the data you need to display on screen and keep your code structure organised.
Now you can easily show the data in a UITableView object from the currencies array.
I would suggest convert the Array In dictionaries response un a Currency object:
var dataArray = NSArray()
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a
nib.
self.getDataFromApi()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getDataFromApi(){
Alamofire.request("https://api.coinmarketcap.com/v2/listings/").responseJSON{ response in
if let locationJSON = response.result.value{
let locationObject: Dictionary = locationJSON as! Dictionary<String, Any>
self.dataArray = locationObject["data"]as! NSArray
self.tableView.reloadData()
// for (key, value) in locationObject {
// print("id:\(key), value:\(value)")
// }
}
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier:"cell") as! UITableViewCell
cell.textLabel?.text = (dataArray.object(at:indexPath.row) as! NSDictionary).value(forKey:"name") as! String
cell.detailTextLabel?.text = (dataArray.object(at:indexPath.row) as! NSDictionary).value(forKey:"symbol") as! String
return cell
}
var nameArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
getData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return nameArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! tableCell
cell.nameLabel.text = nameArray[indexPath.row]
return cell
}
func alamofire() {
Alamofire.request("https://api.coinmarketcap.com/v2/listings/", method: .get, parameters: nil, encoding: URLEncoding.default, headers: nil).responseJSON { (response:DataResponse<Any>) in
switch(response.result) {
case .success(_):
guard let json = response.result.value as! [String:Any] else{ return}
guard let data = ["data"] as! [[String: Any]] else { return}
for item in data {
if let name = item["name"] as? String {
self.nameArray.append(name)
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
break
case .failure(_):
print(response.result.error as Any)
break
}
}
}

How to show Json data in Offline using core data

Here i have created table view with labelName and labelUsername,
and i have downloaded Json data and saving it in core data entity called Details which contains attributes name and username..
here table view showing its data in online...
but how can i show fetched data in table view while in offline..
please help me in the code...
import UIKit
import CoreData
struct JsonData {
var nameS: String
var usernameS: String
init(name: String, username: String) {
self.nameS = name
self.usernameS = username
}
}
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var iteamsArray = [JsonData]()
override func viewDidLoad() {
downloadJson()
}
func downloadJson(){
let urlStr = "https://jsonplaceholder.typicode.com/users"
let url = URL(string: urlStr)
URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) in
guard let respData = data else {
return
}
guard error == nil else {
print("error")
return
}
do{
let jsonObj = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [[String: Any]]
for items in jsonObj {
let nameJson = items["name"] as? String
let usernameJson = items["username"] as? String
let coreData = NSEntityDescription.insertNewObject(forEntityName: "Details", into: self.context) as! Details
coreData.name = nameJson
coreData.username = usernameJson
self.iteamsArray.append(JsonData(name: nameJson!, username: usernameJson!))
}
try self.context.save()
//fetching from core data
let fetchRequest : NSFetchRequest<Details> = Details.fetchRequest()
let details = try self.context.fetch(fetchRequest)
if details.count > 0 {
for detail in details as [NSManagedObject] {
let nameCore = detail.value(forKey: "name")
let usernameCore = detail.value(forKey: "username")
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
catch {
print("catch error")
}
}).resume()
}
}
// MARK: - TableView
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return iteamsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "JsonCell", for: indexPath) as! JsonTableViewCell
let aData = iteamsArray[indexPath.row]
cell.labelName.text = aData.nameS
cell.labelUsername.text = aData.usernameS
cell.selectionStyle = .none
return cell
}
}
First of all forget the custom struct. Use the NSManagedObject class as data source array.
var iteamsArray = [Details]()
In viewDidLoad first fetch the data, if the array is empty load it from the web service
override func viewDidLoad() {
let fetchRequest : NSFetchRequest<Details> = Details.fetchRequest()
do {
iteamsArray = try self.context.fetch(fetchRequest)
if iteamsArray.isEmpty {
downloadJson()
} else {
self.tableView.reloadData()
}
} catch { print(error) }
}
In downloadJson() replace
self.iteamsArray.append(JsonData(name: nameJson!, username: usernameJson!))
with
self.iteamsArray.append(coreData)
and remove these lines
//fetching from core data
let fetchRequest : NSFetchRequest<Details> = Details.fetchRequest()
let details = try self.context.fetch(fetchRequest)
if details.count > 0 {
for detail in details as [NSManagedObject] {
let nameCore = detail.value(forKey: "name")
let usernameCore = detail.value(forKey: "username")
}
}
In cellForRow get the values directly from the NSManagedObject objects
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "JsonCell", for: indexPath) as! JsonTableViewCell
let aData = iteamsArray[indexPath.row]
cell.labelName.text = aData.name
cell.labelUsername.text = aData.userName
cell.selectionStyle = .none
return cell
}

How to fecth data from array with model class

I am not getting data from the array using model class, but array have data in it. I don't know the correct way of using model classes. Maybe the way i implemented is wrong. Please guide.
Code:
// . ViewController class
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// let cell: VisitorsCell = tableView.dequeueReusableCell(withIdentifier: "VisitorsCell", for: indexPath) as! VisitorsCell
let cell:RecentVisitorsCell = self.tblView.dequeueReusableCell(withIdentifier: "VisitorsCell") as! VisitorsCell
**let ds:VisitorDs = recentVisitorsArray[indexPath.row] as! VisitorsDs // . crash here.//**
// let ds = VisitorsDs()
cell.imgProfile.image = UIImage(named: ds.profile)
cell.lblAge.text = ds.age as String
return cell
}
//MARK: Delegatres Server Communication
func didFinishServerCommunicationWithSuccess(_ response:ServerResponse){
print("api response: ", response.infoResponse?["data"] as! [AnyObject])
//VisitorsArray = response.infoResponse?["data"] as! [AnyObject] as NSArray
//if response.operation == "SERVICE_SOURCE" {
if let dictResult = response.infoResponse as? NSDictionary {
if let arrResult = dictResult.object(forKey: "data") as? NSArray
{
for result in arrResult {
let Visitors = VisitorsDs()
recentVisitors.initWithVal(dict: result as! NSDictionary)
VisitorsArray.add(VisitorsDs.self)
}
print("array received: ", VisitorsArray)
self.tblView.reloadData()
}
}
self.tblView.reloadData()
}
// Model class
class VisitorsDs:NSObject {
var profile:String = ""
var name:String = ""
/*
//MARK: Initialization
init(profile:String,name:String,age:String,parentsName:String,mobile:String,purpose:String,date:String,time:String,clinic:String ) {
self.profile = profile
self.name = name
}
*/
func initWithVal(dict: NSDictionary) {
// print(dict)
if let latestValue = dict["image_path"] as? String {
self.profile = latestValue
}
if let latestValue = dict["first_name"] as? String {
self.name = latestValue
}
}
}
Error: Could not cast value of type 'IPAN.VisitorsDs' (0x10d809f58) to 'IPAN.VisitorsDs' (0x10d806eb0).
Please guide, values are empty in uitableview, but value exist in array.
I would recommend to use new Swift 4 Codable protocol to parse JSON into models and then you can use much easier in your tableview. One tutorial:
https://medium.com/swiftly-swift/swift-4-decodable-beyond-the-basics-990cc48b7375
BTW what is in your response?

Display images in UITableview

I'm trying to display images from api in the tableview but I get no images or anything. ImageTableViewCell has the outlet to the image only.
import UIKit
import Alamofire
import SwiftyJSON
import Haneke
class SlideViewController: UIViewController , UITableViewDelegate , UITableViewDataSource {
#IBOutlet weak var tableview : UITableView!
var images = [String]()
override func viewDidLoad() {
super.viewDidLoad()
tableview.delegate = self
tableview.dataSource = self
getJSON()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return images.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "imageCell", for: indexPath) as! ImageTableViewCell
let url = URL(string: images[indexPath.row])
cell.images.hnk_setImage(from: url!)
return cell
}
func getJSON() {
let url = "http://localhost:8000/api/hello"
request(url , method: .get, encoding: JSONEncoding.default )
.responseJSON { response in
if let value: AnyObject = response.result.value as AnyObject? {
//Handle the results as JSON
do{
if let albums = try JSONSerialization.jsonObject(with: response.data!, options: []) as? [String: Any],
let pics = albums["pic"] as? [Any] {
self.images = pics as! [String]
for kick in pics {
self.images.append(kick as! String)
}
}
}catch {
print("Error with Json: \(error)")
}
DispatchQueue.main.async {
self.tableview.reloadData()
}
}
}
}
}
Any help will be appreciated. I tried many methods but this seems to be simple and easy to follow up
Check below code
var images = [String]()
func getJSON() {
let url = "http://localhost:8000/api/hello"
request(url , method: .get, encoding: JSONEncoding.default )
.responseJSON { response in
if let value: AnyObject = response.result.value as AnyObject? {
//Handle the results as JSON
do{
let albums = try JSONSerialization.jsonObject(with: response.data!, options:.allowFragments) as! [[String: AnyObject]]
print(albums)
if let pics = album["pic"]
{
for album in pics
{
images.append(album)
}
}
}catch {
print("Error with Json: \(error)")
}
self.tableview.reloadData()
}
}
}
Your JSON response is Dictionary not Array and its contains pic array inside of it. So type result of jsonObject(with:options:) to [String:Any] instead of [[String: AnyObject]]
do{
if let albums = try JSONSerialization.jsonObject(with: response.data!, options: []) as? [String: Any],
let pics = albums["pics"] as? [Any] {
images = pics.flatMap{ $0 as? String }
}
}catch {
print("Error with Json: \(error)")
}
DispatchQueue.main.async {
self.tableview.reloadData()
}
You must have at least one section to show.
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}