NSURLSession: It is possible with no authentication NSURLSession ( Swift ) - swift

I would like with an API -Key and authenticate user . I have the message " Session URL Task Succeeded : HTTP 200 " get in the Console . Unfortunately, I always get the message of the page " {" message " : " . Authorization has been denied for this request " } " . I spent hours looking for and found a solution . I ask for your help . Here is my code :
let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil)
let URL = NSURL(string: "https://api.testhomepage.com/api/contact")
let request = NSMutableURLRequest(URL: URL!)
// Headers
request.addValue("ARRAffinity=259dfjiehfs315d7249df8805c7895a98c4cbd3327ffdcb82aaa16317f2e6", forHTTPHeaderField: "Cookie")
request.addValue("Basic dWVzQGp1bGl0ZafdgfvcfgeDcyZS1hZWEyLTQzYmVmMjFhNDMxZQ==", forHTTPHeaderField: "Authorization")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("api.testhomepage.com", forHTTPHeaderField: "Host")
request.addValue("//api.testhomepage.com/api/contact: POST", forHTTPHeaderField: "https")
request.HTTPMethod = "GET"
// JSON Body
/* Start a new Task */
let task = session.dataTaskWithRequest(request) { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
if (error == nil) {
// Success
let statusCode = (response as! NSHTTPURLResponse).statusCode
print("URL Session Task Succeeded: HTTP \(statusCode)")
let myURLString = "https://api.testhomepage.com/api/contact"
if let myURL = NSURL(string: myURLString) {
var error: NSError?
let myhtml = try! NSString(contentsOfURL: myURL, encoding: NSUTF8StringEncoding)
if let error = error {
print("Error : \(error)")
} else {
print("HTML : \(myhtml)")
}
} else {
print("Error: \(myURLString) doesn't seem to be a valid URL")
}
}
else {
// Failure
print("URL Session Task Failed: %#", error!.localizedDescription);
}
}
task.resume()
}
This is in the console:
URL Session Task Succeeded: HTTP 200
HTML : {"Message":"Authorization has been denied for this request."}

use request.setvalue("", forHTTPHeaderField: "") instead of request.addValue("", forHTTPHeaderField: "")

You cannot populate the Authentication header yourself, period. That header is owned by the URL loading system, and is populated based on credentials in the user's keychain.
You can learn about how to do authentication by reading the authentication chapter of URL Session Programming Guide. Note that the examples are for NSURLConnection, but the rest of the text applies to both.
Alternatively, you can store the credentials in the keychain as an Internet Password item, as described in Keychain Services Programming Guide.

Related

Swift: Mailgun 401 response code--No valid API key provided

So I have my send email function as seen below:
func email() {
let session = URLSession.shared
let request = NSMutableURLRequest(url: NSURL(string: "https://api.mailgun.net/v3/sandbox################################/messages")! as URL)
request.httpMethod = "POST"
let credentials = "api:key-################################-########-########"
request.setValue("Basic \(credentials.toBase64())", forHTTPHeaderField: "Authorization")
let data = "from: Swift Email <(test#test.com)>&to: [myemail#gmail.com,(myemail#gmail.com)]&subject:Hello&text:Testing_some_Mailgun_awesomness"
request.httpBody = data.data(using: String.Encoding.ascii)
let task = session.dataTask(with: request as URLRequest, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let response = response {
print("url = \(response.url!)")
print("response = \(response)")
let httpResponse = response as! HTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
}
So I guess either my API key is wrong or my request URL is wrong. To find my request URL, I went to https://app.mailgun.com/app/dashboard and then scrolled down to the Sending Domains section and copied that. To get my API key I went to https://app.mailgun.com/app/account/security/api_keys and just copied the Private API Key. I'm really not sure why I'm getting this invalid code--thank you in advance if you figure it out!
Side note: Not really sure if the data constant is set up right (in terms of missing or having too many parens), so if you could check that too that would be phenomenal.
I have a software called "paw" it helps forming REST API call for Xcode, curl, php, etc.
I don't know if this could help you
class MyRequestController {
func sendRequest(somevar: String, completion: #escaping (Books) -> Void) {
/* Configure session, choose between:
* defaultSessionConfiguration
* ephemeralSessionConfiguration
* backgroundSessionConfigurationWithIdentifier:
And set session-wide properties, such as: HTTPAdditionalHeaders,
HTTPCookieAcceptPolicy, requestCachePolicy or timeoutIntervalForRequest.
*/
let sessionConfig = URLSessionConfiguration.default
/* Create session, and optionally set a URLSessionDelegate. */
let session = URLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil)
/* Create the Request:
(POST https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages)
*/
guard var URL = URL(string: "https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages") else {return}
var request = URLRequest(url: URL)
request.httpMethod = "POST"
// Headers
request.addValue("Basic YXBpOllPVVJfQVBJX0tFWQ==", forHTTPHeaderField: "Authorization")
request.addValue("multipart/form-data; charset=utf-8; boundary=__X_PAW_BOUNDARY__", forHTTPHeaderField: "Content-Type")
// Body
let bodyString = "--__X_PAW_BOUNDARY__\r\nContent-Disposition: form-data; name=\"from\"\r\n\r\n'Excited User <mailgun#YOUR_DOMAIN_NAME>'\r\n--__X_PAW_BOUNDARY__\r\nContent-Disposition: form-data; name=\"to\"\r\n\r\nYOU#YOUR_DOMAIN_NAME\r\n--__X_PAW_BOUNDARY__\r\nContent-Disposition: form-data; name=\"to\"\r\n\r\nbar#example.com\r\n--__X_PAW_BOUNDARY__\r\nContent-Disposition: form-data; name=\"subject\"\r\n\r\n'Hello'\r\n--__X_PAW_BOUNDARY__\r\nContent-Disposition: form-data; name=\"text\"\r\n\r\n'Testing some Mailgun awesomeness!'\r\n--__X_PAW_BOUNDARY__--\r\n"
request.httpBody = bodyString.data(using: .utf8, allowLossyConversion: true)
/* Start a new Task */
let task = session.dataTask(with: request, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) -> Void in
if (error == nil) {
// Success
let statusCode = (response as! HTTPURLResponse).statusCode
print("URL Session Task Succeeded: HTTP \(statusCode)")
}
else {
// Failure
print("URL Session Task Failed: %#", error!.localizedDescription);
}
})
task.resume()
session.finishTasksAndInvalidate()
}
}
**** and you CALL this function a bit like this
MyRequestController().sendRequest(somevar: "something")
take a look at https://www.youtube.com/watch?v=44APgBnapag for more details
this tutorial show how to do REST API calls with Xcode, this example it scan a barcode, send the scanned code via a function that calls the API and return infos...

a solution to retrieve information when they're no internet connection

On my app I have a form which the user can complete and send it to us with a SOAP request.
the form sended:
But I have a problem, I they're no internet connection I can't send the data by using my SOAP request.
So firstly I save the request in Core Data, so here it's ok... And when the user is on the app I do a scheduled request to see if they're a request in Core Data to send them.
But what about if the app is not running ? In Android I use background service, but with Apple it's impossible:
Android Services equivalent in iOS Swift
Someone have an idea to retrieve the data ?
Soap function:
func soapCall(fonction:String, soapMessageParamsTab:[SoapMessageParams], completion: #escaping (_ strData: NSString)-> ()/*, completion: #escaping (_ result: Int, _ resultContactWebId: Int)->()*/){
let soapRequest = AEXMLDocument()
let attributes = ["xmlns:SOAP-ENV" : "http://schemas.xmlsoap.org/soap/envelope/", "xmlns:ns1" : namespace, "xmlns:xsd" : "http://www.w3.org/2001/XMLSchema", "xmlns:xsi" : "http://www.w3.org/2001/XMLSchema-instance", "xmlns:SOAP-ENC" : "http://schemas.xmlsoap.org/soap/encoding/", "SOAP-ENV:encodingStyle" : "http://schemas.xmlsoap.org/soap/encoding/"]
let envelope = soapRequest.addChild(name: "SOAP-ENV:Envelope", attributes: attributes)
let body = envelope.addChild(name: "SOAP-ENV:Body")
let sendLead = body.addChild(name: "ns1:"+fonction)
for obj in soapMessageParamsTab {
sendLead.addChild(name: obj.soapName, value: obj.soapValue, attributes: ["xsi:type" : "xsd:"+obj.soapType])
}
//sendLead.addChild(name: "xml", value: messageSoap, attributes: ["xsi:type" : "xsd:string"])
let soapMessage = soapRequest.xmlString
let messageSoapMinify = minify(soapMessage)
// URL Soap
let urlString = soapServiceURL
let url = URL(string: urlString)
var theRequest = URLRequest(url: url!)
// Taille SoapMessage
let msgLength = String(messageSoapMinify.characters.count)
// Soap Login
let username = soapUsername
let password = soapPassword
let loginString = NSString(format: "%#:%#", username, password)
let loginData: Data = loginString.data(using: String.Encoding.utf8.rawValue)!
let base64LoginString = loginData.base64EncodedString(options: NSData.Base64EncodingOptions.lineLength64Characters)
// Requete Soap
theRequest.addValue("text/xml; charset=utf-8", forHTTPHeaderField: "Content-Type")
theRequest.addValue("Keep-Alive", forHTTPHeaderField: "Connection")
theRequest.addValue(msgLength, forHTTPHeaderField: "Content-Length")
theRequest.addValue("urn:ResponseAction", forHTTPHeaderField: "SOAPAction")
theRequest.httpMethod = "POST"
theRequest.httpBody = messageSoapMinify.data(using: String.Encoding.utf8, allowLossyConversion: true)
theRequest.addValue("Basic \(base64LoginString)", forHTTPHeaderField: "Authorization")
// print("Request is \(theRequest.allHTTPHeaderFields!)")
let session = Foundation.URLSession.shared
let task = session.dataTask(with: theRequest, completionHandler: {data, response, error -> Void in
if error != nil
{
print(error?.localizedDescription)
}
//print("Response Login: \(response)")
//print("Error Login: \(error)")
let strData = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)!
//print("Body Login: \(strData)")
completion(strData)
})
task.resume()
}
Using NSURLSession with background configuration you should be ok.
Note: If the app is explicitly killed by the user the scheduled request will be cancelled. This trick works just if the app was killed by the system for any reason (low memory, crash etc...). There is nothing you can do if the app was killed by the user. The background fetch might be a solution, but it will be decided by iOS if and when to awake the app for the opportunistic fetch.
This could help you:
https://blog.newrelic.com/2016/01/13/ios9-background-execution/
Edit:
This tutorial could be useful as well: https://www.raywenderlich.com/110458/nsurlsession-tutorial-getting-started

NSURLSession parameters not recognized

Im attemping to make a HTTPRequest using NSURLSession. When I set the full url the request returns the correct data but when using parameters (NSJSONSerialization.dataWithJSONObject -> HTTPBody I get this error
error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost."
is there something im doing wrong here?
let json = ["api_key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"]
do {
let jsonData = try NSJSONSerialization.dataWithJSONObject(json, options: .PrettyPrinted)
let url = NSURL(string: "https://api.themoviedb.org/3/discover/movie")!
let request = NSMutableURLRequest(URL: url)
request.HTTPBody = jsonData
request.HTTPMethod = "GET"
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){ data, response, error in
if error != nil{
print("Error -> \(error)")
return
}
do {
let result = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String:AnyObject]
print("Result -> \(result)")
} catch {
print("Error -> \(error)")
}
}
task.resume()
} catch {
print(error)
}
}
This is not a duplicate! I looked at the suggested answer (none of them worked) before asking this question
In your case that issue can be solved by changing the request.HTTPMethod = "GET" to request.HTTPMethod = "POST"
You should not send HTTP Body in the get request, to send the data with the body you should change HTTPMethod to post
Note: Please check if this api method supports POST requests, if it don't support post you can't use it with http body/post, as per doc i only find 'get' request for the discover/movie which can be like this:
let url = NSURL(string: "http://api.themoviedb.org/3/discover/movie?api_key=YOUR_API_KEY")!
let request = NSMutableURLRequest(URL: url)
request.addValue("application/json", forHTTPHeaderField: "Accept")
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { data, response, error in
if let response = response, data = data {
print(response)
print(String(data: data, encoding: NSUTF8StringEncoding))
} else {
print(error)
}
}
task.resume()
Ref: You can check more information from this url: http://docs.themoviedb.apiary.io/#reference/discover/discovermovie/get

ERROR code 201: missing user password when creating anonymous user: Parse.com REST API

I am trying to sign up an anonymous user using REST API. I found several discussion regarding linking Twitter/Facebook users that share the same problem but none gave this solution.
According to Parse.com REST API Doc, you only need to provide some sort of id to the authData, i.e UDID, but it complains that there is no password. This is the error:
"{"code":201,"error":"missing user password"}
This is my code:
func registerAnonymisUserWithComplitionHandler(deviceUDID:String, onComplition: RESTResponse){
let request = NSMutableURLRequest()
request.HTTPMethod = "POST"
request.addValue("APP ID", forHTTPHeaderField: "X-Parse-Application-Id")
request.addValue("API Key", forHTTPHeaderField: "X-Parse-REST-API-Key")
request.addValue("1", forHTTPHeaderField: "X-Parse-Revocable-Session")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let urlString = "https://api.parse.com"
let baseURL = NSURL(string: urlString)
let url = NSURL(string: "/1/users", relativeToURL: baseURL!)
request.URL = url
let param = ["authData": ["anonymous":["id":deviceUDID]]]
let session = NSURLSession.sharedSession()
do {
request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(param, options: .PrettyPrinted)
} catch {
print("error")
}
let task = session.dataTaskWithRequest(request) {
(data, response, error) in
if (error == nil){
do {
let tmpUserDictionary = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String:AnyObject]
print(tmpUserDictionary)
let statusResponse = response as! NSHTTPURLResponse
onComplition(jsonDictionary: tmpUserDictionary, response: statusResponse, error: nil)
} catch {
}
} else {
onComplition(jsonDictionary: nil, response: nil, error: error)
}
}
task.resume()
}
Thanks to Muhammad Junaid Butt, I have fixed a small issue in the param and it works.
Check the attached screenshot. I used REST API of PARSE to create an Anonymous user and it is working fine. (I used Postman for testing this).

Status code 400 while making POST request in swift

I keep getting this error which I don't know how to resolve!
I'm trying to parse access token from a web API and for that I've set up post request.
Somehow it's throwing an error on 'responseString' line.
import UIKit
import Foundation
class ViewController: UIViewController, NSURLConnectionDelegate {
#IBOutlet var userName: UITextField!
#IBOutlet var password: UITextField!
#IBAction func submit(sender: AnyObject) {
let postString = "username=" + userName.text + "&password=" + password.text + "&grant_type=password";
let data: NSData = postString.dataUsingEncoding(NSUTF8StringEncoding)!
let base64LoginString = data.base64EncodedStringWithOptions(nil)
let postLength=NSString(format: "%ld", data.length)
let url = NSURL(string: "http://www.myurl.com/Token")
let request = NSMutableURLRequest(URL: url!);
request.HTTPMethod = "POST"
request.HTTPBody = data
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.addValue(base64LoginString, forHTTPHeaderField: "Authorization")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.setValue(postLength as String, forHTTPHeaderField: "Content-Length")
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {data, response, error in
if error != nil {
println("error = \(error)")
return
}
//print out response object
println("******* response = \(response)")
//print out response body // THIS LINE THROWS STATUS CODE 400 ERROR
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)
println("******* response data = \(responseString)")
var err: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: &err) as? NSDictionary
if let parseJSON = json {
var token = parseJSON["access_token"] as? String
println("token: \(token)")
}
}
task.resume()
}
This is the error
******* response = <NSHTTPURLResponse: 0x7ff1eb4496d0> { URL: https://www.myurl.com/Token } { status code: 400, headers {
"Cache-Control" = "no-cache";
"Content-Length" = 34;
"Content-Type" = "application/json;charset=UTF-8";
Date = "Mon, 10 Aug 2015 20:16:14 GMT";
Expires = "-1";
Pragma = "no-cache";
"X-Powered-By" = "ASP.NET";
} }
Your error seems to be incorrect usage of basic auth format. It should be like this:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
where
QWxhZGRpbjpvcGVuIHNlc2FtZQ==
is username:password in base64
So in your case it is like this :
let postString = userName.text + ":" + password.text ;
let data: NSData = postString.dataUsingEncoding(NSUTF8StringEncoding)!
let base64LoginString = data.base64EncodedStringWithOptions(nil)
let postLength=NSString(format: "%ld", data.length)
let url = NSURL(string: "http://www.myurl.com/Token")
let request = NSMutableURLRequest(URL: url!);
request.HTTPMethod = "POST"
request.HTTPBody = ""
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.addValue("Basic " + base64LoginString, forHTTPHeaderField: "Authorization")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("0", forHTTPHeaderField: "Content-Length")
btw it would be a lot easier to find your error if you provide your api url
The error code 400 says that the server doesn't understand for request, maybe the request is malformed. This is very likely a client side issue. However, I've written a simple python http server, and used the code above (with only change to the URL entry point), everything works. I strongly suggest that you should check the API documentation on the backend and find out why your backend server doesn't recognize your request. Your client side code seems correct.
Try this on your own:
Create myserver.py, and add the following lines to it:
from flask import Flask, request
app = Flask(__name__)
#app.route('/Token', methods=['POST'])
def hello_world():
print(request.form)
return 'Hello World!'
if __name__ == '__main__':
app.run()
Run this python script from terminal, and change http://www.myurl.com to http://127.0.0.1:5000, you'll see that the request gets through, and the server can correctly parse your username password, etc.
It's always a good idea to try to setup your own local web server to mock the backend during debugging/development according to backend API specification.
try this one
//first make json object.you can use your object for this one.
let JSONObject: [String : AnyObject] = [
"DeviceHash" : UUIDValue,
"DeviceName" : deviceNameValue!,
"SerialKey": serialKeyValue!,
]
if NSJSONSerialization.isValidJSONObject(JSONObject) {
var request: NSMutableURLRequest = NSMutableURLRequest()
let url = "Your Url"
var err: NSError?
request.URL = NSURL(string: url)
request.HTTPMethod = "POST"
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
do {
request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(JSONObject, options: NSJSONWritingOptions())
print(JSONObject)
} catch {
print("bad things happened")
}
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue()) {(response, data, error) -> Void in
let httpResponse = response as! NSHTTPURLResponse
if error != nil {
print("error")
}else {
print(response)
}
}
}