need to get the country name from open api - ios

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
}

Related

I try to convert Json string (response) to json object but after JSONSerialization the output is not clear

I got a response from web service and try to work on it to put it in a model, but i can't work with it .
this is the response.string
"{\"USER_NO\":1,\"USER_NAME\":\"المسار البديل لتكنولوجيا المعلومات\",\"TASK_CATEGORY_NO\":1,\"USER_ID_NUMBER\":\"1031523473\",\"Mobile_No\":\"0567123432\"}"
I used extension :
extension String{
func tooDictionary() -> NSDictionary {
let blankDict : NSDictionary = [:]
if let data = self.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as! NSDictionary
} catch {
print(error.localizedDescription)
}
}
return blankDict
}
}
to convert it to dictionary then put it in object but i failed to do that
i'm sorry but i'm new developer in swift4 ,any help please
and thank
Use Decodable:
struct User: Decodable {
private enum CodingKeys : String, CodingKey {
case userNumber = "USER_NO", taskCategoryNumber = "TASK_CATEGORY_NO"
case userName = "USER_NAME", userID = "USER_ID_NUMBER", mobileNumber = "Mobile_No"
}
let userNumber, taskCategoryNumber: Int
let userName, userID, mobileNumber : String
}
let jsonString = """
{"USER_NO":1,"USER_NAME":"المسار البديل لتكنولوجيا المعلومات","TASK_CATEGORY_NO":1,"USER_ID_NUMBER":"1031523473","Mobile_No":"0567123432"}
"""
let data = Data(jsonString.utf8)
do {
let result = try JSONDecoder().decode(User.self, from: data)
print(result)
} catch { print(error) }
Note: Don't use NSArray / NSDictionary in Swift

Get JSON data from NSDictionary

I have struct in my api
{
"Hall":"Hall",
"Date":20180501,
"Prices":[
{
"Time":1,
"Price":4000
},
{
"Time":2,
"Price":4000
},
{
"Time":3,
"Price":4000
}
]
}
Now I'm stuck and can't pull out the price and time. I understand that there were many question, but I still can't understand, please help.
I use this code:
let url = URL(string: "http://<...>/api/prices?hall=<...>&date=20180501")
var request = URLRequest(url: url!)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
if let err = error {
print(err)
} else {
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary {
// ...
}
DispatchQueue.main.async {
self.collectionView.reloadData()
}
} catch {
print("error")
}
}
})
task.resume()
}
I'm new with json, and just started learning it. I know it's easy, but I can't figure it out. I also know that i can use codable and decodable, but now I need to get price and time in this implementation.
First of all don't use NSArray / NSDictionary, use native Swift types.
The value for key Prices is an array of [String:Int] dictionaries:
if let jsonResult = try JSONSerialization.jsonObject(with: data!) as? [String:Any],
let prices = jsonResult["Prices"] as? [[String:Int]] {
for price in prices {
print(price["Time"]!, price["Price"]!)
}
}
However I would recommended to decode the JSON into a struct which is very simple in Swift 4
struct Item : Decodable {
let hall : String
let date : Int
let prices : [Price]
private enum CodingKeys : String, CodingKey { case hall = "Hall", date = "Date", prices = "Prices"}
}
struct Price : Decodable {
let time, price : Int
private enum CodingKeys : String, CodingKey { case time = "Time", price = "Price"}
}
do {
let result = try JSONDecoder().decode(Item.self, from: data!)
print(result)
} catch { print(error) }
Product Structure:
struct Product{
let time:Int
let price:Int
init(_ object:[String:Int]){
self.time = object["Time"] ?? 0
self.price = object["Price"] ?? 0
}
}
Class Variable:
var products = [Product]()
JSON parsing:
do{
if let jsonObject = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:Any] {
//Use Swift dictionary instead of NSDictionary
if let prices = jsonObject["Prices"] as? [[String:Int]]{
for price in prices{
self.products.append(Product(price))
}
}
}
}catch{
print("Error: ",error.localizedDescription)
}
Now, your products Array will contain all Price and Time
Example:
for product in products{
print("Time:",product.time)
print("Price:",product.price)
}
Output:
Time: 1
Price: 4000
Time: 2
Price: 4000
Time: 3
Price: 4000
Note: For better understanding, this is my video series about JSON parsing in swift 4
var timeArray = [String]()
var priceArray = [String]()
if jsonResult.object(forKey:"Prices") as? NSArray != nil
{
let pricesArray = jsonResult.object(forKey:"Prices") as! NSArray
for i in 0..<self.pricesArray.count
{
// Getting Time
if jsonResult.object(forKey:"Time") as? Int != nil
{
self.timeArray.append(jsonResult.object(forKey:"Time") as! Int)
}
else
{
print("Time is not a intrger")
}
// Getting Price
if jsonResult.object(forKey:"Price") as? Int != nil
{
self.priceArray.append(jsonResult.object(forKey:"Price") as! Int)
}
else
{
print("Price is not a intrger")
}
}
}
else
{
print("Empty Array")
}
if let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:Any], let pirces = json["prices"] // checking json is formed well.
{
for price in prices { // iterating throught array that you received.
print (price["Price"])
}
}

Passing data from JSON to table view cell in Swift 3

I'm trying to pass data from a JSON response to a table view cell. I'm having problems with capturing the response values that I'm extracting in URLSession.shared.dataTask.
func callYouTubeAPIToGetAllVideos() {
let url = URL(string: "https://www.googleapis.com/youtube/v3/search?part=snippet&channelId=XYZ&maxResults=50&order=date&key=ABC")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error!)
} else {
if let usableData = data {
let json = try? JSONSerialization.jsonObject(with: usableData, options: [])
if let dictionary = json as? [String: Any?] {
if let array = dictionary["items"] as? [Any] {
for object in array {
if let objectAsDictionary = object as? [String: Any?] {
if let objectWithKindAndVideoId = objectAsDictionary["id"] as? [String: String] {
if let videoId = objectWithKindAndVideoId["videoId"] {
//pass data to table cell
}
}
if let snippet = objectAsDictionary["snippet"] as? [String: Any] {
if let description = snippet["description"] {
//pass data to table cell
}
}
}
}
}
}
}
}
}
task.resume()
}
I tried appending the values to an instance variable but it didn't work.
Sorry about the messy code, this is my 1st time working with JSON in Swift.
First of all never declare a received JSON dictionary as [String:Any?]. A received dictionary value can't be nil.
Declare a custom struct Video.
struct Video {
let videoId : String
let description : String
}
Declare a data source array.
var videos = [Video]()
Parse the JSON into the array and reload the table view on the main thread.
func callYouTubeAPIToGetAllVideos() {
let url = URL(string: "https://www.googleapis.com/youtube/v3/search?part=snippet&channelId=XYZ&maxResults=50&order=date&key=ABC")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error!)
} else {
do {
if let dictionary = try JSONSerialization.jsonObject(with: data!) as? [String: Any],
let array = dictionary["items"] as? [[String: Any]] {
for object in array {
if let objectWithKindAndVideoId = object["id"] as? [String: String],
let snippet = object["snippet"] as? [String: Any] {
let videoId = objectWithKindAndVideoId["videoId"] ?? ""
let description = snippet["description"] as? String ?? ""
let video = Video(videoId: videoId, description: description)
self.videos.append(video)
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
} catch {
print(error)
}
}
}
task.resume()
}
In cellForRow assign the values to the text properties
let video = videos[indexPath.row]
cell.textLabel!.text = video.videoId
cell.detailTextLabel?.text = video.description

Parsing data asynchronously using URLSession and SwiftyJSON not working

I'm trying to parse data using SwiftyJSON but for some reason I keep getting runtime errors. I left the errors in the code with //, there are 2 of them. This is a sample of the data I'm trying to parse:
result = {
cost = {
"airbnb_median" = {
CHF = 86;
USD = 86;
}
}
This is the function I use to parse the JSON after it has been retrieved by URLSession:
func parseJSONFromData(_ jsonData: Data?) -> JSON?
{
if let data = jsonData {
do {
let jsonDictionary = try JSON(data)
return jsonDictionary
} catch let error as NSError {
print("error processing json data: \(error.localizedDescription)")
}
}
return nil
}
This is my custom JSON object:
class JSONObject {
var airbnbUS: Int
var airbnbLocal: Int
init(airbnbUS: Int, airbnbLocal: Int)
init(resultsDictionary:[String: Any]){
airbnbUS = (resultsDictionary["cost"]["airbnb_median"]["USD"] as? Int)!///Error Type 'Any?' has no subscript members
airbnbLocal = (resultsDictionary["cost"]["airbnb_median"]["CHF"] as? Int)!
}
This is how I update the dictionary for the JSOn object values:
static func updateResultsDictionary(urlExtension: String, completion:
#escaping (JSONObject?) -> Void) {
let nm = NetworkManager.sharedManager
_ = nm.getJSONData(urlExtension: urlExtension) {data in
guard let jsonDictionary = nm.parseJSONFromData(data),
let resultDictionaries = jsonDictionary["result"] //Error Initializer for conditional binding must have Optional type, not 'JSON'
else {
completion(nil)
return
}
for resultsDictionary in resultDictionaries {
let jsonInfo = JSONObject(resultsDictionary: resultsDictionary)
completion(jsonInfo)
}
}
}

Deserialize a JSON array to a Swift array of objects

I am new to Swift, and am not able to figure out how to deserialize a JSON array to an array of Swift objects. I'm able to deserialize a single JSON user to a Swift user object fine, but just not sure how to do it with a JSON array of users.
Here is my User.swift class:
class User {
var id: Int
var firstName: String?
var lastName: String?
var email: String
var password: String?
init (){
id = 0
email = ""
}
init(user: NSDictionary) {
id = (user["id"] as? Int)!
email = (user["email"] as? String)!
if let firstName = user["first_name"] {
self.firstName = firstName as? String
}
if let lastName = user["last_name"] {
self.lastName = lastName as? String
}
if let password = user["password"] {
self.password = password as? String
}
}
}
Here's the class where I'm trying to deserialize the JSON:
//single user works.
Alamofire.request(.GET, muURL/user)
.responseJSON { response in
if let user = response.result.value {
var swiftUser = User(user: user as! NSDictionary)
}
}
//array of users -- not sure how to do it. Do I need to loop?
Alamofire.request(.GET, muURL/users)
.responseJSON { response in
if let users = response.result.value {
var swiftUsers = //how to get [swiftUsers]?
}
}
The best approach is the use Generic Response Object Serialization provided by Alamofire here is an example :
1) Add the extension in your API Manager or on a separate file
public protocol ResponseObjectSerializable {
init?(response: NSHTTPURLResponse, representation: AnyObject)
}
extension Request {
public func responseObject<T: ResponseObjectSerializable>(completionHandler: Response<T, NSError> -> Void) -> Self {
let responseSerializer = ResponseSerializer<T, NSError> { request, response, data, error in
guard error == nil else { return .Failure(error!) }
let JSONResponseSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
let result = JSONResponseSerializer.serializeResponse(request, response, data, error)
switch result {
case .Success(let value):
if let
response = response,
responseObject = T(response: response, representation: value)
{
return .Success(responseObject)
} else {
let failureReason = "JSON could not be serialized into response object: \(value)"
let error = Error.errorWithCode(.JSONSerializationFailed, failureReason: failureReason)
return .Failure(error)
}
case .Failure(let error):
return .Failure(error)
}
}
return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
}
}
public protocol ResponseCollectionSerializable {
static func collection(response response: NSHTTPURLResponse, representation: AnyObject) -> [Self]
}
extension Alamofire.Request {
public func responseCollection<T: ResponseCollectionSerializable>(completionHandler: Response<[T], NSError> -> Void) -> Self {
let responseSerializer = ResponseSerializer<[T], NSError> { request, response, data, error in
guard error == nil else { return .Failure(error!) }
let JSONSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
let result = JSONSerializer.serializeResponse(request, response, data, error)
switch result {
case .Success(let value):
if let response = response {
return .Success(T.collection(response: response, representation: value))
} else {
let failureReason = "Response collection could not be serialized due to nil response"
let error = Error.errorWithCode(.JSONSerializationFailed, failureReason: failureReason)
return .Failure(error)
}
case .Failure(let error):
return .Failure(error)
}
}
return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
}
}
2) update your model object like this:
final class User: ResponseObjectSerializable, ResponseCollectionSerializable {
let username: String
let name: String
init?(response: NSHTTPURLResponse, representation: AnyObject) {
self.username = response.URL!.lastPathComponent!
self.name = representation.valueForKeyPath("name") as! String
}
static func collection(response response: NSHTTPURLResponse, representation: AnyObject) -> [User] {
var users: [User] = []
if let representation = representation as? [[String: AnyObject]] {
for userRepresentation in representation {
if let user = User(response: response, representation: userRepresentation) {
users.append(user)
}
}
}
return users
}
}
3) then you can use it like that :
Alamofire.request(.GET, "http://example.com/users")
.responseCollection { (response: Response<[User], NSError>) in
debugPrint(response)
}
Source: Generic Response Object Serialization
Useful Link: Alamofire JSON Serialization of Objects and Collections
Since you are using Alamofire to make your requests why don't you give a chance to Hearst-DD ObjectMapper it has an Alamofire extension AlamofireObjectMapper. I think it'll save you time!
I would loop through them then add each user to an array (preferably a property of the VC and not an instance variable) but here is an example.
Alamofire.request(.GET, "YourURL/users")
.responseJSON { response in
if let users = response.result.value {
for user in users {
var swiftUser = User(user: user as! NSDictionary)
//should ideally be a property of the VC
var userArray : [User]
userArray.append(swiftUser)
}
}
}
You could also try EVReflection https://github.com/evermeer/EVReflection
It's even more simple, i.e. to parse JSON (code snippet taken from EVReflection link):
let json:String = "{
\"id\": 24,
\"name\": \"Bob Jefferson\",
\"friends\": [{
\"id\": 29,
\"name\":
\"Jen Jackson\"}]}"
you can use this class:
class User: EVObject {
var id: Int = 0
var name: String = ""
var friends: [User]? = []
}
in this way:
let user = User(json: json)

Resources