the alamofire isn't doing a proper connection to the api - swift

i am trying to get a response that contain json from openweathermap api,
but from the debugger it seems that when he get to the code block of alamofire its just skips it
here is the block of code that i am using for this task
func printJson(){
Alamofire.request("https://api.openweathermap.org/data/2.5/find?appid=6ad1f143546b8c6d761ecbda6a34a005&q=yavne", method: .get).responseJSON { response in
if response.data != nil {
var json : JSON
do{
json = try JSON(data: response.data!)
print(json)
}catch{}
}
}
}

Use this helper class to make API calls.
class NetworkHandler {
static let shared = NetworkHandler()
func sendRequest(withUrl url:String,andBody body:[String:Any]? = [:],
completionHandler: #escaping ((_ status: Bool,_ response:Any) -> Void)) {
DispatchQueue.global(qos: .userInitiated).async {
Alamofire.request(url, method: .post, parameters: body,encoding: JSONEncoding.default, headers: nil).responseJSON {
response in
guard response.response?.statusCode == 200 else{
completionHandler(false,"Service Temporarily Unavailable.\nPlease check your internet connection or contact your Service provider.")
return
}
switch response.result {
case .success:
completionHandler(true,response.value!)
break
case .failure(let error):
completionHandler(false,error.localizedDescription)
}
}
}
}
}

Related

Alamofire - How to get API error from AFError

In my quest to implement Alamofire 5 correctly and handle custom error model responses, I have yet to find an accepted answer that has an example.
To be as thorough as possible, here is my apiclient
class APIClient {
static let sessionManager: Session = {
let configuration = URLSessionConfiguration.af.default
configuration.timeoutIntervalForRequest = 30
configuration.waitsForConnectivity = true
return Session(configuration: configuration, eventMonitors: [APILogger()])
}()
#discardableResult
private static func performRequest<T:Decodable>(route:APIRouter, decoder: JSONDecoder = JSONDecoder(), completion:#escaping (Result<T, AFError>)->Void) -> DataRequest {
return sessionManager.request(route)
// .validate(statusCode: 200..<300) // This will kill the server side error response...
.responseDecodable (decoder: decoder){ (response: DataResponse<T, AFError>) in
completion(response.result)
}
}
static func login(username: String, password: String, completion:#escaping (Result<User, AFError>)->Void) {
performRequest(route: APIRouter.login(username: username, password: password), completion: completion)
}
}
I am using it like this
APIClient.login(username: "", password: "") { result in
debugPrint(result)
switch result {
case .success(let user):
debugPrint("__________SUCCESS__________")
case .failure(let error):
debugPrint("__________FAILURE__________")
debugPrint(error.localizedDescription)
}
}
I have noticed that if I use .validate() that the calling function will receive a failure however the response data is missing. Looking around it was noted here and here to cast underlyingError but thats nil.
The server responds with a parsable error model that I need at the calling function level. It would be far more pleasant to deserialize the JSON at the apiclient level and return it back to the calling function as a failure.
{
"errorObject": {
"summary": "",
"details": [{
...
}]
}
}
UPDATE
Thanks to #GIJoeCodes comment I implemented this similar solution using the router.
class APIClient {
static let sessionManager: Session = {
let configuration = URLSessionConfiguration.af.default
configuration.timeoutIntervalForRequest = 30
configuration.waitsForConnectivity = true
return Session(configuration: configuration, eventMonitors: [APILogger()])
}()
#discardableResult
private static func performRequest<T:Decodable>(route:APIRouter, decoder: JSONDecoder = JSONDecoder(), completion:#escaping (_ response: T?, _ error: Error?)->Void) {
sessionManager.request(route)
.validate(statusCode: 200..<300) // This will kill the server side error response...
.validate(contentType: ["application/json"])
.responseJSON { response in
guard let data = response.data else { return }
do {
switch response.result {
case .success:
let object = try decoder.decode(T.self, from: data)
completion(object, nil)
case .failure:
let error = try decoder.decode(ErrorWrapper.self, from: data)
completion(nil, error.error)
}
} catch {
debugPrint(error)
}
}
}
// MARK: - Authentication
static func login(username: String, password: String, completion:#escaping (_ response: User?, _ error: Error?)->Void) {
performRequest(route: APIRouter.login(username: username, password: password), completion: completion)
}
}
Called like this
APIClient.login(username: "", password: "") { (user, error) in
if let error = error {
debugPrint("__________FAILURE__________")
debugPrint(error)
return
}
if let user = user {
debugPrint("__________SUCCESS__________")
debugPrint(user)
}
}
This is how I get the errors and customize my error messages. In the validation, I get the errors outside of the 200..<300 response:
AF.request(
url,
method: .post,
parameters: json,
encoder: JSONParameterEncoder.prettyPrinted,
headers: headers
).validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.responseJSON { response in
switch response.result {
case .success(let result):
let json = JSON(result)
onSuccess()
case .failure(let error):
guard let data = response.data else { return }
do {
let json = try JSON(data: data)
let message = json["message"]
onError(message.rawValue as! String)
} catch {
print(error)
}
onError(error.localizedDescription)
}
debugPrint(response)
}
First, there's no need to use responseJSON if you already have a Decodable model. You're doing unnecessary work by decoding the response data multiple times. Use responseDecodable and provide your Decodable type, in this case your generic T. responseDecodable(of: T).
Second, wrapping your expected Decodable types in an enum is a typical approach to solving this problem. For instance:
enum APIResponse<T: Decodable> {
case success(T)
case failure(APIError)
}
Then implement APIResponse's Decodable to try to parse either the successful type or APIError (there are a lot of examples of this). You can then parse your response using responseDecodable(of: APIResponse<T>.self).

Alamofire and downloading images from server

I need to download image as data from the URL and recreate it to UIImage(data:). The problem is, that Alamofire downloads only a small amount of image data on first request:
after I make new call to my API, whole image is downloaded:
It really doesn't make any sense to me why it could be like that. The first request always fails to download all the data. This is the code what I'm using to download the data from URL:
func requestWithData(
_ url: String,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
decoder: JSONDecoder = JSONDecoder(),
headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil
) -> Future<Data, ServerError> {
return Future({ promise in
AF.request(
url,
method: method,
parameters: parameters,
encoding: JSONEncoding.default,
headers: headers,
interceptor: interceptor ?? self
)
.validate(statusCode: [200, 201, 204, 400, 401])
.responseData(completionHandler: { (response) in
switch response.result {
case .success(let value):
promise(.success(value))
case .failure(let error):
promise(.failure(self.createError(response: response.response, error: error, data: response.data)))
}
})
})
}
func getLocationImage(location: Location) -> Future<Void, ServerError> {
Future { promise in
self.networkManager.requestWithData(Endpoint.locationBadge(locationId: location.id, uuid: location.uuid ?? "").url)
.sink { completion in
if case .failure(let error) = completion {
promise(.failure(error))
}
} receiveValue: { [unowned self] imageData in
self.updateLocationImage(location: location, image: imageData)
.sink { completion in
if case .failure(let error) = completion {
promise(.failure(.init(message: "Error while saving the data", code: .coreData, args: [error.localizedDescription])))
}
} receiveValue: { _ in
promise(.success(()))
}
.store(in: &subscription)
}
.store(in: &self.subscription)
}
}
SOLVED: The problem was with Alamofire request, I was able to resolve the issue with using AlamofireImage framework.

API working in Postman but giving error in Code

I am trying to call API in postman and its working fine, But If I am trying to call API in swift Alamofire, Its give me error-
My Code is-
func sendToServer(){
let urlString = "https://xxxxxxxxxx/TempService/SaveBarCodes"
let data: Parameters = ["SerialNo": "T8180399","Status":101]
Alamofire.request(urlString, method: .post, parameters: data,encoding: JSONEncoding.default, headers: nil).responseJSON {
response in
switch response.result {
case .success:
print(response)
break
case .failure(let error):
print(error)
}
}
}
Error is-
The JSON value could not be converted to System.Collections.Generic.List`1[BreakBulkModels.Model.WebapiModels.DtoInventoryApi]. Path: $ | LineNumber: 0 | BytePositionInLine: 1.
Your API accepts as parameters an array of JSON objects but you are currently sending a JSON object:
{
"SerialNo": "T8180399",
"Status": 101
}
Because Parameters is a typealias to Dictionary<String, Any> (what you need is Array<Dictionary<String, Any>>) you have to do your parameter encoding yourself and then call request(_:) function of Alamofire passing your URLRequest:
do {
let urlString = "https://xxxxxxxxxx/TempService/SaveBarCodes"
let url = try urlString.asURL()
var request = URLRequest(url: url)
let data = [["SerialNo": "T8180399", "Status": 101]]
request = try JSONEncoding.default.encode(request, withJSONObject: data)
Alamofire.request(request).responseJSON { response in
switch response.result {
case .success:
print(response)
break
case .failure(let error):
print(error)
}
}
} catch {
print(error)
}
Edit: With Alamofire v5 there is a more elegant way by using Encodable protocol:
struct BarCode: Encodable {
var SerialNo: String
var Status: Int
}
func sendToServer(){
let urlString = "https://xxxxxxxxxx/TempService/SaveBarCodes"
let data = [BarCode(SerialNo: "T8180399", Status: 101)]
AF.request(
urlString,
method: .post,
parameters: data,
encoder: JSONParameterEncoder.default
).responseJSON { response in
switch response.result {
case .success:
print(response)
break
case .failure(let error):
print(error)
}
}
}

How do I trace the response of this call in Swift Xcode?

Data in the below call is nil so the call is failing:
postRequest("/api/users/signup", params: params, headers: nil) { data in
guard data != nil else {
return completionHandler(nil, UsersStoreError.CannotSignup)
This is the postRequest function:
private func postRequest(_ url: String, params: Parameters?, headers: HTTPHeaders?, completion: #escaping([String:Any]?) -> Void) {
let enc = JSONEncoding.default
let url = AppConstants.ENDPOINT + url
Alamofire
.request(url, method: .post, parameters:params, encoding:enc, headers:headers)
.validate()
.responseJSON { response in
switch (response.result) {
case .success(let data): completion((data as! [String:Any]))
case .failure(_): completion(nil)
}
}
}
How can I trace the response of this call?
Set a breakpoint in the responseJSON block in your postRequest method and inspect/print the response parameter. That should give you information about the request and the response.
It's possible that the response isn't valid JSON. To check what you're actually getting back, add this to responseJSON (before your switch):
debugPrint(String(data: response.data!, encoding: .utf8))

Custom made Creditcard Expiry Date field

I'm trying to make a POST API request using Alamofire 4.0 and Swift 3.0 but I'm having a tough time with it.
My Postman looks like this:
And using Basic Auth
I can't even get my code to compile for now and I've no idea why. This is my function:
func testCall(token:String, completionHandler: #escaping ((AnyObject?, Error?) -> Void)) {
let urlString = Constant.apiUrlString + Constant.apiPostOrderCard
print(urlString)
let parameters = ["test" : "test"]
Alamofire.request(urlString, method: .post, parameters: parameters, encoding: .JSON, headers: self.header).validate().responseJSON { (request, response, result) -> Void in
switch result {
case .Success(let value):
print(value)
completionHandler(value, nil);
break
case .Failure(let data, let error):
self.handleFailure(data, error: error, response: response, completionHandler: completionHandler)
break
}
}
What am I doing wrong?
Did you just migrate to Alamofire 4? There are couple of syntax changes, you need to read the migration guide when there is an version update.
Here is the updated code :)
func testCall(token:String, completionHandler: #escaping ((AnyObject?, Error?) -> Void)) {
let urlString = Constant.apiUrlString + Constant.apiPostOrderCard
let parameters : Parameters = ["test": "test"]
Alamofire.request(urlString, method: .post, parameters: parameters).validate().responseJSON { response in
if let status = response.response?.statusCode {
switch(status){
case 200:
if let result = response.result.value {
let JSON = result as! NSDictionary
// implement your logic here
completionHandler(value, nil);
}
break
case 400:
// implement your logic here
self.handleFailure(data, error: error, response: response, completionHandler: completionHandler)
break
default:
break
}
}
}
}
the Parameters is Alamofire's object, feel free to use its and the constructor had changed also.
Just replace your encoding: .JSON to encoding: JSONEncoding.default and ensure parameters has type Parameters:
Alamofire.request(urlString, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: self.header).validate().responseJSON { response in
switch response.result {
case .success(let json): //do something
case .failure(let error): // do something
}
}