I am trying to get NBA standings from this website https://erikberg.com/api
To do this, I am using AlmoFire to make a Get request:
Alamofire.request(.GET, "https://erikberg.com/nba/standings.json")
.responseJSON { response in
print(response.2.value)
}
What this is returning is a response array with a NSURLRequest at index 0, an NSHTTPURLResponse at index 1, and a result of type AnyObject at index 2. The AnyObject, when printed, results in this (I'm only showing one team because the array is very long):
Optional({
standing = (
{
"away_lost" = 14;
"away_won" = 21;
conference = EAST;
"conference_lost" = 13;
"conference_won" = 29;
division = CEN;
"first_name" = Cleveland;
"games_back" = 0;
"games_played" = 71;
"home_lost" = 6;
"home_won" = 30;
"last_five" = "4-1";
"last_name" = Cavaliers;
"last_ten" = "7-3";
lost = 20;
"ordinal_rank" = 1st;
"playoff_seed" = 1;
"point_differential" = 437;
"point_differential_per_game" = "6.2";
"points_against" = 6948;
"points_allowed_per_game" = "97.9";
"points_for" = 7385;
"points_scored_per_game" = "104.0";
rank = 1;
streak = W2;
"streak_total" = 2;
"streak_type" = win;
"team_id" = "cleveland-cavaliers";
"win_percentage" = ".718";
won = 51;
},exc...
I would like to know how I can parse this data so I can extract and categorize each team based on their standings.
Thank you
You can use ObjectMapper to parse JSON. Make a Standing class which contains all the JSON to object mapping code.
class Standing: Mappable {
var awayLost: Int?
var awayWon: Int?
..... // Define all variables
required init?(_ map: Map) {}
// This function is used to map all variables to corresponding JSON strings
func mapping(map: Map) {
awayLost <- map["away_lost"]
awayWon <- map["away_won"]
...
}
Similarly, make a parent class to hold array of standing.
class ParentJson: Mappable {
var standingsDate: String?
var standing: [Standing]
}
func mapping(map: Map) {
standing <- map["standing"]
}
And then in your alamofire response, map the corresponding class
Alamofire.request(.GET, "https://erikberg.com/nba/standings.json")
.responseJSON { response in
let parentJson = Mapper<ParentJson>().map(response.2.value)
let standingsArray: [Standing] = parentJson.standing
standingArray will now have all the data required to categorize.
I use the SwiftyJSON package for all things JSON related. Then, you can use this idiom
.responseJSON { response in
switch response.result {
case .Success:
if let value = response.result.value {
let json = JSON(value)
if let datum = json["field"].string { // Depending on expected type; see docs
}
case .Failure(let error):
print(error)
}
Related
Following is the code which i used Alamofire to call a GET api for getting the train details:
var url:String!
url = "https://api.railwayapi.com/v2/name-number/train/bhopal/apikey/iixp5yxd/"
Alamofire.request(url, method: .get, encoding: JSONEncoding.default)
.responseJSON { response in
switch response.result {
case .success(let json):
print(json)
if let json = response.result.value as? [String:Any] {
print(json["train"]!)
if let nestedDictionary1 = json["train"] as? [String: AnyObject] {
// access individual value in dictionary
if let views = nestedDictionary1["number"] as? String {
print(views)
}
if let classes = nestedDictionary1["classes"] as? [String: AnyObject] {
print(classes)
}
}
}
DispatchQueue.main.async {
// handle your code
}
case .failure(let error):
print(error)
}
}
At the print(json) line i am getting the following JSON response as:
{
debit = 1;
"response_code" = 200;
train = {
classes = (
{
available = N;
code = 2S;
name = "SECOND SEATING";
},
{
available = N;
code = FC;
name = "FIRST CLASS";
},
{
available = Y;
code = 2A;
name = "SECOND AC";
},
{
available = N;
code = 3E;
name = "3rd AC ECONOMY";
},
{
available = Y;
code = SL;
name = "SLEEPER CLASS";
},
{
available = N;
code = CC;
name = "AC CHAIR CAR";
},
{
available = Y;
code = 3A;
name = "THIRD AC";
},
{
available = Y;
code = 1A;
name = "FIRST AC";
}
);
days = (
{
code = MON;
runs = Y;
},
{
code = TUE;
runs = Y;
},
{
code = WED;
runs = Y;
},
{
code = THU;
runs = Y;
},
{
code = FRI;
runs = Y;
},
{
code = SAT;
runs = Y;
},
{
code = SUN;
runs = Y;
}
);
name = "SHAN-E- BHOPAL EXP";
number = 12156;
};
}
How shall i extract the value of key "Classes" and the corresponding keys of "Classes" . I used this in the above code , but unfortunately its not working:
if let classes = nestedDictionary1["classes"] as? [String: AnyObject] {
print(classes)
}
Since i am a newbie to the Alamofire please tell where i am getting wrong and how to solve this?
Please read the output. JSON has only two collection types, array [] (in the output ()) and dictionary {}.
So the value for key classes is clearly an array ([[String:Any]]) and JSON unspecified values are always Any, never AnyObject.
And please use more descriptive variable names.
if let json = response.result.value as? [String:Any] {
if let train = json["train"] as? [String: Any] {
print(train)
// access individual value in dictionary
if let number = train["number"] as? String { // more likely `Int`
print(number)
}
if let classes = train["classes"] as? [[String: Any]] {
for aClass in classes {
let name = aClass["name"] as! String
let available = aClass["available"] as! String
print(name, available)
}
}
}
}
Nevertheless consider to use the Codable protocol. It's more convenient and more efficient
I'm getting an array of dictionaries from the server. Then I'm trying to convert it to jsonDictionary it seems like I'm doing something wrong. How can I also init my Users model?
Here is the code:
func getSearchedUsers(key: String, completion: #escaping(SearchedUsers?) -> Void) {
if let url = URL(string: baseURL + "search?qerty=\(key)") {
Alamofire.request(url).responseJSON { (response) in
if let array = response.result.value as? [[String:Any]] {
var dictionary = [String:Any]()
for item in array {
for (key, value) in item {
dictionary.updateValue(value, forKey: key)
}
}
} else {
completion(nil)
}
}
}
}
And here is the model:
class SearchedUsers {
let id: Int
let username: String?
let fullName: String?
let profilePicture: URL?
let isPrivate: Bool
init(data: [String: Any]) {
id = data["id"] as! Int
username = data["username"] as? String
fullName = data["fullName"] as? String
isPrivate = data["isPrivate"] as! Bool
profilePicture = data["profilePicUrl"] as? URL
}
}
How can I get this to work?
Here is the response I get:
[Result]: SUCCESS: (
{
byline = "21.9k followers";
followerCount = 21911;
friendshipStatus = {
following = 0;
"incoming_request" = 0;
"is_bestie" = 0;
"is_private" = 0;
"outgoing_request" = 0;
};
fullName = "Undefined Variable";
hasAnonymousProfilePicture = 0;
id = 8513861541;
isPrivate = 0;
isVerified = 0;
mutualFollowersCount = 0;
picture = "https://scontent-ams3-1.cdninstagram.com/vp/885ac17fe17809de22790f0559f61877/5CD13A1C/t51.2885-19/s150x150/39312159_480582069091253_3011569611268161536_n.jpg?_nc_ht=scontent-ams3-1.cdninstagram.com";
pk = 8513861541;
profilePicId = "1857507164564653723_8513861541";
profilePicUrl = "https://scontent-ams3-1.cdninstagram.com/vp/885ac17fe17809de22790f0559f61877/5CD13A1C/t51.2885-19/s150x150/39312159_480582069091253_3011569611268161536_n.jpg?_nc_ht=scontent-ams3-1.cdninstagram.com";
reelAutoArchive = on;
username = "i_am_variable";
},
{
byline = "467 followers";
followerCount = 467;
friendshipStatus = {
following = 0;
"incoming_request" = 0;
"is_bestie" = 0;
"is_private" = 0;
"outgoing_request" = 0;
};
fullName = undefined;
hasAnonymousProfilePicture = 0;
id = 8657882817;
isPrivate = 0;
isVerified = 0;
latestReelMedia = 1547794887;
mutualFollowersCount = 0;
picture = "https://scontent-ams3-1.cdninstagram.com/vp/fb3c992c899aa269bdce2c4c1db8575b/5CD068BA/t51.2885-19/s150x150/46378106_2062632390480778_1266491662662631424_n.jpg?_nc_ht=scontent-ams3-1.cdninstagram.com";
pk = 8657882817;
profilePicId = "1931972067016763185_8657882817";
profilePicUrl = "https://scontent-ams3-1.cdninstagram.com/vp/fb3c992c899aa269bdce2c4c1db8575b/5CD068BA/t51.2885-19/s150x150/46378106_2062632390480778_1266491662662631424_n.jpg?_nc_ht=scontent-ams3-1.cdninstagram.com";
reelAutoArchive = on;
username = "undefi.ned";
})
It's an array of dictionaries, I need to parse it in a proper way. That's my main issue.
If you know how to parse dictionary, then you should know how to make one ;) There are tools out there to make your own model class, like: http://www.jsoncafe.com/
EDIT: As suggested by Robert in the comment section below, you can learn Decodable.
You can use that to give yourself an idea how a model class could or should look like. Use it however you like. In a decent project, there could be tons of data, and you don't want to make a class model out of it especially if you're the only one handling the iOS project.
So we suppose, we have this json data, based on your post:
{
"id": 1,
"username": "dd",
"fullName": "dd",
"profilePicture": "ddd",
"isPrivate": true
}
We could make a model out of it like so:
//
// UserRootClass.swift
// Model Generated using http://www.jsoncafe.com/
// Created on January 18, 2019
import Foundation
class UserRootClass : NSObject {
var fullName : String!
var id : Int!
var isPrivate : Bool!
var profilePicture : String!
var username : String!
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
init(fromDictionary dictionary: [String:Any]){
fullName = dictionary["fullName"] as? String
id = dictionary["id"] as? Int
isPrivate = dictionary["isPrivate"] as? Bool
profilePicture = dictionary["profilePicture"] as? String
username = dictionary["username"] as? String
}
/**
* Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
*/
func toDictionary() -> [String:Any]
{
var dictionary = [String:Any]()
if fullName != nil{
dictionary["fullName"] = fullName
}
if id != nil{
dictionary["id"] = id
}
if isPrivate != nil{
dictionary["isPrivate"] = isPrivate
}
if profilePicture != nil{
dictionary["profilePicture"] = profilePicture
}
if username != nil{
dictionary["username"] = username
}
return dictionary
}
}
The model class above was made using the tool I gave above, but I removed the NSCoding protocol methods.
I hope this helps! Good luck and welcome to Stackoverflow.
You can use Decodable if you have Struct instead of Class for easy parsing. Here is the example in Alamofire 5.0
struct SearchedUsers: Decodable {
let id: Int
let username: String?
let fullName: String?
let profilePicture: URL?
let isPrivate: Bool
}
AF.request("http://url_endpoint/").responseData { response in
do {
// data we are getting from network request
let decoder = JSONDecoder()
let response = try decoder.decode([SearchedUsers].self, from: response.data!)
} catch { print(error) }
}
How to use values globally parsed from JSONResponse. So, far able to extract the array from JSONResponse, but after that trying to put that array in an global array, not getting how to declare global array. somehow managed to do that but now not able to extract values index wise from that array. Please guide.
Code tried now:
if (JSONResponse["status"].intValue == 1)
{
if let fetchedImages = JSONResponse["data"].arrayObject {
self.arrResponseReceived = fetchedImages
}
print("self.arrResponseReceived", self.arrResponseReceived)
}
else
{
CommonMethodsClass.showAlertMessage(vc: self, titleStr: "Error!", messageStr: strMsg)
}
Code tried earlier:
arrResponseReceived = JSONResponse["data"].arrayValue
Global variable declared for array is:
var arrResponseReceived : [Any] = []
Data to parse is:
{
data = (
{
AverageRating = 4;
Image = "<null>";
IsActive = 1;
Latitude = "28.567806";
Longitude = "77.3236273";
SalonAddress = " Sec-58, India";
SalonEmail = "vy.chan#th-rce.com";
SalonID = 1;
SalonImage = "";
SalonMobile = 9999888877;
SalonName = Affinity;
SalonPhone = 9999888877;
TimeIntervalminutes = 20;
},
{
AverageRating = 5;
Image = "<null>";
IsActive = 1;
Latitude = "";
Longitude = "";
SalonAddress = "Mall, India";
SalonEmail = "rad#th-rce.com";
SalonID = 3;
SalonImage = "";
SalonMobile = 9999888877;
SalonName = Looks;
SalonPhone = 9999888877;
TimeIntervalminutes = 30;
}
);
message = "";
status = 1;
}
You should definite the Class of what is inside of JSONResponse["data"].arrayValue, or how Xcode knows it.
If you want to get an data array which is made of Dictionary , here is how I cheated it out.
func getDatas(from json: Any?) -> [Dictionary] {
guard let json = json as? NSDictionary, let entries = json.value(forKeyPath: "data") as? [NSDictionary] else {
return []
}
return entries.flatMap { entry in
guard let dictionary = entry as? NSDictionary else {
return nil
}
return dictionary
}
}
you call it as such
self.arrResponseReceived = getDatas(from: json)
NSDictionary has the method value(forKeyPath:) that is near magic. Calling json.value(forKeyPath: "data") returns an array of dictionaries. Each dictionary is an "entry" object in the json. Next, I map each entry which returns a dictionary.
If this is something more than a quick solution, then I would consider adding SwiftyJSON to your project.
func getDatas(from json: Any?) -> [Dicitonary] {
return JSON(json)["data"].arrayValue.flatMap { entry in
guard let dictionary = entry as? NSDictionary else {
return nil
}
return dictionary
}
}
I'm new with this mapper thing and too confused. I have an API request, given a Title, the API return this:
{
Response = True;
Search = (
{
Poster = "https://images-na.ssl-images-amazon.com/images/M/MV5BMjAxODQ2MzkyMV5BMl5BanBnXkFtZTgwNjU3MTE5OTE#._V1_SX300.jpg";
Title = ARQ;
Type = movie;
Year = 2016;
imdbID = tt5640450;
},
{
Poster = "N/A";
Title = Arq;
Type = movie;
Year = 2011;
imdbID = tt2141601;
},
{
Poster = "N/A";
Title = "A.R.Q.";
Type = movie;
Year = 2015;
imdbID = tt3829612;
}
);
totalResults = 3;
}
So I've created a mappable class for this result:
class SearchResponse: Mappable {
var isSuccess : String?
var searchArray: [Movie]?
var searchCount: String?
required init?(map: Map) {
}
func mapping(map: Map) {
isSuccess <- map["Response"]
searchArray <- map["Search"]
searchCount <- map["totalResults"]
}
}
class Movie: Mappable {
var posterURL : String?
var title : String?
var runtime : String?
var director : String?
var actors : String?
var genre : String?
var plot : String?
var production : String?
var year : String?
var imdbID : String?
var imdbRating : String?
required init?(map: Map) {
}
func mapping(map: Map) {
posterURL <- map["Poster"]
title <- map["Title"]
runtime <- map["Runtime"]
director <- map["Director"]
actors <- map["Actors"]
genre <- map["Genre"]
plot <- map["Plot"]
production <- map["Production"]
year <- map["Year"]
imdbID <- map["imdbID"]
imdbRating <- map["imdbRating"]
}
}
Question: I mapped this movie class like this, but for the search by title I'll only have 4 of this attributes. But for the next search I'll need all of them. Is that right? Or should I create two separate classes to deal with each kind of response?
Ok! I'm showing the result of this search on my SearchTableViewController. Now I want to show more details of this movie (any movie of the "Search" array on this previous response). To do that, the API offers another type of search, that is search by imdbID. So I've created a segue on my SearchTableViewController to get this ID and pass to my MovieViewController (the view that will show these details):
let searchSegue = "segueFromSearch"
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let searchIndex = tableView.indexPathForSelectedRow?.row
let movie = movies?[searchIndex!]
let selectedImdbID = movie?.imdbID
print("|Table View Controler| Segue. IMDB_ID: \(String(describing: selectedImdbID))")
if segue.identifier == searchSegue {
if let destination = segue.destination as? MovieViewController {
destination.imdbID = selectedImdbID!
print("|Table View Controler| Inside of if let. Debug print: I get til here. imdbID = \(selectedImdbID!)")
}
}
}
My function for this API request is:
//The movieSearched variable is the text typed on my searchBar
let URL = "https://www.omdbapi.com/?s=\(movieSearched)&type=movie"
Alamofire.request(URL).responseObject{ (response: DataResponse<SearchResponse>) in
print("response is: \(response)")
switch response.result {
case .success(let value):
let searchResponse = value
self.movies = (searchResponse.searchArray)
self.searchTableView.reloadData()
case .failure(let error):
let alert = UIAlertController(title: "Error", message: "Error 4xx / 5xx: \(error)", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
OK, given this overview of what I have, let's talk about my problem...
When I search by ID, the Json response is that:
{
Actors = "Robbie Amell, Rachael Taylor, Shaun Benson, Gray Powell";
Awards = "N/A";
BoxOffice = "N/A";
Country = "USA, Canada";
DVD = "16 Sep 2016";
Director = "Tony Elliott";
Genre = "Sci-Fi, Thriller";
Language = English;
Metascore = "N/A";
Plot = "Trapped in a lab and stuck in a time loop, a disoriented couple fends off masked raiders while harboring a new energy source that could save humanity.";
Poster = "https://images-na.ssl-images-amazon.com/images/M/MV5BMjAxODQ2MzkyMV5BMl5BanBnXkFtZTgwNjU3MTE5OTE#._V1_SX300.jpg";
Production = Netflix;
Rated = "N/A";
Ratings = (
{
Source = "Internet Movie Database";
Value = "6.4/10";
},
{
Source = "Rotten Tomatoes";
Value = "60%";
}
);
Released = "16 Sep 2016";
Response = True;
Runtime = "88 min";
Title = ARQ;
Type = movie;
Website = "N/A";
Writer = "Tony Elliott";
Year = 2016;
imdbID = tt5640450;
imdbRating = "6.4";
imdbVotes = "17,481";
}
My problem
I did this alamofire request for the search by ID:
func getMovieById() {
let URL = "https://www.omdbapi.com/?i=\(String(describing: imdbID!)))"
Alamofire.request(URL).responseObject{ (response: DataResponse<SearchResponse>) in
print("|MovieController| Response is: \(response)")
let Result = response.result.value
print("Result for search by id: \(String(describing: Result!.searchArray))")
// Have to get the values here, right?
}
}
Obviously, I'm not getting the data that I want it here. So...
Questions:
How can I get the values of the Json["Search"] with mappable classes?
Do I have to change the classes that I have? If yes, how and why?
I'm too confused by so many layers. Also, I'm beginner with swift and I'm using this ObjectMapper for the first time. Sorry for so much code here, but I think I had to explain my scenario.
You have to map each property with correct data type of that property. One of the objects in your response contains a Boolean value ( For ex. "Response"), but you are declaring it as a String. We have to match the data type of the property exactly, else the object will be nil and will not be mapped.
Also search by id response does not match your mapper class.
let Result = response.result.value is wrong. response.result.value would yield you SearchResponse object.
Bottom line
You have to get the mapping part right first. Any mismatching types will be not be mapped. Using response object will give you the whole object with all the mappings instead of the JSON obviously. So it should be: let movie = response.result.value. Then you access movie's properties like for ex. movie.actors
I've been trying to find out how to save a part of a JSON response from Alamofire as a variable to make an if statement and can't seem to find how to do it.
I made a piece of code to use as an example for the question which consists of the following:
import UIKit
import Alamofire
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request("http://api.openweathermap.org/data/2.5/weather?q=London,uk&appid=44143256ee15e9c3f521ca062463dd8d").responseJSON { response in
print(response.result) // result of response serialization
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
}
}
}
I get the following response from the API request:
JSON: {
base = stations;
clouds = {
all = 0;
};
cod = 200;
coord = {
lat = "51.51";
lon = "-0.13";
};
dt = 1485031800;
id = 2643743;
main = {
humidity = 83;
pressure = 1026;
temp = "270.54";
"temp_max" = "273.15";
"temp_min" = "267.15";
};
name = London;
sys = {
country = GB;
id = 5091;
message = "0.0038";
sunrise = 1484985141;
sunset = 1485016345;
type = 1;
};
visibility = 7000;
weather = (
{
description = haze;
icon = 50n;
id = 721;
main = Haze;
}
);
wind = {
speed = 1;
};
}
From this JSON response I would like to save the weather description so I could use the following statement:
if var currentWeather = sunny {
return "Nice day!"
} else {
return "Uh-Oh, keep warm!"
}
If anyone could help me with this I would greatly appreciate it! If I have to use SwiftyJSON to make it easier that's fine I've just been struggling with this for a while and need to figure it out!
You can parse your result following the printout of your JSON response:
guard let JSON = response.result.value as? [String:Any],
let weather = JSON["weather"] as? [[String:Any]] else {
print("Could not parse weather values")
return
}
for element in weather {
if let description = element["description"] as? String {
print(description)
}
}
If you wanted to save the result, or do something based on a certain result as you described, you could insert something else instead of just print(description) like:
if description == "sunny" {
//do something
}
Let me know if this makes sense. Keep in mind that ( and ) in the Xcode console means "Array".