How can I read the content of a form-data when the response is in html(Swift) - swift

I'm building an iOS application and I need to access the information on a website. I've located the API endpoint, and was able to get a result in Postman
screenshot of API header and form data
So far I have this code which can allow me to make the request, but how do I parse the response(which is an HTML form, then display response in app
var urlRequest = URLRequest(url: url!)
urlRequest.setValue("application/form-data",forHTTPHeaderField: "Content-Type")
urlRequest.httpMethod = "POST"
let postString = "year=2021&season=Outdoor&province=ON&age_group=OPEN&age_sub_group_masters=ALL&age_sub_group_para=ALL&rankings_event=100m&best_by_athlete=1&rankings_event_spec_num=1&is_relay_EVENT=0&page=1"
urlRequest.httpBody = postString.data(using: .utf8)
urlRequest = .init(url: url!)```

I actually found a great resources that showed how to send POST Request with post body, and how to read the response
How To send POST Request
Now it's just a matter of parsing the HTML that is returned, and displaying it in app.
let url = URL(string: Constants.rankingAPI)
guard let requestUrl = url else {
fatalError()
}
var request = URLRequest(url: requestUrl)
request.httpMethod = "POST"
let postString = "year=2021&season=Outdoor&province=ON&age_group=OPEN&age_sub_group_masters=ALL&age_sub_group_para=ALL&rankings_event=100m&best_by_athlete=1&rankings_event_spec_num=1&is_relay_EVENT=0&page=1"
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
// Check for Error
if let error = error {
print("Error took place \(error)")
return
}
// Convert HTTP Response Data to a String
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print("Response data string:\n \(dataString.htmlToString)")
}
}
task.resume()
}

Related

Swift POST to api

I have an odd issue and I am hoping you guys can help me.
The Task:
I want to post json to a API rest call.
And I am sure the rest API works - I have copied the json from print() in Swift and tried it in postman, and the works just fine.
When I try to post via Swift i get 400 Bad request.
The code:
func postMobileContacts(serverURL: String, jsonToPost: String){
// Prepare URL
let url = URL(string: serverURL)
guard let requestUrl = url else { fatalError() }
// Prepare URL Request Object
var request = URLRequest(url: requestUrl)
request.httpMethod = "POST"
// Set HTTP Request Body
request.httpBody = Data(jsonToPost.utf8);
// Set HTTP Request Header
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Authorization", forHTTPHeaderField: String(data: KeychainHelper.standard.read(service: "access-token", account: "store")!, encoding: .utf8)!)
// Perform HTTP Request
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
// Check for Error
if let error = error {
print("Error took place \(error)")
return
}
// Convert HTTP Response Data to a String
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print("Response data string:\n \(dataString)")
}
}
task.resume()
}
This line is definitely wrong:
request.setValue("Authorization", forHTTPHeaderField: String(data: KeychainHelper.standard.read(service: "access-token", account: "store")!, encoding: .utf8)!)
it should be the other way around.
request.setValue(String(data: KeychainHelper.standard.read(service: "access-token", account: "store")!, encoding: .utf8)!, forHTTPHeaderField: "Authorization")
You mixed up the value with the header name.
Made some edits to your code
Currently the data is not directly sent to the request as said in comments.
JSONEncodable can be send to the request after encoding to data.
func postMobileContacts(serverURL: String, jsonToPost: Encodable) { //<--here
// Prepare URL
let url = URL(string: serverURL)
guard let requestUrl = url else { fatalError() }
let data = try! JSONEncoder().encode(jsonToPost) //<-Changed here
// Prepare URL Request Object
var request = URLRequest(url: requestUrl)
request.httpMethod = "POST"
// Set HTTP Request Body
request.httpBody = data; //<-- and here
// Set HTTP Request Header
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Authorization", forHTTPHeaderField: String(data: KeychainHelper.standard.read(service: "access-token", account: "store")!, encoding: .utf8)!)
// Perform HTTP Request
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
// Check for Error
if let error = error {
print("Error took place \(error)")
return
}
// Convert HTTP Response Data to a String
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print("Response data string:\n \(dataString)")
}
}
task.resume()
}

How to use POST request to Firebase Firestore from swift?

I would like to use the Firestore REST API from Swift, because I am using Siri Shortcut Intents, where I am not able to use the native SDK.
What I have tried so far is to create an URLSession with "POST" httpmethod, but no luck. I have been able successfully to create document to use the form found on firestore website. But I could make successful Swift version of it.
Here is the code I have tried:
private func addTask() {
let parent = "projects/reality-kanban/databases/(default)/documents/l3VXrtTLoz11VGn60ott"
let collectionId = "A33XrtfL2ea3dG340era"
let urlString = "https://firestore.googleapis.com/v1/\(parent)/\(collectionId)"
let requestBody = DocumentBody(name: parent, fields: RequestTask(description: "test")) // it is a codable struct
let jsonData = try! JSONEncoder().encode(requestBody)
print(String(data: jsonData, encoding: .utf8)!)
let url = URL(string: urlString)!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = jsonData
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else {
print("Invalid Response received from the server") // this is what I get
return
}
}
task.resume()
}
This is the error I get: Invalid Response received from the server (400)
Add --debug to the command you are running to corroborate if you have set the right project.

How can I stay logged in when web scraping with URLSession?

With my program I try to login and scrape the home page, however after I log in with a post request (in which the response says successful), when I try to get the home page it says my session has timed out.
I've tried using different types of session, modifying minor parts of the code, and Alamofire with no success
import Foundation
let session: URLSession = {
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 30 // seconds
configuration.timeoutIntervalForResource = 30 // seconds
return URLSession(configuration: .default)
}()
let url = URL(string: "examplesite.com")!
var request = URLRequest(url: url)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField:
"Content-Type")
request.httpMethod = "POST"
let postString = "Email=exampleemail&Password=examplepass"
request.httpBody = postString.data(using: .utf8)
//log in
session.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
// check for fundamental networking error
print("\(error)")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode
!= 200 { // check for http errors
print("\(httpStatus.statusCode)")
print("\(response)")
}
let responseString = String(data: data, encoding: .utf8)
print("\(responseString)")
let urlnext = URL(string: "example.com/HomePage")!
//get page after loggin in
session.dataTask(with: urlnext) { datanext, responsenext, errornext in
guard let datanext = datanext, errornext == nil else {
print("\(errornext)")
return
}
let stringnext = String(data: datanext, encoding: .utf8)
//get page html
print("\(stringnext)")
}.resume()
}.resume()
// Infinitely run the main loop to wait for our request.
// Only necessary if you are testing in the command line.
RunLoop.main.run()
the results are a successful login {\"msg\":\"\",\"valid\":\"1\"}, and the html code of a time out page instead of the home page

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

Swift Send Email with MailGun

Problem
I would like to use the MailGun service to send emails from a pure Swift app.
Research So Far
As I understand it, there are two methods to send an email via MailGun. One is to email MailGun with the emails, and MailGun will redirect it (See Send via SMTP). That will, as I understand it, not work, as iOS cannot programatically automatically send mail, and must use methods that require user intervention. As such, I should use the API directly. As I understand it, I need to open a URL to do this, and so I should use some form of NSURLSession, as per this SO answer
Code
MailGun provides documentation for Python, which is as follows:
def send_simple_message():
return requests.post(
"https://api.mailgun.net/v3/sandbox(Personal info).mailgun.org/messages",
auth=("api", "key-(Personal info)"),
data={"from": "Excited User <(Personal info)>",
"to": ["bar#example.com", "(Personal info)"],
"subject": "Hello",
"text": "Testing some Mailgun awesomness!"})
with (Personal info) being substituted for keys/information/emails.
Question
How do I do that in Swift?
Thanks!
In python, the auth is being passed in the header.
You have to do a http post request, passing both the header and the body.
This is a working code:
func test() {
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: NSURL(string: "https://api.mailgun.net/v3/sandbox(Personal info).mailgun.org/messages")!)
request.HTTPMethod = "POST"
let data = "from: Excited User <(Personal info)>&to: [bar#example.com,(Personal info)]&subject:Hello&text:Testinggsome Mailgun awesomness!"
request.HTTPBody = data.dataUsingEncoding(NSASCIIStringEncoding)
request.setValue("key-(Personal info)", forHTTPHeaderField: "api")
let task = session.dataTaskWithRequest(request, 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! NSHTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
}
people are getting 400 or 401 errors because none of the other answers construct the url correctly. here is some code that works in swift 5 and iOS15:
func sendEmail() {
// variablize our https path with API key, recipient and message text
let mailgunAPIPath = "https://api:YOUR_API_KEY#api.mailgun.net/v3/YOUR_DOMAIN/messages?"
let emailRecipient = "RECIPIENT#EMAIL.COM"
let emailMessage = "Testing%20email%20sender%20variables"
// Create a session and fill it with our request
let session = URLSession.shared
let request = NSMutableURLRequest(url: NSURL(string: mailgunAPIPath + "from=USER#YOUR_DOMAIN&to=\(emailRecipient)&subject=A%20New%20Test%21&text=\(emailMessage)")! as URL)
// POST and report back with any errors and response codes
request.httpMethod = "POST"
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()
}
requests.post sends an HTTP POST request, encoding key/value pairs as application/x-www-form-urlencoded. You need to do the same.
convert the set of key-value pairs into application/x-www-form-urlencoded as per How to escape the HTTP params in Swift
compose the request using the resulting string for data & send it as per iOS : http Post using swift
I spent hours trying to get the selected answer working, but to no avail.
Although I was finally able to get this working properly with a large HTTP response. I put the full path into Keys.plist so that I can upload my code to github and broke out some of the arguments into variables so I can have them programmatically set later down the road.
// Email the FBO with desired information
// Parse our Keys.plist so we can use our path
var keys: NSDictionary?
if let path = NSBundle.mainBundle().pathForResource("Keys", ofType: "plist") {
keys = NSDictionary(contentsOfFile: path)
}
if let dict = keys {
// variablize our https path with API key, recipient and message text
let mailgunAPIPath = dict["mailgunAPIPath"] as? String
let emailRecipient = "bar#foo.com"
let emailMessage = "Testing%20email%20sender%20variables"
// Create a session and fill it with our request
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: NSURL(string: mailgunAPIPath! + "from=FBOGo%20Reservation%20%3Cscheduler#<my domain>.com%3E&to=reservations#<my domain>.com&to=\(emailRecipient)&subject=A%20New%20Reservation%21&text=\(emailMessage)")!)
// POST and report back with any errors and response codes
request.HTTPMethod = "POST"
let task = session.dataTaskWithRequest(request, 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! NSHTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
}
The Mailgun Path is in Keys.plist as a string called mailgunAPIPath with the value:
https://API:key-<my key>#api.mailgun.net/v3/<my domain>.com/messages?
Hope this offers a solution to anyone else having issues with MailGun and wanting to avoid a 3rd party solution!
Swift 3 answer:
func test() {
let session = URLSession.shared
var request = URLRequest(url: URL(string: "https://api.mailgun.net/v3/sandbox(Personal info).mailgun.org/messages")!)
request.httpMethod = "POST"
let data = "from: Excited User <(Personal info)>&to: [bar#example.com,(Personal info)]&subject:Hello&text:Testinggsome Mailgun awesomness!"
request.httpBody = data.data(using: .ascii)
request.setValue("key-(Personal info)", forHTTPHeaderField: "api")
let task = session.dataTask(with: request, 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()
}