POST Request gives timeout when testing on iPhone device - ios

I'm working on a school project, which is developing a Web application with an API and an iPhone app with swift. I developed the website in Laravel with Homestead/Vagrant.
But when it comes to the iPhone app, i'm facing with a problem:
When a user presses the login button the app sends a http POST request to the API and the API checks if the users exists in the db, if so then it will return a token, after which the user will be redirected to the dashboard. This works fine on the xcode simulator.
My code of the request part looks like this:
var loginUrl = "http://10.1.1.33/api/login" + "?email=" + self.email + "&password=" + self.password
let request = NSMutableURLRequest(URL: NSURL(string: loginUrl)!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
let task = session.dataTaskWithRequest(request, completionHandler: {
data, response, error -> Void in
guard error == nil else {
print("First Error!!! \(error)")
self.errorLabel.text = "Request timed Out"
self.enableLoginBtn(true, buttonText: "Login")
return
}
do {
let data: AnyObject? = try NSJSONSerialization.JSONObjectWithData(data!, options: [])
// Okay, the `json` is here, let's get the value for 'success' out of it
let json = JSON(data!)
dispatch_async(dispatch_get_main_queue(), {
if json["token"] != nil {
print("Login successvol!)")
if self.saveSession(json["token"].string!) {
self.enableLoginBtn(true, buttonText: "Login")
self.goToDashboard()
}else {
print("Saving token went wrong")
}
}else {
self.showError("Gebruikersnaam of wachtwoord is onjuist")
self.enableLoginBtn(true, buttonText: "Login")
}
})
}catch let parseError{
print(parseError)
let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("Error could not parse JSON: \(jsonStr)")
self.enableLoginBtn(true, buttonText: "Login")
}
})
task.resume()
But when i try to test the exact same thing on my iPhone 5s (I have a developer license) it's failing every single time. The error i'm getting is:
Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo=0x1742e1e80 {NSUnderlyingError=0x170259230 "The operation
couldn’t be completed. (kCFErrorDomainCFNetwork error -1001.)",
NSErrorFailingURLStringKey=http://10.1.1.33/api/login?email=jejz#kdk.nl&password=sndxbdj,
NSErrorFailingURLKey=http://10.1.1.33/api/login?email=jejz#kdk.nl&password=sndxbdj,
NSLocalizedDescription=The request timed out.}
The last couple days i searched the whole internet for a solution, but i couldn't find any solution.
I'd be really grateful if someone can help me out with this.
Thanks in advance.
Best regards
EDITED ---------------------
Screenshot of the error

I think you might be confused about POST and GET requests.
POST parameters are sent in the Request body, while in the GET request the parameters are sent in the URL.
GET Method parameters example:
/test/demo_form.asp?name1=value1&name2=value2
POST Method parameters example:
POST /test/demo_form.asp HTTP/1.1
Host: w3schools.com
name1=value1&name2=value2
So what you are trying to do, is send the parameters in the URL with the POST request, which is why it doesn't work!
You need to put the parameters in the Request body!
What you need to do is:
1. Change the request URL
var loginUrl = "http://10.1.1.33/api/login"
2. Add the parameters in the Request body
let postString = "email=" + self.email + "&password=" + self.password
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);

If you are using iOS 9, this is due to the new Transport security setting that is enabled by default. It blocks all outgoing non https requests. There is a way to turn it off for all requests, so any http request will load. There is also a way to control each url or url wildcard that can load on http.
If you are using http for your login, I suggest you change your server to support SSL and TLS 2.0 (pretty much the standard on most hosts with https) rather than turning it off to load http. Usernames and passwords shouldnt go over http.
If however you need to turn it off for furthering the development until you get a certificate setup, here is how to do it.
Add this to your info.plist file (open it in source code mode, control click on the file and select)
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
If you want to control specific urls, you can use <key>NSExceptionDomains</key>

Related

HTTP DELETE Works From Browser But Not From Postman or IOS App

When attempting an http request to my rest api, I continually get a 401 error when using the following code. I don not get this error making any other type of request. I have provided the function that makes the request below.
func deleteEvent(id: Int){
eventUrl.append(String(id))
let request = NSMutableURLRequest(url: NSURL(string: eventUrl)! as URL)
request.httpMethod = "DELETE"
print(eventUrl)
eventUrl.removeLast()
print(self.token!)
request.allHTTPHeaderFields = ["Authorization": "Token \(self.token)"]
let task = URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
if error != nil {
print("error=\(String(describing: error))")
//put variable that triggers error try again view here
return
}
print("response = \(String(describing: response))")
}
task.resume()
}
When sending the delete request with postman, the rest api just returns the data I want to delete but does not delete it. For reference I have posted the view and permissions classes associated with this request Any help understanding why this may be resulting in an error is greatly appreciated!
Views.py
class UserProfileFeedViewSet(viewsets.ModelViewSet):
"""Handles creating, reading and updating profile feed items"""
authentication_classes = (TokenAuthentication,)
serializer_class = serializers.ProfileFeedItemSerializer
queryset = models.ProfileFeedItem.objects.all()
permission_classes = (permissions.UpdateOwnStatus, IsAuthenticated)
def perform_create(self, serializer):
"""Sets the user profile to the logged in user"""
#
serializer.save(user_profile=self.request.user)
Permissions.py
class UpdateOwnStatus(permissions.BasePermission):
"""Allow users to update their own status"""
def has_object_permission(self, request, view, obj):
"""Check the user is trying to update their own status"""
if request.method in permissions.SAFE_METHODS:
return True
return obj.user_profile.id == request.user.id
HEADER SENT WITH DELETE REQUEST VIA POSTMAN
Preface: You leave out too much relevant information from the question for it to be properly answered. Your Swift code looks, and please don't be offended, a bit beginner-ish or as if it had been migrated from Objective-C without much experience.
I don't know why POSTMAN fails, but I see some red flags in the Swift code you might want to look into to figure out why your iOS app fails.
I first noticed that eventUrl seems to be a String property of the type that contains the deleteEvent function. You mutate it by appending the event id, construct a URL from it (weirdly, see below), then mutate it back again. While this in itself is not necessarily wrong, it might open the doors for racing conditions depending how your app works overall.
More importantly: Does your eventUrl end in a "/"? I assume your DELETE endpoint is of the form https://somedomain.com/some/path/<id>, right? Now if eventUrl just contains https://somedomain.com/some/path your code constructs https://somedomain.com/some/path<id>. The last dash is missing, which definitely throws your backend off (how I cannot say, as that depends how the path is resolved in your server app).
It's hard to say what else is going from from the iOS app, but other than this potential pitfall I'd really recommend using proper Swift types where possible. Here's a cleaned up version of your method, hopefully that helps you a bit when debugging:
func deleteEvent(id: Int) {
guard let baseUrl = URL(string: eventUrl), let token = token else {
// add more error handling code here and/or put a breakpoint here to inspect
print("Could not create proper eventUrl or token is nil!")
return
}
let deletionUrl = baseUrl.appendingPathComponent("\(id)")
print("Deletion URL with appended id: \(deletionUrl.absoluteString)")
var request = URLRequest(url: deletionUrl)
request.httpMethod = "DELETE"
print(token) // ensure this is correct
request.allHTTPHeaderFields = ["Authorization": "Token \(token)"]
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
print("Encountered network error: \(error)")
return
}
if let httpResponse = response as? HTTPURLResponse {
// this is basically also debugging code
print("Endpoint responded with status: \(httpResponse.statusCode)")
print(" with headers:\n\(httpResponse.allHeaderFields)")
}
// Debug output of the data:
if let data = data {
let payloadAsSimpleString = String(data: data, encoding: .utf8) ?? "(can't parse payload)"
print("Response contains payload\n\(payloadAsSimpleString)")
}
}
task.resume()
}
This is obviously still limited in terms of error handling, etc., but a little more swifty and contains more console output that will hopefully be helpful.
The last important thing is that you have to ensure iOS does not simply block your request due to Apple Transport Security: Make sure your plist has the expected entries if needed (see also here for a quick intro).

How to get a MS Translator access token from Swift 3?

I am trying to work with a MS Translator API from Swift 3 (right now playing in playgrounds, but the target platform is iOS). However, I got stuck when I was trying to get an access token for OAuth2. I have following code (I tried to port the code from example at Obtaining an access token):
let clientId = "id".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let clientSecret = "secret".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let scope = "http://api.microsofttranslator.com".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let translatorAccessURI = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13"
let requestDetails = "grant_type=client_credentials&client_id=\(clientId)&client_secret=\(clientSecret)&scope=\(scope)"
let postData = requestDetails.data(using: .ascii)!
let postLength = postData.count
var request = URLRequest(url: URL(string: translatorAccessURI)!)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("\(postLength)", forHTTPHeaderField: "Content-Length")
request.httpBody = postData
URLSession.shared.dataTask(with: webRequest) { (returnedData, response, error) in
let data = String(data: returnedData!, encoding: .ascii)
print(data)
print("**************")
print(response)
print("**************")
print(error)
}.resume()
Of course, I used a valid clientId and a valid clientSecret.
Now the callback prints following information. First, the returnedData contain a message that the request was invalid, along with a following message:
"ACS90004: The request is not properly formatted."
Second, the response comes with a 400 code (which fits the fact that the request is not properly formatted).
Third, the error is nil.
Now I was testing the call using Postman, and when I used the same URI, and put the requestDetails string as a raw body message (I added the Content-Type header manually), I got the same response. However, when I changed the body type in Postman UI to application/x-www-form-urlencoded and typed in the request details as key value pairs through its UI, the call succeeded. Now it seems that I am doing something wrong with the message formatting, or maybe even something bad with the Swift URLRequest/URLSession API, however, I cannot get a hold on to what. Can somebody help me out, please? Thanks.
OK, so after some more desperate googling and experimenting I have found my error. For the future generations:
The problem resided in encoding the parameters in the body of the PUT http request. Instead of:
let scope = "http://api.microsofttranslator.com"
.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
I have to use the following:
let scope = "http://api.microsofttranslator.com"
.addingPercentEncoding(withAllowedCharacters:
CharacterSet(charactersIn: ";/?:#&=$+{}<>,").inverted)!
Seems that the API (or the HTTP protocol, I am not an expert in this) have problems with / and : characters in the request body. I have to give credit to Studiosus' answer on Polyglot issue report.

Error Code=-1005 "The network connection was lost." in Swift while consuming Web Service

I'm working on a iOS project in Swift 2.0, which has Web service calls, these services are slow to respond and that is normal, can be up to 1 minute or a little more, when i call the service 70% of the time it answers with the error "the network connection was lost." The tests were conducted in both simulator and different phone devices and iPad and the result is the same. The network connection is strong and the same application was also created on Android and working properly almost 100% of the time.
The way I call services from any view is as follows:
#IBAction func contratarAct(sender: AnyObject) {
conexion.delegate = self
loadingView = MEXLoadingView(delegate: self, title: "Espere por favor", percent: false, view: self.view)
self.loadingView.showAnimated(true)
let url = urlServicios.urlBaseServicios + "/" + idSolicitud + "/" + idNoCliente + "/CONTRATO"
conexion.consultaServicioGET(url, httpMethod: "PUT")
}
And the method that is executed is as follows:
func consultaServicioGET(url : String, httpMethod : String ){
let urlString = url
let request = NSMutableURLRequest(URL: NSURL(string: urlString)!)
request.timeoutInterval = 540
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
var session = NSURLSession.sharedSession()
request.HTTPMethod = httpMethod
let urlconfig = NSURLSessionConfiguration.defaultSessionConfiguration()
urlconfig.timeoutIntervalForRequest = 540
urlconfig.timeoutIntervalForResource = 540
session = NSURLSession(configuration: urlconfig, delegate: self, delegateQueue: nil)
let task = session.dataTaskWithRequest(request , completionHandler: {
(data:NSData?, response:NSURLResponse?, error:NSError?) in
if error != nil {
let jsonError : NSDictionary = NSDictionary()
self.delegate?.respuestaServicioGET!(jsonError, mensaje: "\(error!.localizedDescription)")
return
}
let jsonString = NSString(data: data!,encoding: NSASCIIStringEncoding)
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
let json: NSDictionary = try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
if (json.isKindOfClass(NSDictionary) ){
self.delegate?.respuestaServicioGET!(json, mensaje: "OK")
}else{
let jsonError : NSDictionary = NSDictionary()
self.delegate?.respuestaServicioGET!(jsonError, mensaje: "ERROR")
}
})
task.resume()
}
the error displayed is:
error=Optional(Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost." UserInfo={NSUnderlyingError=0x7fbde5f51df0 {Error Domain=kCFErrorDomainCFNetwork Code=-1005 "(null)" UserInfo={_kCFStreamErrorCodeKey=-4, _kCFStreamErrorDomainKey=4}}, NSErrorFailingURLStringKey=https://particulares-gw-obparticularesmx-pre.appls.cto2.paas.gsnetcloud.com:443/OPB/57dadf7de4b0ac2e518de44a/57dadf7de4b06c6b04ef0dcf/CONTRATO, NSErrorFailingURLKey=https://particulares-gw-obparticularesmx-pre.appls.cto2.paas.gsnetcloud.com:443/OPB/57dadf7de4b0ac2e518de44a/57dadf7de4b06c6b04ef0dcf/CONTRATO, _kCFStreamErrorDomainKey=4, _kCFStreamErrorCodeKey=-4, NSLocalizedDescription=The network connection was lost.})
I add some code like the following:
urlconfig.timeoutIntervalForRequest = 540
urlconfig.timeoutIntervalForResource = 540
Trying to get more "timeout" but this is not looks like a timeout.
I can not get out of this error for days, any help will be greatly appreciated. I'm desperate.
If you're expecting a socket to stay open for minutes at a time, you're in for a world of hurt. That might work on Wi-Fi, but on cellular, there's a high probability of the connection glitching because of tower switching or some other random event outside your control. When that happens, the connection drops, and there's really nothing your app can do about it.
This really needs to be fixed by changing the way the client requests data so that the responses can be more asynchronous. Specifically:
Make your request.
On the server side, immediately provide the client with a unique identifier for that request and close the connection.
Next, on the client side, periodically ask the server for its status.
If the connection times out, ask again.
If the server says that the results are not ready, wait a few seconds and ask again.
On the server side, when processing is completed, store the results along with the identifier in a persistent fashion (e.g. in a file or database)
When the client requests the results for that identifier, return the results if they are ready, or return a "not ready" error of some sort.
Have a periodic cron job or similar on the server side to clean up old data that has not yet been collected.
With that model, it doesn't matter if the connection to the server closes, because a subsequent request will get the data successfully.
I faced the same issue and I am attaching a screenshot of the resolution to show how I resolved the issue.
In my case, the issue was that the API requests are blocked from the server Sucuri/Cloudproxy (Or you can say firewall service). Disabling the firewall resolved the issue
I don't why but it's works when I add sleep before my request:
sleep(10000)
AF.request(ViewController.URL_SYSTEM+"/rest,get_profile", method: .post, parameters: params, encoding: JSONEncoding.default , headers: headers).responseJSON { (response) in
}
I faced this issue and spend more than 1 week to fix this. AND i just solved this issue by changing Wifi connection.

Twilio Send SMS in swift

I'm trying to make an app that when you press a button it sends a text message to a preset number, and I am using Twilio for the SMS messages. The issue is that I am using Swift for my app, and they have no examples in swift currently. This is my code for sending the message:
func sendSMS()
{
let twilioSID = "AC11ed62fdb971a8f56d9be531a5ce40c2"
let twilioSecret = "mySecretID"
let fromNumber = "number"
let toNumber = "number"
let message = "This is a test message"
// Build the request
let request = NSMutableURLRequest(URL: NSURL(string:"https://\(twilioSID):\(twilioSecret)#api.twilio.com/2010-04-01/Accounts/\(twilioSID)/SMS/Messages")!)
request.HTTPMethod = "POST"
request.HTTPBody = "From=\(fromNumber)&To=\(toNumber)&Body=\(message)".dataUsingEncoding(NSUTF8StringEncoding)
// Build the completion block and send the request
NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: { (data, response, error) in
print("Finished")
if let data = data, responseDetails = NSString(data: data, encoding: NSUTF8StringEncoding) {
// Success
print("Response: \(responseDetails)")
} else {
// Failure
print("Error: \(error)")
}
}).resume()
}
But whenever I try and run that function, I get this
Finished
Response: <?xml version='1.0' encoding='UTF-8'?>
<TwilioResponse><RestException><Code>20003</Code><Detail>Your AccountSid or AuthToken was incorrect.</Detail><Message>Authentication Error - No credentials provided</Message><MoreInfo>https://www.twilio.com/docs/errors/20003</MoreInfo><Status>401</Status></RestException></TwilioResponse>
I know that my credentials are correct.....
Is there a better way of doing this? Does anyone have an example out there?
Matthew, as mentioned in the comments above/we do not recommend that you send SMS from the REST API via client side code for security reasons.
We suggest that you wrap your credentials and sending of the SMS in a backend app using one of the available helper libraries in the examples seen here:
https://www.twilio.com/docs/api/rest/sending-messages
A colleague of mine actually wrote a post to address this for the Android community and it looks like we should definitely do the same for Swift, which would be more my speed.
[Update]: How to send an SMS from Swift post.
Hope this helps.

How can I pull the grades on the website, and perform the login using a POST request?

I am trying to pull my school grades from the website which stores all my grades, but I am having trouble logging in using HTTP requests, and pulling the information of the next page. Any help is appreciated :)
override func viewDidLoad() {
super.viewDidLoad()
let myUrl = NSURL(string: "https://homeaccess.katyisd.org/HomeAccess/Account/LogOn?ReturnUrl=%2fhomeaccess%2f")
let request = NSMutableURLRequest(URL: myUrl!)
request.HTTPMethod = "POST"
let postString = "User_Name=**hidden**&Password=**hidden**"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){
data,response,error in
if(error != nil){
print("error=\(error)")
return
}
print("response = \(response)")
// Print out response body
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString)")
//Let’s convert response sent from a server side script to a NSDictionary object:
do{
var myJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves) as? NSDictionary
if let parseJSON = myJSON {
// Now we can access value of First Name by its key
var firstNameValue = parseJSON["User_Name"] as? String
print("firstNameValue: \(firstNameValue)")
}
}catch{
print(error)
}
}
}
First, you need task.resume() after defining the task in order to start the connection loading, otherwise the object will be created and nothing will actually happen.
According to this error you posted, there's an SSL verification error on the site you are trying to access. The most secure option is to fix the SSL on the site, but I presume that is beyond your control in this case. The easier fix is to bypass the SSL error by adding "App Transport Security Settings" > "Allow Arbitrary Loads" = YES in your info.plist, as #André suggested. Or, if you are only using the one domain, bypass the particular domain in the NSExceptionDomains. See this question for more info.
According to this error you posted, a JSON parsing error is occurring. It is currently being caught and printed by your catch block, so the data is not actually processed. In your case, this is occurring because the response from Home Access Center is HTML, not JSON, so the JSON parser is failing. You are probably looking for an HTML parser. Swift does not have one built-in; look at this question for some example open-source options.
I have actually created a program that interfaces with Home Access Center. Sadly, there is no public API available -- APIs typically return JSON, which is easier to work with. Instead, you will need to use an HTML parser to analyze the page that is meant for human users and cookies to fake that a human user is logging on.
add task.resume() at the end of your code. also add the following to your info.plist file:

Resources