get data from AF responseJSON - swift

I have code to process location data so I can extract details that can anonymize the data -- for example, if my places API says it is a _type: "Building" and building: "Safeway" -- I could save the data as an md5 of the lat/long:"safeway", and all safeways would look the same when inspecting my location data. That's also what I want.
static func getLocationData(location: CLLocation, _ callback: #escaping (CLLocation?) -> Void) {
let parameters = [
"q": location.coordinate.latitude.description + "," + location.coordinate.longitude.description,
"key": Places.OPENCAGEDATA_API_KEY
]
AF.request(Places.uri, method: .get, parameters: parameters, encoding: URLEncoding(destination: .queryString)).responseJSON { response in
switch response.result {
case .success(let json):
print(json)
DispatchQueue.main.async {
callback(location)
}
case .failure(let error):
print(error)
callback(nil)
}
}
}
This transaction works, as I see printed:
{
documentation = "https://opencagedata.com/api";
licenses = (
{
name = "CC-BY-SA";
url = "https://creativecommons.org/licenses/by-sa/3.0/";
},
{
name = ODbL;
url = "https://opendatacommons.org/licenses/odbl/summary/";
}
);
rate = {
limit = 2500;
remaining = 2496;
reset = 1556150400;
};
results = (
{
annotations = {
DMS = {
lat = "MYLAT N";
lng = "MYLONG W";
};
FIPS = ...
But now json is just a type Any that happens to print nicely. How would I get , for example, json.results.annotations.DMS.lat?

This should help :
AF.request(Places.uri, method: .get, parameters: parameters, encoding: URLEncoding(destination: .queryString)).responseString { response in
switch response.result {
case .success(let json):
print(json)
if let returnedValue = responseObject.result.value, !returnedValue.isEmpty {
do {
let locationObject = try JSONDecoder().decode(Location.self, from: (returnedValue.data(using: .utf8))!)
} catch let e {
print("Couldn't Parse data because... \(e)")
}
}
DispatchQueue.main.async {
callback(location)
}
case .failure(let error):
print(error)
callback(nil)
}
}

You can try
if let res = json as? [String: Any]{
if let inner = res["results"] as? [[String: Any]] {
for item in inner {
if let ert = item["annotations"] as? [[String: Any]] {
for ann in ert {
print(ann["lat"])
}
}
}
}
}
Also you can do
struct Root: Codable {
let results: [DataClass]
}
struct DataClass: Codable {
let annotations: [Anno]
}
struct Anno: Codable {
let lat:Double // or String as in your question it's a String IDN if this is a description
}
do {
guard let data = try JSONSerialization.data(withJSONObject: json, options: []) else { return }
let locationObject = try JSONDecoder().decode(Root.self, from:data)
} catch {
print(error)
}

Related

How can I write a generic wrapper for alamofire request in swift?

How can I write a generic wrapper for alamofire request ?
How can I convert the POST and GET function to Generic function in the following code?
I need to have generic request functions that show different behaviors depending on the type of data received.
Also, Can the response be generic?
My non-generic code is fully outlined below
import Foundation
import Alamofire
import SwiftyJSON
// for passing string body
extension String: ParameterEncoding {
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var request = try urlRequest.asURLRequest()
request.httpBody = data(using: .utf8, allowLossyConversion: false)
return request
}
}
public class ConnectionManager {
func Post (FirstName: String, LastName: String, NationalID: String, NationalCode: String, BirthDate: Date,Image: String,isFemale: Bool,Age:Int64,Avg:Double, completion: #escaping CompletionHandler) {
let body: [String:Any] = [
"FirstName":FirstName,
"LastName": LastName,
"NationalID": NationalID,
"NationalCode": NationalCode,
"BirthDate":BirthDate,
"Image": Image,
"isFemale": isFemale,
"Age": Age,
"Avg": Avg
]
Alamofire.request(BASE_URL, method: .post, parameters: body, encoding: JSONEncoding.default, headers: HEADER).responseJSON { (response) in
if response.response?.statusCode == 200 {
guard let data = response.result.value else { return }
print(data)
completion(true)
} else {
print("error reg auth service")
guard let er = response.result.value else { return }
print(er)
completion(false)
debugPrint(response.result.error as Any)
}
}
}
func get(FirstName: String, LastName: String, NationalID: String, NationalCode: String, BirthDate: Date,Image: String,isFemale: Bool,Age:Int64,Avg:Double, completion: #escaping CompletionHandler) -> [Person] {
let body: [String:Any] = [
"FirstName":FirstName,
"LastName": LastName,
"NationalID": NationalID,
"NationalCode": NationalCode,
"BirthDate":BirthDate,
"Image": Image,
"isFemale": isFemale,
"Age": Age,
"Avg": Avg
]
Alamofire.request(BASE_URL, method: .get, parameters: body, encoding: JSONEncoding.default, headers: HEADER).responseJSON { (response) in
if response.response?.statusCode == 200 {
print("no error login in authservice")
guard let data = response.result.value else { return }
print(data)
completion(true)
}
else if response.response?.statusCode == 400 {
print("error 400 login in authservice")
guard let er = response.result.value else { return }
print(er)
debugPrint(response.result.error as Any)
completion(false)
} else {
print("error ## login in authservice")
guard let er = response.result.value else { return }
print(er)
debugPrint(response.result.error as Any)
completion(false)
}
}
return [Person]()
}
}
The best idea is to use the URLRequestConvertible Alamofires protocol and create your own protocol and simple structs for every API request:
import Foundation
import Alamofire
protocol APIRouteable: URLRequestConvertible {
var baseURL: String { get }
var path: String { get }
var method: HTTPMethod { get }
var parameters: Parameters? { get }
var encoding: ParameterEncoding { get }
}
extension APIRouteable {
var baseURL: String { return URLs.baseURL }
// MARK: - URLRequestConvertible
func asURLRequest() throws -> URLRequest {
let url = try baseURL.asURL().appendingPathComponent(path)
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = method.rawValue
return try encoding.encode(urlRequest, with: parameters)
}
}
and request can look like this:
struct GetBooks: APIRouteable {
var path: String
var method: HTTPMethod
var parameters: Parameters?
var encoding: ParameterEncoding
}
and inside the APIClient prepare the generic method:
func perform<T: Decodable>(_ apiRoute: APIRouteable,
completion: #escaping (Result<T>) -> Void) {
let dataRequest = session
.request(apiRoute)
dataRequest
.validate(statusCode: 200..<300)
.responseDecodable(completionHandler: { [weak dataRequest] (response: DataResponse<T>) in
dataRequest.map { debugPrint($0) }
let responseData = response.data ?? Data()
let string = String(data: responseData, encoding: .utf8)
print("Repsonse string: \(string ?? "")")
switch response.result {
case .success(let response):
completion(.success(response))
case .failure(let error):
completion(.failure(error))
}
})
}
and use it:
func getBooks(completion: #escaping (Result<[Book]>) -> Void) {
let getBooksRoute = APIRoute.GetBooks()
perform(getBooksRoute, completion: completion)
}

Alamofire invalid URL

Getting error while trying to get info.
Server supports russian language in URL.
Error:
[Result]: FAILURE: invalidURL(url: "http://ws.audioscrobbler.com/2.0/?method=artist.search&artist=сплин&api_key=bad5acca27008a09709ccb2c0258003b&format=json")
Request:
Alamofire.request("http://ws.audioscrobbler.com/2.0/? method=artist.search&artist=\(nameOfArtist)&api_key=bad5acca27008a09709ccb2c0258003b&format=json")
.responseObject { (response: DataResponse<SearchArtistAPIModel>) in
//to get status code
debugPrint(response)
if let status = response.response?.statusCode {
switch(status){
case 200...499:
print("example success")
default:
print("error with response status: \(status)")
}
}
if let artistName = response.result.value {
guard let artistsArray = artistName.results?.artistmatches?.artist else { return }
var names: [String] = []
for artists in artistsArray {
guard let artistsName = artists.name else { return }
names.append(artistsName)
completion(names)
}
}
}
Encode your URL then create request.
let urlString = "http://ws.audioscrobbler.com/2.0/?method=artist.search&artist=\(nameOfArtist)&api_key=bad5acca27008a09709ccb2c0258003b&format=json"
if let encoded = urlString.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed),let url = URL(string: encoded)
{
Alamofire.request(url).validate().responseJSON { (json) in
print(json)
//Enter your code here
}
}

Alamofire print request gives nil

Hi I try to print from request but catch nil
Alamofire.request("http://.../api/v1.8/set/order/", method: .get, parameters: params)
.responseString { response in
switch response.result {
case .success(let value):
print("value\(value)")
var json = JSON(value)
let nass = json["description"].string
print("nass\(nass)")
}
in cinsole print me
value {"result":{"code":"15","description":"Invalid account email"}}
nass nil
Alamofire.request("http://xincal.ru/api/v1.8/set/order/", method: .get, parameters: params).responseJSON { response in
if let r = response.result.value as? NSDictionary
if let result = r?.value(forKey: "result") as? NSDictionary {
if let nass = result.value(forKey: "description") as? String {
print(nass)
}
}
} else {
print("Connection error")
}
}
But we have better way to parse the response data using Codable.
Prepare models for the response
struct Result: Codable {
let code: String
let description: String
}
struct Response: Codable {
let result: Result
}
Alamofire.request("http://xincal.ru/api/v1.8/set/order/", method: .get, parameters: params).responseJSON { response in
if let data = response.data
let object = try! JSONDecoder().decode(Response.self, from: jsonData)
print(object.result.description)
} else {
print("Connection error")
}
}

Api call not working in alamofire but working in postman

I have an API Call which is working using postman but when same data is given in alamofire it is not working. Please help me. I spent around 1 day fixing it.
let parameters = [
"paymenttype":"debitcard",
"coupon":"",
"products":[["pid":"72","qnty":"1"],["pid":"4","qnty":"1"],["pid":"3","qnty":"1"]],
"grandtotal":"71499.52",
"discount":"",
"itemtotalval":"69417.0",
"cgst":"1041.26",
"comment":"sss",
"sgst":"1041.26",
"billingaddress":[["bfname":"debdeep nandy","bmobilenumber":"9875463215","bpincode":"823691" , "baddress":"kolkata","bcity":"kolkata","bstate":"West Bengal","bcountry":"india","bemail":"dev#gmail.com"]],
"shippingaddress":[["sfname":"debdeep nandy","smobilenumber":"9875463215","spincode":"823691","saddress":"kolkata","scity":"kolkata","sstate":"West Bengal","scountry":"india","semail":"india"]],
"user_id":1
] as [String : AnyObject]
Alamofire.request(
URL(string:"http://jarsservices.com/pccj-app/apiv1/place_order")!,
method: .post,
parameters: parameters)
.validate()
.responseString { (response) -> Void in
guard response.result.isSuccess else {
print("Error while sending data: \(response.result.error)")
return
}
if let value = response.result.value as? [String: Any] {
DispatchQueue.main.async {
// self.activityView.stopAnimating()
if value["status"] as! Int == 1 {
self.view.makeToast("Thank you! Your feedback is recorded in server.")
}
else {
self.view.makeToast(value["msg"] as? String)
}
}
}
else {
print("Malformed data received from fetchAllRooms service")
return
}
}
There is no authentication in headers. please help me out.
I have added the image from postman that is working.
Try Out this code,
Add this extension
extension Array where Element: Codable {
public var toData: Data {
let encoder = JSONEncoder()
do {
return try encoder.encode(self)
}
catch {
fatalError(error.localizedDescription)
}
}
public var toJson: String? {
return toData.toJson
}
}
extension Data {
// Convert NSData to String
public var toJson : String? {
return String(data: self, encoding: String.Encoding.utf8)
}
}
You web API Parameters like
let parameters: [String : Any] = [
"paymenttype":"debitcard",
"coupon":"",
"products":[["pid":"72","qnty":"1"],["pid":"4","qnty":"1"],["pid":"3","qnty":"1"]].toJson ?? "[]",
"grandtotal":"71499.52",
"discount":"",
"itemtotalval":"69417.0",
"cgst":"1041.26",
"comment":"sss",
"sgst":"1041.26",
"billingaddress":[["bfname":"debdeep nandy","bmobilenumber":"9875463215","bpincode":"823691" , "baddress":"kolkata","bcity":"kolkata","bstate":"West Bengal","bcountry":"india","bemail":"dev#gmail.com"]].toJson ?? "[]",
"shippingaddress":[["sfname":"debdeep nandy","smobilenumber":"9875463215","spincode":"823691","saddress":"kolkata","scity":"kolkata","sstate":"West Bengal","scountry":"india","semail":"india"]].toJson ?? "[]",
"user_id":1
]
And Your else Part is
else {
print("Malformed data received from fetchAllRooms service")
debugPrint(String(data: response.data!, encoding: String.Encoding.utf8))
//Print out our data
return
}
Note: in Alamofire parameter Pass Array or Dictionary value using JSON string format then this is worked.
Try out with this method:
func hitPostServiceJsonForm(_ params:Dictionary<String,Any>,unReachable:(() -> Void),handler:#escaping ((Dictionary<String,Any>?) -> Void)) {
var request = URLRequest(url: URL(string: BASE_URL)!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try! JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
print_debug(BASE_URL)
Alamofire.request(request).responseJSON { response in
SwiftLoader.hide()
//print_debug("Request: \(String(describing: response.request))") // original url request
//print_debug("Response: \(String(describing: response.response))") // http url response
print_debug("Result: \(response.result)") // response serialization result
switch response.result {
case .success:
if let jsonDict = response.result.value as? Dictionary<String,Any> {
print_debug("Json Response: \(jsonDict)") // serialized json response
handler(jsonDict)
}
else{
handler(nil)
}
if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
print("Server Response: \(utf8Text)") // original server data as UTF8 string
}
break
case .failure(let error):
handler(nil)
print_debug(error)
break
}
}
}

While Loop increments and re-runs before Promisekit can finish running and return the proper value in Swift

I'm trying to use a while loop with Promisekit with Alamofire to chain four GET requests, return a value, and rerun the four requests with a new parameter. This is the current code that I'm using:
var index = 0
var count = classDictionary["class"]!.count-1
while index <= count {
firstly {
parseBooksXML(index)
}.then { abrevCode in
self.parseBooksXML2(index, key: abrevCode)
}.then { courseNumber in
self.parseBooksXML3(index, key: courseNumber)
}.then { instructorCode in
self.parseBooksXML4(instructorCode)
}
index += 1
}
Each of the first three functions returns a promised string value which is then passed onto the next function until the fourth and final function calls another function to parse the downloaded HTML using Kanna.
Ideally I would like all four functions to be called and completed after which the index will increment and run the loop again using the new index number. As a note, the index in passed onto the functions as a way to identify which index in an array a value should be compared to.
For clarity, I have included the code for the parseBooksXML functions below:
func parseBooksXML(index: Int) -> Promise<String> {
return Promise {fulfill, reject in
let headers = [
"Referer": "URL"
]
Alamofire.request(.GET, "URL", headers: headers)
.responseData { response in
switch response.result {
case .Success:
let xml = SWXMLHash.parse(response.data!)
do {
let range = self.classDictionary["class"]![index].rangeOfString("[a-zA-Z]{2,4}", options: .RegularExpressionSearch)
let result = self.classDictionary["class"]![index].substringWithRange(range!)
try self.abrevCode = (xml["departments"]["department"].withAttr("abrev", result).element!.attribute(by: "id")!.text)
}
catch {
print("Error: \(error)")
}
fulfill(self.abrevCode)
case .Failure(let error):
print(error)
}
}
}
}
func parseBooksXML2(index: Int, key: String) -> Promise<String> {
return Promise {fulfill, reject in
let headers = [
"Referer": "URL"
]
Alamofire.request(.GET, "URL", headers: headers)
.responseData { response in
switch response.result {
case .Success:
let xml = SWXMLHash.parse(response.data!)
do {
let range = self.classDictionary["class"]![index].rangeOfString("\\d\\d\\d", options: .RegularExpressionSearch)
let result = self.classDictionary["class"]![index].substringWithRange(range!)
try self.courseNumber = (xml["courses"]["course"].withAttr("name", result).element?.attribute(by: "id")?.text)!
}
catch {
print("Error: \(error)")
}
fulfill(self.courseNumber)
case .Failure(let error):
print(error)
}
}
}
}
func parseBooksXML3(index: Int, key: String) -> Promise<String> {
return Promise {fulfill, reject in
let headers = [
"Referer": "URL"
]
Alamofire.request(.GET, "URL", headers: headers)
.responseData { response in
switch response.result {
case .Success:
let xml = SWXMLHash.parse(response.data!)
do {
let range = self.classDictionary["class"]![index].rangeOfString("[a-zA-Z]{1,3}?\\d?\\d?\\d?$", options: .RegularExpressionSearch)
let result = self.classDictionary["class"]![index].substringWithRange(range!)
try self.instructorCode = (xml["sections"]["section"].withAttr("instructor", self.classTeacher[index]).element?.attribute(by: "id")?.text)!
}
catch {
print("Error: \(error)")
}
fulfill(self.instructorCode)
case .Failure(let error):
print(error)
}
}
}
}
func parseBooksXML4(key: String) -> Void {
let headers = [
"Referer": "URL"
]
Alamofire.request(.GET, "URL", headers: headers)
.responseData { response in
switch response.result {
case .Success:
self.parseISBN(String(data: response.data!, encoding: NSUTF8StringEncoding)!)
case .Failure(let error):
print(error)
}
}
}
Any help would be appreciated!
You need to use when:
let count = classDictionary["class"]!.count-1
let promises = (0..<count).map { index -> Promise<ReplaceMe> in
return firstly {
parseBooksXML(index)
}.then { abrevCode in
self.parseBooksXML2(index, key: abrevCode)
}.then { courseNumber in
self.parseBooksXML3(index, key: courseNumber)
}.then { instructorCode in
self.parseBooksXML4(instructorCode)
}
}
when(fulfilled: promises).then {
//…
}
Since parseBooksXML4 call is async, you should wrap parseBooksXML4() call to return promise and wait for that to finish before increment index.
firstly {
parseBooksXML(index)
}.then { abrevCode in
self.parseBooksXML2(index, key: abrevCode)
}.then { courseNumber in
self.parseBooksXML3(index, key: courseNumber)
}.then { instructorCode in
self.parseBooksXML4(instructorCode)
}.then { _ in
index += 1
}
}

Resources