How to parse this JSON from swift 3 facebook graph request - ios

I am using a graph request to get a json using this code:
nextrequest.start({ (response: HTTPURLResponse?, result: Any?) in
print(result)
})
this is the json result below and I have no idea how to access the the data inside such as gender, id and name...
Optional(FacebookCore.GraphRequestResult<FacebookCore.GraphRequest>.success(FacebookCore.GraphResponse(rawResponse: Optional({
gender = male;
id = 1128614937219535;
name = "Rayan Slim";
picture = {
data = {
height = 320;
"is_silhouette" = 0;
url = "https://scontent.xx.fbcdn.net/v/t1.0-1/p320x320/12541113_961418627272501_5451131278168499090_n.jpg?oh=47433bc236ce63ce1c07b92499087f29&oe=586A406A";
width = 320;
};
};
}))))
any help would be greatly appreciated!!!!

After permission has been granted here's the function that works.
if AccessToken.current != nil {
GraphRequest(graphPath: "me", parameters: ["fields": "id, name, first_name, last_name, picture.type(large), email"]).start({ (urlResponse, requestResult) in
switch requestResult {
case .Success(let response):
if let responseDictionary = response.dictionaryValue {
let email = responseDictionary["email"] as? String
print(email)
let first = responseDictionary["name"] as? String
print(first)
if let picture = responseDictionary["picture"] as? NSDictionary {
if let data = picture["data"] as? NSDictionary{
if let profilePicture = data["url"] as? String {
print(profilePicture)
}
}
}
}
case .Failed(let error):
print(error)
}
})
}

This is tested and working in Swift 3, using the Facebook SDK for Swift
let pictureRequest = GraphRequest(graphPath: "me/picture?type=large&redirect=false", parameters: [:])
pictureRequest.start{
(urlResponse, requestResult) in
switch requestResult {
case .failed(let error):
print("error in graph request:", error)
break
case .success(let graphResponse):
if let responseDictionary = graphResponse.dictionaryValue {
print(responseDictionary)
var dict: NSDictionary!
dict = responseDictionary["data"] as! NSDictionary
print(dict)
print(dict["url"])
}
}
}

if let userDataDict = result as? NSDictionary {
self.first_name = userDataDict["first_name"] as? String
self.id = userDataDict["id"] as? String
self.last_name = userDataDict["last_name"] as? String
let pictDict = userDataDict["picture"] as? NSDictionary
let pictureUrl = pictDict?["data"] as? NSDictionary
self.pictureUrl = pictureUrl?["url"] as? String
}

do like
nextrequest.start({ (response: HTTPURLResponse?, result: Any?) in
print(result)
if let userData = result as? [NSObject: Any]
{
if let name = userData["name"] as? String
{
print(name)
}
if let picture = userData["picture"] as? [NSObject: Any] {
if let data = picture["data"] as? [NSObject: Any] {
if let profilePictureURL = data["url"] as? String {
// Now add the data to the UI elements
print (profilePictureURL)
}
}
}
}
})

Make use of some JSON parser, maybe Unbox that makes it easier to handle JSON. This code is not tested, but it is an outline of how you could do it. It is always code to store data in a struct instead of using dictionaries.
typealias JSON = [String: Any]
protocol Model {
init?(json: JSON)
}
func getDataFromFacebook() {
...
nextrequest.start {
(response: HTTPURLResponse?, result: Any?) in
self.handleFacebookResult(result)
}
}
func handleFacebookResult(_ result: Any?) {
guard
let json = result as? JSON,
let person = Person(json: json)
else { return }
//do something with person! :)
}
struct Person: Model {
let name: String
let gender: String
let picture: Picture
init?(json: JSON) {
guard
let name = json["name"] as? String,
let gender = json["gender"] as? String,
let pictureJson = json["picture.data"] as? JSON, // "picture.data" possible when using 'Unbox'
let picture = Picture(json: pictureJson)
else { return nil }
self.name = name
self.gender = gender
self.picture = picture
}
}
struct Picture: Model {
let height: Int // Or maybe float...?
let width: Int // Or maybe float...?
let url: String
init?(json: JSON) {
guard
let height = json["height"] as? Int,
let width = json["width"] as? Int,
let url = json["url"] as? String
else { return nil }
self.height = height
self.width = width
self.url
}
}

Related

Why URLSession.DataTask.shared is not working properly?

I need to fetch some quizzes for my application from the server. Unfortunately, it seems that URLSession.DataTask.shared is not working. How do I fix the problem?
This is for Swift 4.
import Foundation
import UIKit
class QuizService {
let baseUrl = "https://iosquiz.herokuapp.com/api/quizzes"
func fetchQuizzes(completion: #escaping (([Quiz]?) -> Void)) -> Void {
if let url = URL(string: baseUrl) {
let request = URLRequest(url: url)
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
if let resultsList = json as? [String: Any], let results = resultsList["quizzes"] as? [[String: Any]] {
let quizzes = results.map({ json -> Quiz? in
print(json)
if
let title = json["title"] as? String,
let id = json["id"] as? Int,
let level = json["level"] as? Int,
let description = json["description"] as? String,
let category = json["category"] as? String,
let questions = json["questions"] as? [String: Any],
let imageUrl = json["image"] as? String {
let quiz=Quiz(id:id,title:title,descript:description,category:category,level:level,imageUrl:imageUrl,questions:questions)
return quiz
} else {
return nil
}
}).filter { $0 != nil } .map { $0! }
completion(quizzes)
} else {
completion(nil)
}
} catch {
completion(nil)
}
} else {
completion(nil)
}
}
dataTask.resume()
} else {
completion(nil)
}
}
}
My error is that field of quizzes are null, so my code is not working in my view controller.
I took a look at the response here https://iosquiz.herokuapp.com/api/quizzes.
The "questions" should be array of dictionaries instead of dictionary.
so it should works if you replace this line
let questions = json["questions"] as? [String: Any]
with this line
let questions = json["questions"] as? [[String: Any]]
BTW, I prefer to extract the logic of parsing json into another method to keep fetchQuizzes method simple.
Hope this helps!

need to get the country name from open api

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
}

Fetch data from JSON in swift 3

I am new to iOS and my task is to fetch data from JSON. I have one JSON and from that I am able to get some data but some no. Please help me .
In this I used code :
NetworkManager.post("http://dev.depolitie.be/dev/public/api/cameras/get_cameras_list", parameters: parameters as [String : AnyObject], success: {(result: NSDictionary) -> Void in
print ("Api Success 22 : result is:\n \(result)")
if let error = result.value(forKey: "error") {
print(error)
}else {
let userDict = result.value(forKey: "data") as! NSDictionary
self.cameraArray = userDict["data"] as! NSArray
let introText = userDict["introtext"] as? Int
print("introtext",introText)
//let firstName = employee["firstName"]! as Strin
// let introtext = userDataAsArray["introtext"]! as Int
// print("introText", introtext)
}
self.cameraTableView.reloadData()
}, failure: {(error: NSDictionary?) -> Void in
print ("Api Failure : error is:\n \(String(describing: error))")
})
The inner data I get by initialising it as array but I am not able to get "introtext" field from JSON.
Try this code..😊
NetworkManager.post("http://dev.depolitie.be/dev/public/api/cameras/get_cameras_list", parameters: parameters as [String : AnyObject], success: {(result: NSDictionary) -> Void in
print ("Api Success 22 : result is:\n \(result)")
if let error = result.value(forKey: "error") {
print(error)
}else {
let userDict = result.value(forKey: "data") as! NSDictionary
let dataArray = userDict["data"] as! NSArray
let firstElementOfDataArray = dataArray.object(at: 0) as! NSDictionary
let adminId = firstElementOfDataArray["admin_id"] as! String
let createdAt = firstElementOfDataArray["created_at"] as! String
let description = firstElementOfDataArray["description"] as! String
}
self.cameraTableView.reloadData()
}, failure: {(error: NSDictionary?) -> Void in
print ("Api Failure : error is:\n \(String(describing: error))")
})
As I said in my comment your introtext is inside the root dictionary, also you need to cast to the proper type String in this case
Try with this code
if let error = result.value(forKey: "error") {
print(error)
}else {
if let userDict = result["data"] as? [String:AnyObject]{
if let cameraArrayValues = userDict["data"] as? [[String:AnyObject]]{
self.cameraArray = cameraArrayValues
}
}
if let introText = result["introtext"] as? String{
print("Introtext:" + introText)
}
}

Parsing JSON with Alamofire Problems

I am new to swift and in programming, and I am trying to parse a JSON with the help of Alamofire and SwiftyJSON, if the JSON file is simple, I have no problems and working good, but when I have something like Dictionary -> Dictionary -> Array -> Dictionary, problems begin, so I have the following code:
func performCYesterdayWeatherFetch(forSelectedCity: String)
{
let properString = forSelectedCity.addingPercentEncoding(withAllowedCharacters:NSCharacterSet.urlQueryAllowed)
Alamofire.request("http://api.apixu.com/v1/history.json?key=MY_KEY&q=\(properString!)&dt=2016-10-20").responseJSON { (response) -> Void in
guard response.result.isSuccess else
{
print("Error while fetching remote rooms: \(response.result.error)")
return
}
guard let json = response.result.value as? JSON,
let forecastJson = json["forecast"].dictionary else
{
print("YESTERDAY PROBLEM")
return
}
for item in (forecastJson["forecastday"]?.arrayValue)!
{
let day = item["day"].dictionaryObject
guard let yesterdayTempCels = day?["avgtemp_c"] as! Double?,
let yesterdayTempFahr = day?["avgtemp_f"] as! Double? else
{
return
}
MY_KEY - is really my key, the problem is not in that i didn't input the key.
It always get in else here:
guard let json = response.result.value as? JSON,
let forecastJson = json["forecast"].dictionary else
{
print("YESTERDAY PROBLEM")
return
}
They result JSON looks like that:
The thin I need is avgtemp_c and avgtemp_f
What am I doing wrong?
Here you have a solution where you don't even need SwiftyJSON to get those values.
let properString = forSelectedCity.addingPercentEncoding(withAllowedCharacters:NSCharacterSet.urlQueryAllowed)
Alamofire.request("http://api.apixu.com/v1/history.json?key=MY_KEY&q=\(properString!)&dt=2016-10-20").responseJSON { (response) -> Void
guard let json = response.result.value as? [String: Any],
let forecastDictionary = json["forecast"] as? [String: Any],
let forecastDayArray = forecastDictionary["forecastday"] as? [[String: Any]] else {
print("YESTERDAY PROBLEM")
return
}
for item in forecastDayArray {
guard let day = item["day"] as? [String: Any],
let yesterdayTempCels = day["avgtemp_c"] as? Double,
let yesterdayTempFahr = day["avgtemp_f"] as? Double else {
return
}
// Here you should have the values that you need
}
}

Parse JSON with an Optional in swift

Optional({"session":{"_id":"574fe96fa28f9aaadb000034","application_id":41262,"created_at":"2016-06-02T08:08:15Z","device_id":0,"nonce":21576,"token":"5b04f409c06ecf24ad2d9479a1ef7ef78916f864","ts":1464854895,"updated_at":"2016-06-02T08:08:15Z","user_id":0,"id":7274}})
I need to parse and save token from the above dictionary (in Swift)
My request goes like this :
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
if error != nil {
print("error=\(error)")
return
}
print("response = \(response)")
let dict = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(dict)")
}
task.resume()
Need to parse dict
JSON parsing/mapping can be a pain and time consuming.
I just happen to have made a tool for it :
Jenerator
Download from here and move to /usr/local/bin/
It is a little command line tool written in Swift to generate a Swift model based on a JSON. If I passed it your JSON it gave me back this :
import Foundation
struct SOSession {
var created_at : String
var _id : String
var id : Int
var device_id : Int
var token : String
var updated_at : String
var nonce : Int
var user_id : Int
var ts : Int
var application_id : Int
init(data:[String:AnyObject]) {
self.created_at = (data["created_at"] as? String) ?? ""
self._id = (data["_id"] as? String) ?? ""
self.id = (data["id"] as? Int) ?? 0
self.device_id = (data["device_id"] as? Int) ?? 0
self.token = (data["token"] as? String) ?? ""
self.updated_at = (data["updated_at"] as? String) ?? ""
self.nonce = (data["nonce"] as? Int) ?? 0
self.user_id = (data["user_id"] as? Int) ?? 0
self.ts = (data["ts"] as? Int) ?? 0
self.application_id = (data["application_id"] as? Int) ?? 0
}
static func fromSource(urlString : String) -> SOSession? {
guard let url = NSURL(string: urlString), data = NSData(contentsOfURL: url) else {
return nil
}
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers)
if let outerDict = json as? [String:AnyObject], let dict = outerDict["session"] as? [String:AnyObject] {
return SOSession(data: dict)
}
} catch {}
return nil
}
}
Getting the Token then becomes as simple as this :
let myToken = SOSession.fromSource("someUrl")?.token
To use Jenerator I saved your JSON in a file on my desktop and ran in terminal :
jenerator "$HOME/Desktop/so-1.json" StackOverflowQuestion1 SO
jenerator "path-to-file-with-json" save-file-name class-prefix
You can now copy the generator code to your project.
In Swift 4:
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
if let data = responseJSON as? [String: Any] {
if let success = data["success"] as? Int {
if success == 1 {
self.dataArray = data["results"] as! Array
label.text = self.dataArray[row]["id"] as? String
// parse in similar fashion
}
}
}
}

Resources