I'm creating an application to download my university timetable. I've done the REST calls in Java first to demonstrate a prototype which works nicely. And now I'd like to do it in Swift using Alamofire (or anything else which works).
Below is the REST call in Java that I'm trying to replicate.
Client client = Client.create();
String authString = "UID=XXXXX&PASS=XXXXX";
byte[] authBytes = authString.getBytes();
WebResource webResouce = client.resource("https://access.adelaide.edu.au/sa/login.asp");
ClientResponse response = webResource.post(ClientResponse.class, authBytes);
if (response.getStatus != 302) {
throw new RuntimeException("Failed: HTTP code: " + response.getStatus());
}
However I'm having trouble sending the bytes properly. The server will actually accept any byte data (so you can see if it works without a UID or PASS) and respond with 302, which indicates that it works. Otherwise it'll send a 200 which means it didn't.
I've had a few attempts of sending the UID and PASS in a parameter, getting their bytes and then putting them in a parameter etc etc. but nothing seems to work so far.
Any help would be great, thanks!
You should use Alamofire's custom encoding technique (something like this). This is my 3rd hour of Swift so bear with me.
struct ByteEncoding: ParameterEncoding {
private let data: Data
init(data: Data) {
self.data = data
}
func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var urlRequest = try urlRequest.asURLRequest()
urlRequest.httpBody = data
return urlRequest
}
}
Alamofire.request(url: "url", method: .post, parameters: nil, encoding: ByteEncoding(data: authBytesAsData)
Documentation
https://github.com/Alamofire/Alamofire#custom-encoding
If you use a regular NSURLRequest you can just set the request body:
let URL = NSURL(string: "https://access.adelaide.edu.au/sa/login.asp")!
let request = NSMutableURLRequest(URL: URL)
request.HTTPBody = // NSData you want as your body
Edit
As pointed out by #mattt himself, you can pass an NSURLRequest to Alamofire. No need for the hassle with custom parameter encoding as I answered first. (See below)
I don't exactly know how to do this using Alamofire, but it seems you can use a Custom parameter encoding with a closure. I didn't test this but took it from the Alamofire unit test source:
let encodingClosure: (URLRequestConvertible, [String: AnyObject]?) -> (NSURLRequest, NSError?) = { (URLRequest, parameters) in
let mutableURLRequest = URLRequest.URLRequest.mutableCopy() as NSMutableURLRequest
mutableURLRequest.HTTPBody = parameters["data"]
return (mutableURLRequest, nil)
}
let encoding: ParameterEncoding = .Custom(encodingClosure)
let URL = NSURL(string: "https://access.adelaide.edu.au/sa/login.asp")!
let URLRequest = NSURLRequest(URL: URL)
let data: NSData = // NSData you want as your body
let parameters: [String: AnyObject] = ["data": data]
let URLRequestWithBody = encoding.encode(URLRequest, parameters: parameters).0
Here's a quick example of how you could make this type of request.
import Alamofire
class BytesUploader {
func uploadBytes() {
let URLRequest: NSURLRequest = {
let URL = NSURL(string: "https://access.adelaide.edu.au/sa/login.asp")!
let mutableURLRequest = NSMutableURLRequest(URL: URL)
mutableURLRequest.HTTPMethod = "POST"
let authString = "UID=XXXXX&PASS=XXXXX"
let authData = authString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
mutableURLRequest.HTTPBody = authData
return mutableURLRequest.copy() as NSURLRequest
}()
let request = Alamofire.request(URLRequest)
request.response { request, response, data, error in
if let response = response {
println("Response status code: \(response.statusCode)")
if response.statusCode == 302 {
println("Request was successful")
} else {
println("Request was NOT successful")
}
} else {
println("Error: \(error)")
}
}
}
}
You need to encode your authorization string as an NSData object and then set that as the HTTPBody of the NSURLRequest. This should match your Java code that you posted.
Related
I set up the API and all, the only thing is Bearer Token I couldn't find any information about any code on how to implement it so it can validate the URL I am using as API.
do I need to create new swift file just for bearer token or I can write the code to the API swift file "the code below is api file"
static let shared = APICaller()
private let baseURL = "http://000.000.000.000:3030/api/"
private init() {}
var vehicles = [Vehicles]()
func getVehicles(for id: String, IMEI: Int, completed: #escaping (Result<[Vehicles],Errors>) -> Void ){
let endpoint = baseURL + "GetVehicle/?UserIdentificationValue=346HIU4623UIHG3I3I&IMEI=216216123612"
guard let url = URL(string: endpoint) else {
completed(.failure(.invalidURL))
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let _ = error {
completed(.failure(.unableToComplete))
return
}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
completed(.failure(.invalidResponse))
return
}
guard let data = data else {
completed(.failure(.invalidData))
return
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
self.vehicles = try JSONDecoder().decode([Vehicles].self, from: data)
DispatchQueue.main.async {
completed(.failure(.invalidData))
}
} catch {
completed(.failure(.invalidData))
}
}
task.resume()
Thanks In Advance
Also I am new to swift so I would appreciate if you can tell me my API code is correct or needs any fixes since its about receiving some car info and putting into a table view cell :)
I have attached the request including headers in which you need to pass Bearer token like did in below code
let headers = [
"content-type": "application/json",
"authorizetoken": "NjQzOPA2N0NDNDFAH4CNDk3R23F2FQUY0NjV3FFE=",
"cache-control": "no-cache",
]
let parameters = ["id":"123456789"] as [String : Any]
let postData = try? JSONSerialization.data(withJSONObject: parameters, options: [])
let request = NSMutableURLRequest(url: NSURL(string: "Your URL")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 120.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as? Data
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData
I'm trying to create a URL Request in Swift to send an HTTP POST request to a Discord Webhook, and the request completes in a failure defined as 'responseProblem'. I'm not sure where the response problem is originating from.
Discord should technically be able to accept HTTP requests, and a bunch of research into the issue has led me nowhere. I've worked with Discord webhooks before, but I've never played around with this kind of stuff before, so I'm kinda stuck for what I should do now.
import Foundation
enum APIError:Error {
case responseProblem
case decodingProblem
case encodingProblem
}
struct APIRequest {
let resourceURL: URL
init(endpoint: String) {
let resourceString = "<discord-webhook-url-removed-for-privacy>"
guard let resourceURL = URL(string: resourceString) else {fatalError()}
self.resourceURL = resourceURL
}
func save (_ messageToSave:Message, completion: #escaping(Result<Message, APIError>) -> Void ) {
do {
var urlRequest = URLRequest(url: resourceURL)
urlRequest.httpMethod = "POST"
urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
urlRequest.httpBody = try JSONEncoder().encode(messageToSave)
let dataTask = URLSession.shared.dataTask(with: urlRequest) { data, response, _ in
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200, let
jsonData = data else {
completion(.failure(.responseProblem)) // <<< 'responseProblem' is outputted in console as the error
return
}
do {
let messageData = try JSONDecoder().decode(Message.self, from: jsonData)
completion(.success(messageData))
} catch {
completion(.failure(.decodingProblem))
}
}
dataTask.resume()
} catch {
completion(.failure(.encodingProblem))
}
}
}
When I run this Swift program, I expected it to send a request to the Discord Webhook to send a message into it's designated channel. However, the only error outputted into the console is responseProblem. Please let me know if there is anything further I need to add to get the root cause of the problem (I'm still semi-fresh to Swift, and I normally work with JavaScript, so I'm not sure how to properly debug in Swift and Xcode.)
Swift app is built in iOS 12.2 because Xcode doesn't like this stuff in iOS 13
This is a simplified version of how I post to a Discord webhook with Swift. From your post I can't see how you're converting your custom Message struct into a JSON dictionary, but you need at least the key/value pair for "content" to successfully post a Discord Webhook. And of course the posts can be customized in many other ways (with a custom "username" etc.).
var messageString: String = ""
guard let url = URL(string: "your-full-discord-webhook-url") else { return }
let messageJson: [String: Any] = ["content": messageString]
let jsonData = try? JSONSerialization.data(withJSONObject: messageJson)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "content-type")
request.httpBody = jsonData
let task = URLSession.shared.dataTask(with: request)
task.resume()
This is trivia, but can not find good example.
I have following function for sending POST requests:
static func sendArrival(scan: ArrivalScan){
var urlComponents = URLComponents()
urlComponents.scheme = "https"
urlComponents.host = "api.my.url.com"
urlComponents.path = "/Delivery/Arrival/?id="
guard let url = urlComponents.url else { fatalError("Could not create URL from components") }
// Specify this request as being a POST method
var request = URLRequest(url: url)
request.httpMethod = "POST"
// Make sure that we include headers specifying that our request's HTTP body
// will be JSON encoded
var headers = request.allHTTPHeaderFields ?? [:]
headers["Content-Type"] = "application/json"
headers["ZM_APIKey"] = "mySecretKey"
request.allHTTPHeaderFields = headers
// Now let's encode out Post struct into JSON data...
let encoder = JSONEncoder()
do {
let jsonData = try encoder.encode(scan)
// ... and set our request's HTTP body
request.httpBody = jsonData
print("jsonData: ", String(data: request.httpBody!, encoding: .utf8) ?? "no body data")
} catch {
//TODO: error handling
}
// Create and run a URLSession data task with our JSON encoded POST request
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session.dataTask(with: request) { (responseData, response, responseError) in
guard responseError == nil else {
//TODO: error handling
return
}
// APIs usually respond with the data you just sent in your POST request
if let data = responseData, let utf8Representation = String(data: data, encoding: .utf8) {
print("response: ", utf8Representation)
} else {
print("no readable data received in response")
}
}
task.resume()
}
But something is not working. To decode I need to see full request (in format it will hit server). How to print this?
To see much of the information in the request printed to the console, you can use:
dump(request)
A better option in my opinion is to use a tool like Charles or Wireshark, as Rob mentions above in the comments.
I am trying to make an HTTP Post request to a development server with self signed certificate. This is the function making the POST call:
func makeHTTPPostRequest(path: String, body: JSON, onCompletion: (JSON?, NSError?) -> Void) {
let request = NSMutableURLRequest(URL: NSURL(string: path)!)
request.HTTPMethod = "POST"
// I am using SwiftyJSON
do {
request.HTTPBody = try body.rawData()
} catch _ { }
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue:NSOperationQueue.mainQueue())
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
var json: JSON?
if let _data = data {
json = JSON(data: _data)
}
onCompletion(json, error)
})
task.resume()
}
When I make a POST request, the server returns me "Empty fields" error even though I have properly set the HTTPBody of the request:
PS: The route is working fine when I call it from Postman.
The request.HTTPBody property must be a NSData object. If you want to send JSON, the data object should contain a sequence of Unicode characters (preferable UTF-8) which is your serialised JSON representation.
Additionally, you should also set the Content-Type header accordingly to application/json.
I am still learning Swift and I am trying to make a POST request to my web service via my new iOS App written in Swift.
I need to know how to add 2 headers to my already existing code. Also am I adding the parameters correctly?
What I have so far:
let myUrl = NSURL(string: "https://MY-MOBILE-SERVICE.azure-mobile.net/api/login");
let request = NSMutableURLRequest(URL:myUrl!);
request.HTTPMethod = "POST";
// Compose a query string
let postString = "email=myemail#website.com&password=123";
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)")
}
task.resume()
Here are the Headers I need to add to this request:
X-ZUMO-APPLICATION: 45634243542434
ACCEPT: application/json
How do I attach these headers to my request?
Have a look at the reference docs:
https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSMutableURLRequest_Class/#//apple_ref/occ/instm/NSMutableURLRequest/setValue:forHTTPHeaderField:
so you would do: request.setValue("ACCEPT" forHTTPHeaderField: "application/json")
if you use alamofire, this should work, it also eases things for you so you choose get or post
var pars2 : Dictionary<String,String> = ["api_key":"value"]
Alamofire.request(.POST, "someURLString" ,parameters: pars2).responseJSON()
{
(request, response, data, error) in
if(data != nil)
{
self.countryIDArray.removeAll(keepCapacity: false)
var status = data!["status"]!! as! String
}
}