I use this code.
var apiPath : String = "/api/list/"
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.timeoutIntervalForRequest = 60
let manager = Alamofire.Manager(configuration: configuration)
manager.session.configuration.HTTPAdditionalHeaders = ["_token" : self._token]
manager.request(.GET, self._host + self._url + apiPath, parameters: nil)
.responseSwiftyJSON ({ (request, response, resultJson, error) in
if (resultJson["Success"]) {
//get list success
} else {
println("request : \(request)")
println("response : \(response)")
println("resultJson : \(resultJson)")
println("error : \(error)")
}
})
And I got some problem
Alamofire version 1.2.1 : No Problem
Alamofire version 1.2.2 & 1.2.3 :
request : { URL: https://test.com/api/list/ }
response : nil
resultJson : null
error : Optional(Error Domain=NSURLErrorDomain Code=-999 "cancelled"
UserInfo=0x7feb92c434f0 {NSErrorFailingURLKey=https
://test.com/api/list/, NSLocalizedDescription=cancelled,
NSErrorFailingURLStringKey=http s://test.com/api/list/})
why response was nil and resultJson was null on version 1.2.2 and 1.2.3
Please help me what problem in this code..
I just encountered the same problem as you today after updating Alamofire from 1.2.1 to 1.2.3.
I discovered by adding "manager.session.invalidateAndCancel()" at the end and inside the responseJSON block fixed this issue. But what I cannot get my head around is that how can this line of code INSIDE the responseJSON block affects the responseJSON results.
Anyway I will just run with this fix until the Alamofire team fixes it or someone explains to me why this is happening.
I noticed that your API endpoint indicates to a secure connection:
httpS://test.com/api/list/
Just try it just in case, maybe it repeats your situation.
In my case, this was a typo in the API manager code. Which from the part can be said is connected with App Transport Security Settings.
Just changed the protected protocol from httpS:// to http:// and the error:
NSURLErrorDomain Code = -999 "cancelled"
was gone and it all worked!
+And also if you had a similar problem. Be sure to discuss this with a backend specialist who deals with the server or API configuration for your application. This means that the server does not have valid security certificates. Perhaps you still need a secure connection. Or this specialist can again configure everything back from http:// to httpS://, and I'm not sure (did not check) whether this will work again when in the code you are already using a non-secure http:// connection.
Related
I'm creating a login page, and I used to handle API error such as:
{"status":"error","answer":"Password is wrong"}
this way:
if error?["status"] as? String == "error" {
Alert.showBasic(title: error!["status"] as! String, message: error!["answer"] as! String, vc: self)
This worked perfectly. But now I'm working with another API, that responds to my request this way:
{"password":["password is wrong"]}
The confusion what I have, is that I don't get from API answer, a status for my request, like success or error.
Is there a way to handle it?
The error != nil depends and what you feed to your model and how you feed depends on status code
if let httpResponse = response as? HTTPURLResponse{
switch httpResponse.statusCode {
case 200 : //handle Root data model
case 401 : //handle wrong password model
}
}
URLSession error is different . Its when the request is failed and no response . But you got the response but its up to you differentiate whether its good or bad.
Server response should be static if there is a change not noticed to you means something wrong in the backend.
Using Alamofire 4/Swift 3 how can you differentiate between a request that fails due to:
Network connectivity (host down, can't reach host) vs
Invalid server HTTP response code (ie: 499) which causes the Alamofire request to fail due to calling validate()?
Code:
sessionManager.request(url, method: .post, parameters:dict, encoding: JSONEncoding.default)
.validate() //Validate status code
.responseData { response in
if response.result.isFailure {
//??NETWORK ERROR OR INVALID SERVER RESPONSE??
}
}
We want to handle each case differently. In the latter case we want to interrogate the response. (In the former we don't as there is no response).
Here's our current working solution:
sessionManager.request(url, method: .post, parameters:dict, encoding: JSONEncoding.default)
.validate() //Validate status code
.responseData { response in
if response.result.isFailure {
if let error = response.result.error as? AFError, error.responseCode == 499 {
//INVALID SESSION RESPONSE
} else {
//NETWORK FAILURE
}
}
}
If result.error it is of type AFError you can use responseCode. From the AFError source comments:
/// Returns whether the `AFError` is a response validation error. When `true`, the `acceptableContentTypes`,
/// `responseContentType`, and `responseCode` properties will contain the associated values.
public var isResponseValidationError: Bool {
if case .responseValidationFailed = self { return true }
return false
}
Maybe there is a better way (?) but that seems to work...
Alamofire can tell you about the status of the request,
this code works just fine for me:
if let error = response.result.error as? NSError {
print(error)//print the error description
if (error.code == -1009){
print(error.code) // this will print -1009,somehow it means , there is no internet connection
self.errorCode = error.code
}
//check for other error.code
}else{
//there is no problem
}
the error.code will tell you what is the problem
Automatic validation should consider status code within 200...299 (success codes) range so when you get an invalid server HTTP response code 5xx (499 means Client Closed Request) you are sure it's not depend by validation.
About the statusCode, my advice is to follow the correct new rules to get it. If you have some problem to retrieve it look this SO answer.
Speaking about network reachability you could write:
let manager = NetworkReachabilityManager(host: "www.apple.com")
manager?.listener = { status in
print("Network Status Changed: \(status)")
}
manager?.startListening()
There are some important things to remember when using network reachability to determine what to do next.
Do NOT use Reachability to determine if a network request should be
sent. You should ALWAYS send it.
When Reachability is restored, use the event to retry failed network
requests. Even though the network requests may still fail, this is a
good moment to retry them.
The network reachability status can be useful for determining why a
network request may have failed. If a network request fails, it is
more useful to tell the user that the network request failed due to
being offline rather than a more technical error, such as "request
timed out."
You can find these details also in the official Alamofire 4 GitHUB page
I am using Alamofire in swift to send http request/post to server. Below is the code I used in swift.
Alamofire.request(.POST, "http://localhost:8080/hello", headers: [ACCESS_TOKEN:token, "Content-Type":"application/x-www-form-urlencoded" ],
parameters:["friend_id" : friendId, "skill_id" : skillId]).response(completionHandler: {
(request, response, data, error) in
print(request)
print(response)
print(data)
print(error)
})
Below is the code defined in server side:
#POST
#Path("/hello")
#Produces(MediaType.APPLICATION_JSON)
public Response nominateSkill(#Context HttpServletRequest request, #FormParam("friend_id") long friendId, #FormParam("skill_id") int skillId) {
// ...
}
When I run the swift code, I always got below error message in server side:
A servlet request to the URI http://localhost:8080/hello contains form parameters in the request body but the request body has been consumed by the servlet or a servlet filter accessing the request parameters. Only resource methods using #FormParam will work as expected. Resource methods consuming the request body by other means will not work as expected.
I think the problem would be coursed by the swift code which didn't set the parameter correctly. But I don't know how to set them correctly?
I found the solution after some search. I need to add "encoding: .URL" on the request method like below:
Alamofire.request(.POST, "http://localhost:8080/hello", headers: [ACCESS_TOKEN:token, "Content-Type":"application/x-www-form-urlencoded" ],
parameters:["friend_id" : friendId, "skill_id" : skillId],
encoding: .URL).response(completionHandler: {
(request, response, data, error) in
print(request)
print(response)
print(data)
print(error)
})
Your swift code seems fine. Make sure on which side problem is occur. You can try https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop?hl=en to test the api and make sure api doen't have any issues.
You may also try to change data type of friend_id and skill_id to string in server side and run again.
I'm developing a service browser for iOS device and i'm facing some problems.
I've correctly discovered services by using NSNetServiceBrowser and resolved it but if i try to do a .GET request using Alamofire to the hostname of the NSNetService returned by discovery i get a cannot resolve name
i've used this code to do the request
var url : String = "http://\(self.servers[0].hostName):32400/library/sections"
Alamofire.request(.GET, url.lowercaseString,encoding: ParameterEncoding.URL).responsePropertyList { request, response, result in
if let array = response.result.value as? [[String: String]] {
print("bau")
}
}
self.servers[0].hostName returns a string like iMac-di-Tiz.local.
and opening the url in Chrome i get the page correctly.
Does anyone have tried it and made it work?
This my first project using swift. I am usin alamofire to connect the API. I have a local copy form the API I want to use for debugging - so I can set test data - because the remote API has already real data that I can't mess with.
The problem is I am getting the below error when I try to access https://localhost:8443/MyProject
Optional(Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “localhost” which could put your confidential information at risk." UserInfo=0x7fbeb8c61ff0 {NSURLErrorFailingURLPeerTrustErrorKey=, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorCodeKey=-9813, NSUnderlyingError=0x7fbeb8ea5c00 "The operation couldn’t be completed. (kCFErrorDomainCFNetwork error -1202.)", NSLocalizedDescription=The certificate for this server is invalid. You might be connecting to a server that is pretending to be “localhost” which could put your confidential information at risk., NSErrorFailingURLKey=https://localhost:8443/myproject/api/loginUser.pdo, NSErrorFailingURLStringKey=https://localhost:8443/myproject/api/loginUser.pdo, _kCFStreamErrorDomainKey=3})
I have found many solution most of them for Objective-c as using setAllowsAnyHTTPSCertificate or using the delegate for Connection.
but I could not find an equevelent method for setAllowsAnyHTTPSCertificate in swift, and I not sure how to set the delegate to the connection while using alamofire.
any ideas what I need to do?
I know that setAllowsAnyHTTPSCertificate is private api and will cause the project to be rejected by Apple. I want to use it only while debugging then it will be removed before the publishing the project.
Thank you in advance.
You can easily override the default challenge behavior in Alamofire using the SessionDelegate override closures. Here is an example of how you can allow Alamofire to accept invalid certificates:
IMPORTANT: Please do not use this in any production code. Security is VERY important and this implementation completely disregards the security mechanisms in Alamofire. Use at your own risk!
let manager = Alamofire.Manager.sharedInstance
manager.delegate.sessionDidReceiveChallenge = { session, challenge in
var disposition: NSURLSessionAuthChallengeDisposition = .PerformDefaultHandling
var credential: NSURLCredential?
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
disposition = NSURLSessionAuthChallengeDisposition.UseCredential
credential = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust)
} else {
if challenge.previousFailureCount > 0 {
disposition = .CancelAuthenticationChallenge
} else {
credential = manager.session.configuration.URLCredentialStorage?.defaultCredentialForProtectionSpace(challenge.protectionSpace)
if credential != nil {
disposition = .UseCredential
}
}
}
return (disposition, credential)
}
We (the Alamofire TC) are going to implement TLS pinning and several other features related to security in the Alamofire 1.3.0 release.
UPDATE
The Alamofire 1.3.0 release is out and adds MUCH better support for customizing server trust authentication challenges. For further info, please check out the Security section of the README.
Swift 3 version of #cnoon's code
manager.delegate.sessionDidReceiveChallenge = { session, challenge in
var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
var credential: URLCredential?
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust, let trust = challenge.protectionSpace.serverTrust {
disposition = URLSession.AuthChallengeDisposition.useCredential
credential = URLCredential(trust: trust)
} else {
if challenge.previousFailureCount > 0 {
disposition = .cancelAuthenticationChallenge
} else {
credential = self.manager.session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace)
if credential != nil {
disposition = .useCredential
}
}
}
return (disposition, credential)
}
Swift 3
In my case when I use swagger client library, I changed my code to test the local server like this:
open func createSessionManager() -> Alamofire.SessionManager {
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = buildHeaders()
let serverTrustPolicies: [String: ServerTrustPolicy] = ["localhost": .disableEvaluation]
return Alamofire.SessionManager(configuration: configuration, serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies))
}
Replacing with https to http of my base url. resolved the error.