Extract response data from api call in alamofire - ios

i've read many documents about getting the response object from the api , but cant derive how to extract the data. I've used alamofire for api call . The api call is like
AF.request("http://10.177.41.163:9000/signup",
method: .post,
parameters: parameters,
encoder: JSONParameterEncoder.default).responseJSON{ response in
print(response.result)
in my print statement i get the responses as
`success({
error = {
code = PC05;
message = "User already exsists";
};
payload = {
};
success = 0;
})`
which is fine , but i want to extract suppose the error code, how to achieve that? and in general how to extract data from responses in swift.

You need to decode the jSON response, so create a Model to decode this, of course also check the response if it was successful before, you'll set an example:
Declare yourself a model:
struct RootResponse : Decodable {
var error: ErrorStruct
var payload: PayloadStruct
var success: Int
}
struct ErrorStruct: Decodable {
var code: String
var message: String
}
struct PayloadStruct : Decodable {
var ?
}
Once you've declared your model based on your jSON response, then you can switch to the function that makes the request to you:
AF.request("http://10.177.41.163:9000/signup",
method: .post,
parameters: parameters,
encoder: JSONParameterEncoder.default).responseJSON{ response in
print(response.result)
switch response.result {
case .success:
if let data = response.data {
print(data)
// Convert This in JSON
do {
let responseDecoded = try JSONDecoder().decode(RootResponse.self, from: data)
print("USER: ", responseDecoded.error.code, "Etc...")
}catch let error as NSError{
print(error)
}
}
case .failure(let error):
print("Error:", error)
}
}
I hope that's been helpful.

AF.request("http://10.177.41.163:9000/signup",
method: .post,
parameters: parameters,
encoder: JSONParameterEncoder.default).responseJSON{ response in
print(response.result)
let responseJSON = JSON(response.result) // response converted in json format
let statusCode = responseJSON["error"]["code"].stringValue // You can get status code
Note : Install pod 'SwiftyJSON' to convert response.result in JSON format.

Related

How to print result from alamofire response using codable

New to codable and i have tried to create a class for alamofire with codable and tried to make an api request. I have getting some Swift.DecodingError.typeMismatch error and i figured it out it because of my model class. Now what i need is i want to print alamofire response in JSON(String) formate before it decoding so that i can identify the typeMismatch
static func performRequest<T:Decodable>(route:APIRouter, decoder: JSONDecoder = JSONDecoder(), completion:#escaping (Result<T,Error>)->Void) -> DataRequest {
return AF.request(route)
.responseDecodable (decoder: decoder){ (response: DataResponse<T>) in
print(response.result)
completion(response.result)
}
}
i want some code to print the actual result from alamofire
You can print the raw Data in your responseDecodable closure by grabbing it from the DataResponse:
print(response.data.map { String(decoding: $0, as: UTF8.self) } ?? "No data.")
You can also add a separate serializer just to see the String:
.responseDecodable { }
.responseString { }
If you just want to see the response for debugging, you can debugPrint the response in the closure. This will print the request and response body datas as Strings.
.responseDecodable(of: T.self) { response in
debugPrint(response)
}

Alamofire - Bad request

I am afraid I cannot share the API url. But I have checked on Postman, it works. It is a POST request and following is the response :
{
"user_key": "b0aebdedb15e2beaaf479ca3c4f8227e8e970681"
}
Postman screenshot :
In code, this is the request I am making using Alamofire :
Alamofire.request("some url", method: .post, parameters: params, encoding: URLEncoding.httpBody, headers: ["Content-Type":"application/json"])
.responseObject { (response: DataResponse<User>) in
let userResponse = response.result.value
print("")
}
But userResponse comes to be nil. This is the User object :
import ObjectMapper
class User: Mappable {
var authToken: String?
required init?(map: Map) {
}
func mapping(map: Map) {
self.authToken <- map["user_key"]
}
}
I am using ObjectMapper here.
Assuming the it is not a JSON object, I tried to handle it as a String :
Alamofire.request("some url", method: .post, parameters: params, encoding: URLEncoding.httpBody, headers: ["Content-Type":"application/json"])
.responseString { (response: DataResponse<String>) in
let dataUser = response.data as! Any
let dataUser1 = response.data as! AnyObject
let error = response.error
let code = response.response?.statusCode
print(response.value)
print(response.result.value)
}
Still, nothing. I am not able to retrieve the value. The status code however comes out to be 400 (bad request).
What exactly is it ? JSON object or String ? How do I fix this ?
Just try to replace
this URLEncoding.httpBody to JSONEncoding.default
JSONEncoding.default
Uses JSONSerialization to create a JSON representation of the parameters object, which is set as the body of the request. The Content-Type HTTP header field of an encoded request is set to application/json.
Alamofire reference

Parsing JSON Lines with Alamofire/Codable

Is it possible to parse JSON lines with Alamofire and codable?
Here is my code right now.
Alamofire.request(url, method: .get, parameters: parameters, encoding: URLEncoding.default, headers: headers).responseString {(response) in
switch response.result {
case .success(let value):
print ("response is \(value)")
case .failure(let error):
print ("error is \(error)")
}
}
This prints all the JSON lines as a string but I want to serialize the response as an array of JSON. How would I do that? The problem with JSON lines is that it returns each set of json on a separate line so it is not familiar to alamofire.
Here is what I tried as if this was traditional JSON which obviously it is not so this did not work:
Alamofire.request(url, method: .get, parameters: parameters, encoding: URLEncoding.default, headers: headers).responseJSON {(response) in
switch response.result {
case .success(let value):
print ("response is \(value)")
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
let data = try! JSONSerialization.data(withJSONObject: value)
do {
let logs = try decoder.decode([Logs].self, from: data)
completion(logs)
} catch let error {
print ("error parsing get logs: \(error)")
}
case .failure(let error):
print ("failed get logs: \(error) ** \(response.result.value ?? "")")
}
}
For anybody unfamiliar with json lines here is the official format info: http://jsonlines.org
{"_logtype":"syslogline","_ingester":"agent","_ip":"40.121.203.183","pid":5573,"program":"docker","_host":"k8s-master-5A226838-0","logsource":"k8s-master-5A226838-0","_app":"syslog","_file":"/var/log/syslog","_line":"docker[5573]: I0411 00:18:39.644199 6124 conversion.go:134] failed to handle multiple devices for container. Skipping Filesystem stats","_ts":1491869920198,"timestamp":"2017-04-11T00:18:39.000Z","_id":"804760774821019649"}
{"_logtype":"syslogline","_ingester":"agent","_ip":"40.121.203.183","pid":5573,"program":"docker","_host":"k8s-master-5A226838-0","logsource":"k8s-master-5A226838-0","_app":"syslog","_file":"/var/log/syslog","_line":"docker[5573]: I0411 00:18:39.644167 6124 conversion.go:134] failed to handle multiple devices for container. Skipping Filesystem stats","_ts":1491869920198,"timestamp":"2017-04-11T00:18:39.000Z","_id":"804760774821019648"}
{"_logtype":"syslogline","_ingester":"agent","_ip":"40.121.203.183","pid":5573,"program":"docker","_host":"k8s-master-5A226838-0","logsource":"k8s-master-5A226838-0","_app":"syslog","_file":"/var/log/syslog","_line":"docker[5573]: I0411 00:18:37.053730 6124 operation_executor.go:917] MountVolume.SetUp succeeded for volume \"kubernetes.io/secret/6f322c04-e1d2-11e6-bca0-000d3a111245-default-token-swb07\" (spec.Name: \"default-token-swb07\") pod \"6f322c04-e1d2-11e6-bca0-000d3a111245\" (UID: \"6f322c04-e1d2-11e6-bca0-000d3a111245\").","_ts":1491869917193,"timestamp":"2017-04-11T00:18:37.000Z","_id":"804760762212941824"}
Here is an example of writing custom DataSerializer in Alamofire that you could use for decoding your Decodable object.
I am using an example from random posts json url https://jsonplaceholder.typicode.com/posts
Here is an example of Post class and custom serializer class PostDataSerializer.
struct Post: Decodable {
let userId: Int
let id: Int
let title: String
let body: String
}
struct PostDataSerializer: DataResponseSerializerProtocol {
enum PostDataSerializerError: Error {
case InvalidData
}
var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<[Post]> {
return { request, response, data, error in
if let error = error {
return .failure(error)
}
guard let data = data else {
return .failure(PostDataSerializerError.InvalidData)
}
do {
let jsonDecoder = JSONDecoder()
let posts = try jsonDecoder.decode([Post].self, from: data)
return .success(posts)
} catch {
return .failure(error)
}
}
}
}
You could simply hook this upto your Alamofire client which sends request to remote url like so,
let request = Alamofire.request("https://jsonplaceholder.typicode.com/posts")
let postDataSerializer = PostDataSerializer()
request.response(responseSerializer: postDataSerializer) { response in
print(response)
}
You could also do additional error checking for the error and http response code in serializeResponse getter of the custom serializer.
For you json line, it seems that each json object is separated with new line character in so called json line format. You could simply split the line with new line characters and decode each line to Log.
Here is Log class I created.
struct Log: Decodable {
let logType: String
let ingester: String
let ip: String
let pid: Int
let host: String
let logsource: String
let app: String
let file: String
let line: String
let ts: Float64
let timestamp: String
let id: String
enum CodingKeys: String, CodingKey {
case logType = "_logtype"
case ingester = "_ingester"
case ip = "_ip"
case pid
case host = "_host"
case logsource
case app = "_app"
case file = "_file"
case line = "_line"
case ts = "_ts"
case timestamp
case id = "_id"
}
}
And custom log serializer that you could use with your Alamofire. I have not handled error in the following serializer, I hope you can do it.
struct LogDataSerializer: DataResponseSerializerProtocol {
enum LogDataSerializerError: Error {
case InvalidData
}
var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<[Post]> {
return { request, response, data, error in
if let error = error {
return .failure(error)
}
guard let data = data else {
return .failure(LogDataSerializerError.InvalidData)
}
do {
let jsonDecoder = JSONDecoder()
let string = String(data: data, encoding: .utf8)!
let allLogs = string.components(separatedBy: .newlines)
.filter { $0 != "" }
.map { jsonLine -> Log? in
guard let data = jsonLine.data(using: .utf8) else {
return nil
}
return try? jsonDecoder.decode(Log.self, from: data)
}.flatMap { $0 }
return .success(allLogs)
} catch {
return .failure(error)
}
}
}
}
Alamofire is extensible, so I'd suggest writing your own response ResponseSerializer that can parse JSON by line. It seems that each line should parse fine, they're just not parseable together since the whole document isn't valid JSON.

Alamofire 4 Swift 3 GET request with parameters

I'm building a network stack using Alamofire 4 and Swift 3. Following the Alamofire guidelines I've created a router for the endpoints of the services. I'm currently using the free API of OpenWeatherMap but I'm finding problems in order to create a get request.
That's the url needed: http://api.openweathermap.org/data/2.5/weather?q=Rome&APPID=MY_API_KEY. Pasted on a browser, and using a real API Key it works and gives me back my nice json full of info about the weather in the given location.
On my App I can insert the parameters as Dictionary but I cannot find a way to append the api key at the end of the url.
That's my enum router:
enum OWARouter: URLRequestConvertible {
case byCityName(parameters: Parameters)
// MARK: Url
static let baseURLString = "http://api.openweathermap.org"
static let apiKey = "MY_APY_KEY"
static let pathApiKey = "&APPID=\(apiKey)"
var method: HTTPMethod {
switch self {
case .byCityName:
return .get
}
}
var path: String {
switch self {
case .byCityName:
return "/data/2.5/weather"
}
}
// MARK: URLRequestConvertible
func asURLRequest() throws -> URLRequest {
let url = try OWARouter.baseURLString.asURL()
var urlRequest = URLRequest(url: url.appendingPathComponent(path))
switch self {
case .byCityName(let parameters):
urlRequest = try URLEncoding.default.encode(urlRequest, with: parameters)
print((urlRequest.url)!)
}
urlRequest.httpMethod = method.rawValue
return urlRequest
}
}
When I log my (urlRequest.url)! I have this: http://api.openweathermap.org/data/2.5/weather?q=Rome but I cannot find a way to add the apiKey.
What am I doing wrong?
I've also made an ugly test adding this code after the print:
var urlRequest2 = URLRequest(url: (urlRequest.url)!.appendingPathComponent(OWARouter.pathApiKey))
print("URL2: \(urlRequest2)")
And the log is URL2: http://api.openweathermap.org/data/2.5/weather/&APPID=My_API_KEY?q=Rome
How come the api key is in the middle?
If you need this is the simple request code:
Alamofire.request(OWARouter.byCityName(parameters: ["q":"Rome"])).responseJSON { response in
print(response.request)
print(response.response)
print(response.data)
print(response.result)
debugPrint(response)
if let JSON = response.result.value {
print("json: \(JSON)")
}
}
Another question...
If I use as parameters ["q":"Rome, IT"], my output url is: http://api.openweathermap.org/data/2.5/weather?q=Rome%2CIT
How to keep the comma?
Thank you!
Swift - 5 Alamofire 5.0 Updated Code (just Change AF.request Method according to your requirement you can add Parameters headers and intercepter as well )
Alamofire.request(url, method: .get, encoding: JSONEncoding.default)
.responseJSON { response in
switch response.result {
case .success(let json):
print(json)
DispatchQueue.main.async {
// handle your code
}
case .failure(let error):
print(error)
}
}
Used below lines of code:
func getRequestAPICall(parameters_name: String) {
let todosEndpoint: String = "your_server_url" + "parameterName=\(parameters_name)"
Alamofire.request(todosEndpoint, method: .get, encoding: JSONEncoding.default)
.responseJSON { response in
debugPrint(response)
if let data = response.result.value{
// Response type-1
if (data as? [[String : AnyObject]]) != nil{
print("data_1: \(data)")
}
// Response type-2
if (data as? [String : AnyObject]) != nil{
print("data_2: \(data)")
}
}
}
}
func AlamofireGetCode()
{
var url:String!
url = "https://jsonplaceholder.typicode.com/comments"
Alamofire.request(url, method: .get, encoding: JSONEncoding.default)
.responseJSON { response in
switch response.result{
case .success(let json):
print(json)
DispatchQueue.main.async {
print(json)
self.mainarray = json as? NSArray
print(self.mainarray as Any)
self.mytableviewreload.reloadData()
}
case .failure(let error):
print(error)
}
}
}
I've found a solution... the Api Key is simply a parameter to send to the request. So the code to change is not in the router but in the request function:
Alamofire.request(OWARouter.byCityName(parameters: ["q":"Rome","APPID":"MY_API_KEY"])).responseJSON { response in
print(response.request)
//print(response.response)
//print(response.data)
//print(response.result)
//debugPrint(response)
if let JSON = response.result.value {
print("json: \(JSON)")
}
}
EDIT: the comma issue do not gives me any problem now. Thank you.
Swift 5+
Use AF.request
let todosEndpoint: String = "https://jsonplaceholder.typicode.com/todos/1"
let request = AF.request(todosEndpoint)
request.responseJSON { (data) in
print("Response", data)
}
**//
Fist in third party liabrary, install pod 'Alamofire'
Using Alamofire get json data
import UIKit
import Alamofire
class APIWRAPPER: NSObject {
static let instance = APIWRAPPER()
func LoginAPI(Uname : String , Password : String) {
let requestString =
"http://************php/v1/sign-in"
let params = ["user_name": Uname,
"password": Password]
Alamofire.request(requestString,method: .get, parameters: params, encoding: JSONEncoding.prettyPrinted, headers: [:]).responseJSON { (response:DataResponse<Any>) in
switch(response.result) {
case .success(_):
if response.result.value != nil
{
print("response : \(response.result.value!)")
}
else
{
print("Error")
}
break
case .failure(_):
print("Failure : \(response.result.error!)")
break
}
}
}
}

How to get the result value of Alamofire.request().responseJSON in swift 2?

I have a question about the new version of Alamofire for Swift 2
Alamofire.request(.POST, urlString, parameters: parameters as? [String : AnyObject])
.responseJSON { (request, response, result) -> Void in
let dico = result as? NSDictionary
for (index, value) in dico! {
print("index : \(index) value : \(value)")
}
}
In this section I would like to cast the result in to a NSDictionary. But When I compile and put a breakpoint, the debugger says that dico is nil. If I use debugDescription to print result, it is not nil and contains what I expected
How can I cast the Result variable?
The accepted answer works great but with the introduction of Alamofire 3.0.0 there are some breaking changes that affects this implementation.
The migration guide has further explanations but i will highlight the ones related to the actual solution.
Response
All response serializers (with the exception of response) return a generic Response struct.
Response type
The Result type has been redesigned to be a double generic type that does not store the NSData? in the .Failure case.
Also take in count that Alamofire treats any completed request to be successful, regardless of the content of the response. So you need to chain a .validate() before .responseJSON() to hit the .Failure case.
Read more about it here.
Updated code:
let url = "http://api.myawesomeapp.com"
Alamofire.request(.GET, url).validate().responseJSON { response in
switch response.result {
case .Success(let data):
let json = JSON(data)
let name = json["name"].stringValue
print(name)
case .Failure(let error):
print("Request failed with error: \(error)")
}
}
For reference:
Xcode 7.3 (Swift 2.2)
Alamofire 3.3.1
SwiftyJSON 2.3.3
If you don't mind using SwiftyJSON library, here's a working example in Xcode 7 Beta 5 + Alamofire 2.0.0-beta.1 + SwiftyJSON (xcode7 branch)
Alamofire.request(.GET, url, parameters: params, encoding: ParameterEncoding.URL).responseJSON { (_, _, result) in
switch result {
case .Success(let data):
let json = JSON(data)
let name = json["name"].string
case .Failure(_, let error):
print("Request failed with error: \(error)")
}
}
Edit:
Updated SwiftyJSON git page
You can now achieve most of the required behaviour out of the box without the need for SwiftyJSON for example. My OAuthTokenResponse is a simple struct that is Codable. The Alamofire library 5.2.2 lets you respond using the 'responseDecodable'
If you have say a struct that looks like this:
struct OAuthTokenResponse : Codable
{
var access_token:String?
var token_type:String?
var expires_in:Int?
var scope:String?
}
And then in your network call (using Alamofire)
let request = AF.request(identityUrl, method: .post, parameters: parameters, encoding: URLEncoding.httpBody)
request
.validate()
.responseDecodable { (response:AFDataResponse<OAuthTokenResponse>) in
switch response.result {
case .success(let data):
do {
let jwt = try decode(jwt: data.access_token!) // example
self.connected = true
print(jwt)
} catch {
print(error.localizedDescription)
self.connected = false
}
case .failure(let error):
self.connected = false
print(error.localizedDescription)
}
}
In the above code, the success case automatically deserialises your JSON using the decodable protocol into your struct. Any errors will result in the error case being hit instead.

Resources