I am using Alamofire to parse a JSON API, however, I can't figure out how to parse the response data from Alamofire.
When I try to loop through the fetched data, XCode gives me "Segmentation Fault: 11" error.
Here is my current code:
var tableData:NSArray // I have tried several variable types, NSDictionary, String etc.
--
override func viewDidLoad() {
super.viewDidLoad()
self.getJsonData()
}
func getJsonData() {
Alamofire.request(.GET, "https://hotell.difi.no/api/json/mattilsynet/smilefjes/tilsyn", parameters: [:])
.responseJSON { response in
if let JSON = response.result.value {
// print("JSON: \(response.result)")
for entry in JSON["entries"] {
print("\(entry)") // this is where everything crashes
}
}
self.doTableRefresh()
}
}
func doTableRefresh() {
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
return
})
}
What is the correct data format for this JSON result: https://hotell.difi.no/api/json/mattilsynet/smilefjes/tilsyn ? And how do I take the data and populate the tableview?
Convert the response to NSDictionary and NSArray:
func getJsonData() {
Alamofire.request(.GET, "https://hotell.difi.no/api/json/mattilsynet/smilefjes/tilsyn", parameters: [:])
.responseJSON { response in
if let JSON = response.result.value as? NSDictionary{
if let entries = JSON["entries"] as? NSArray{
for entry in entries {
if let entry = entry as? NSDictionary {
for (key, value) in entry {
print("\(key) - \(value)")
}
}
}
}
}
}
}
Without using SwiftyJSON, the idea is to know the right type for each object and successfully downcast.
response.result.value is a dictionary: [String:AnyObject], and the content of json["entries"] is an array of dictionaries: [[String:AnyObject]]. Etc.
Example:
func getJsonData() {
Alamofire.request(.GET, "https://hotell.difi.no/api/json/mattilsynet/smilefjes/tilsyn", parameters: [:])
.responseJSON { response in
if let json = response.result.value as? [String:AnyObject] {
if let entries = json["entries"] as? [[String:AnyObject]] {
for entry in entries {
print(entry) // each entry is a dictionary of type [String:AnyObject]
}
// example of accessing an entry:
if let firstEntry = entries.first, value = firstEntry["adrlinje1"] as? String {
print(value) // "Christian IV gate 3"
}
}
}
}
}
You have to specify the type of your expected value (With SwiftyJSON):
for entry in JSON["entries"] { // Here
print(entry.stringValue)
}
Related
Needs to get country name from below api call :
https://restcountries.eu/rest/v1/all
My code :
var arrRes = []
func getCountry() {
let Url: String = "https://restcountries.eu/rest/v1/all"
Alamofire.request(Url).responseJSON { (responseData) -> Void in
do {
if let datas = responseData.result.value {
let data = (datas as AnyObject).data(using: .utf8)!
let parseData = try JSONSerialization.jsonObject(with: data, options: [])
for country in parseData {
if let name = country["name"] as? String {
print(name)
}
}
}
}
catch let error as NSError {
print(error)
}
}
}
getting error here : 'Any' is not convertible to 'AnyObject' on below line let data = (datas as AnyObject).data(using: .utf8)!..
I need to get only name and append to my array.Any other idea or solution to achieve that ?
Replace do catch block of statement with this.
do {
if let countries = responseData.result.value as? [[String: Any]] {
for country in countries {
if let name = country["name"] as? String {
print(name)
}
}
}
}
catch let error as NSError {
print(error)
}
Try this, its working fine for me.
let urlStr = "https://restcountries.eu/rest/v1/all"
let setFinalURl = urlStr.addingPercentEncoding (withAllowedCharacters: .urlQueryAllowed)!
var request = URLRequest(url: URL(string: setFinalURl)!)
request.httpMethod = HTTPMethod.get.rawValue
Alamofire.request(request).responseJSON
{ (responseObject) -> Void in
if responseObject.result.isSuccess
{
print(responseObject.result.value!)
if "\(String(describing: responseObject.response!.statusCode))" == "200"
{
let result = responseObject.result.value! as AnyObject
let countryNamesArr = result.value(forKey: "name") as! NSArray
print(countryNamesArr)
}
else
{
// handle error
}
}
if responseObject.result.isFailure
{
let error : Error = responseObject.result.error!
print(error.localizedDescription)
}
}
You can try
struct Root: Codable {
let name: String
}
func getCountry() {
let urlStr = "https://restcountries.eu/rest/v1/all"
Alamofire.request(urlStr).responseData { (data) in
do {
guard let data = data.data else { return }
let res = try JSONDecoder().decode([Root].self,from:data)
print(res)
}
catch {
print(error)
}
}
}
Just remove this line
let data = (datas as AnyObject).data(using: .utf8)!
and in optional binding just assign data, since value is of type Data?, from optional binding you get Data
if let data = responseData.result.value
then don't forget to downcast your json to array [String:Any]
...jsonObject(with: data, options: []) as? [[String:Any]]
... then don't forget to unwrap this array or you wouldn't be able to iterate through it in for each loop
Also note that since there is Codable, you should use it instead of JSONSerialization. Then you can decode your json using JSONDecoder to your own model which conforms to protocol Decodable.
As a simple approach, you could implement getCountry() like this:
func getCountry() {
let url: String = "https://restcountries.eu/rest/v1/all"
Alamofire.request(url).responseJSON { response in
if let resultValue = response.result.value, let countryObjects = resultValue as? [[String: Any]] {
let countryNames = countryObjects.compactMap { $0["name"] as? String }
print(countryNames)
}
}
}
At this point, there is no need to use JSONSerialization to get the country names; According to the API response, responseData.result.value is an array of countries (dictionaries), each dictionary has a "name" value, what you should do is to map the response to an array of string. countryNames should contains what are you looking for.
The benefit of using compactMap is to avoid any nil name, so countryNames should be [String] instead of [String?].
However, if you believe that you would need to transform the whole response objects into a custom objects (instead of dictionaries), I would highly recommend to follow the approach of using Decodable.
My code, its working well for me.
Swift 5
public func getCountry(completion: #escaping ([String]) -> ()) {
let url: String = "https://restcountries.eu/rest/v1/all"
AF.request(url).responseJSON { (responseData) -> Void in
do {
guard let data = responseData.data else { return }
let res = try JSONDecoder().decode([CountryName].self,from:data)
completion(self.getCountryName(countryName: res))
}
catch {
print(error)
}
}
}
struct CountryName: Codable {
let name: String
}
private func getCountryName(countryName:[CountryName]) -> [String]{
var country:[String] = []
for index in 0...countryName.count - 1{
country.append(countryName[index].name)
}
return country
}
I'm trying to get data from an API with no documentation.
My code is
let URL_MAIN = "http://evarsity.srmuniv.ac.in/srmswi/usermanager/youLogin.jsp"
let URL_ATTENDANCE = "http://evarsity.srmuniv.ac.in/srmswi/resource/StudentDetailsResources.jsp?resourceid=7"
let URL_TIMETABLE = "http://evarsity.srmuniv.ac.in/srmswi/resource/StudentDetailsResources.jsp?resourceid=5"
func getData(url: String) {
Alamofire.request(url, method: .get)
.responseData { response in
if response.result.isSuccess {
print("Sucess! Got the data")
guard let data = response.result.value else { return }
print(data)
} else {
print("Error: \(String(describing: response.result.error))")
}
}
I am getting the response as 51406 bytes.
I need to get the actual data in either JSON or some other format. Here is the api link for python
https://github.com/arjunmahishi/srm-erp-api/blob/master/erp.py
Convert your responseData to a dictionary using the function i provided below and then parse the data accordingly.Here, i m getting a JSON response in the form of a dictionary.
let strResponse = "\(responseString.value!)"
let arr = strResponse.components(separatedBy: "\n")
let dict = convertStringToDictionary(str:(arr.last ?? "")!)
self.Message = dict?["message"] as! String
let responseStatus = dict?["status"] as! NSString
public func convertStringToDictionary(str:String) -> [String: Any]? {
if let data = str.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
print(error.localizedDescription)
}
}
return nil
}
I want to create AlamofireWebService manager that contains all of my requests and I just use class functions of this class in my viewControllers and use responses in the viewController.
For example something like this in viewController:
let cardResponse : String?
cardResponse = WebServiceManager.shared.getCardTitle()
I searched and found I should use escaping completionHandler in my function, And I wrote this:
import Foundation
import Alamofire
import SwiftyJSON
class WebServiceManager {
static let shared : WebServiceManager = WebServiceManager()
let apiEndPoint = "My URL"
func getCardTitle(completionHandler: #escaping (NSDictionary?, Error?) -> ()) {
Alamofire.request("\(apiEndPoint)")
.responseJSON { response in
switch response.result {
case .success(let value):
print("from .success \(value)")
completionHandler(value as? NSDictionary, nil)
case .failure(let error):
completionHandler(nil, error)
}
}
}
}
In success case, print works fine but in viewController, it just prints nil.
My viewController:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
WebServiceManager.shared.getCardTitle() { responseObject, error in
// use responseObject and error here
print("responseObject = \(responseObject); error = \(error)")
return
}
}
What should I do to print in viewController print my response?
It means that your value can't be cast to NSDictionary it has another type.
completionHandler(value as? NSDictionary, nil)
Please try in the manager a "pseudo code"
if let dic = value as? [String: Any] {
debugPrint("Dic \(dic)")
} else if let str = value as? String {
debugPrint("String \(str)")
} else if let data = value as? Data, let str = String(data: data, encoding: .utf8) {
debugPrint("UTF8 String \(str)")
}
just check response nil
Alamofire.request("URL", method: .post).responseJSON{(responseData) -> Void in
if((responseData.result.value != nil)){
let jsonData = JSON(responseData.result.value)
if let arrJSON = jsonData["keyNodes"].arrayObject {
for index in 0...arrJSON.count-1 {
let aObject = arrJSON[index] as! [String : AnyObject]
var sl:String;
if let sl1 = aObject["sl"] as? String {
sl = sl1;
}
}
}
}
}
I am using Alamofire to call the Riot API and I want to display the information that it has called. I have the get request working, I just don't know how to link to a label in the application. I have included screenshots of the code!
Code
Response
It is just a simple app I am creating!
func callAlamo(url: String){
Alamofire.request(url).responseJSON(completionHandler: {
response in
self.pasrseData(JSONData: response.data!)
})
}
func parseData(JSONData: Data){
do {
var readableJSON = try JSONSerialization.jsonObject(with: JSONData, options: .mutableContainers) as? JSONStandard
print(readableJSON)
}
catch {
print(error)
}
}
No need to serialize since responseJSONfrom Alamofire has done it. Since I don't know what is inside of your JSON object, let's say that you get a return of age and name:
struct InfoModel { // this struct will decompose our JSON object from the response that we get
var age:Int
var name:String
init(json:Dictionary<String,Any>?) {
guard let dict = json,
let age = dict["age"] as? Int,
let name = dict["name"] as? String
else {fatalError() }
self.age = age
self.name = name
}
}
func parse(url: String, completion:#escaping (InfoModel)-> Void) {
Alamofire.request(url).responseJSON {response in
// get the JSON dictionary
if let JSON = response.result.value {
// no need to decompose your object since your struct does it inside of its initializer
completion(InfoModel(json: JSON as? Dictionary<String, Any>))
}
}
}
// call this function anywhere
parse(url: "") { (m:InfoModel) in
print("age= \(m.age), name= \(m.name)")
// now populate your label
labelOne.text = "\(m.age)"
labelTwo.text = name
}
You set the text property of the label in the completion block, basically:
func callAlamo(url: String){
Alamofire.request(url).responseJSON(completionHandler: {
response in
// here we say get me a non optional data (otherwise don't do the if)
if let data = response.data {
// here we are saying if you can't get me a value (i.e. not nil) for:
// json (note the try? will give nil if there is an error)
// name, we get the name out of the json dictionary
// then go to the else block, where we exit the function
// Happy case where we values for json and name we now have non optional variables W00t
guard
let json = try? self.parseData(JSONData: data),
let name = json["name"] as? String
else {
print("name does not exist in json: \(json)")
return
}
// Time to set the label
self.name.text = name
}
})
}
// Changed this to return JSON as a dictionary (it looks like what you printed was a dictionary)
// I also changed this so it throws the error and doesn't deal with it.
// It probably doesn't know what to do if it can't read json something
// else should handle the error higher up the stack
func parseData(JSONData: Data) throws -> [String: Any]? {
return try JSONSerialization.jsonObject(with:
JSONData, options: .mutableContainers) as? [String: Any]
}
NB: This is untested if your having problems and I'll go for a tested solution.
Edit: Answering how to get another property.
The way we got "name" was this chunk of code:
guard
let json = try? self.parseData(JSONData: data),
let name = json["name"] as? String
else {
print("name does not exist in json: \(json)")
return
}
To get another property out we could do this:
guard
let json = try? self.parseData(JSONData: data),
let name = json["name"] as? String,
let summonerLevel = json["summonerLevel"] as? Int
else {
print("name does not exist in json: \(json)")
return
}
Then to display summonerLevel we do the same as with name (although we have an int not a String)
// Time to set the label
self.name.text = name
// (you will have to create this new label)
self.summonerLevelLabel.text = "Level: \(summonerLevel)"
I am stuck with parsing JSON with AlamoFire and SwiftyJSON for iOS. I have a JSON such as this one:
[{"id":23561,"name":"RFI - Persan رادیو صدای Ùرانسه Ùارسی","country":"FR","image":{"url":null,"thumb":{"url":null}},"slug":"rfi-persan-رادیو-صدای-Ùرانسه-Ùارسی","website":"rfi","twitter":"","facebook":"","categories":[{"id":21,"title":"News","description":"","slug":"news","ancestry":"4"}],"streams":[{"stream":"http://rfi-persan.scdn.arkena.com/rfienpersan.mp3","bitrate":0,"content_type":"audio/mpeg","status":1}],"created_at":"2016-01-12T07:52:08+01:00","updated_at":"2016-08-02T01:52:50+02:00"}]
This is what I´ve tried so far, but doesn't work:
func loadSomeJSONData() {
Alamofire.request(.GET, "http://example.com/json/")
.responseJSON { (_, _, data, _) in
let json = JSON(data!)
if let Name = json["name"].string {
println("name: \(firstName)") // Name should equal "RFI"
}
}
}
But for some reason it doesn't get name from json object.
Thank you very much!
Your json is Array not Dictionary, so access the json this way
if let arr = json.arrayObject as? [[String:AnyObject]] {
if let name = arr[0]["name"] as? String {
println("name: \(name)") // Name should equal "RFI"
}
}