Swift POST Request not working - node.js

I have this server set up with NodeJS that can receive a name and an email and let me know if they match.It can also let me know if the received data is not valid.
This is my code in swift:
func post() {
let parameters = [
"email": "windvaan#live.nl",
"password": "Dittoenbram1234!"
]
let url = NSURL(string: "http://localhost:3000/login")
var request = URLRequest(url: url! as URL)
request.httpMethod = "POST"
guard let httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: JSONSerialization.WritingOptions.prettyPrinted) else {
return
}
request.httpBody = httpBody
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
} catch {
print(error.localizedDescription)
}
}
}.resume()
}
When I call this function my server says that the data is not valid, but the data I put as the parameters is. I believe it has something to do with this function and the parameters because my server is working fine. Thx in advance!

I figured it out, I had to add in this in my swift file:
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")

As per as your parameters value, it's not in valid JSON format.
When you print your parameters value it will print - ["password": "Dittoenbram1234!", "email": "windvaan#live.nl"] , which is invalid.
You need to have this format - { email = "windvaan#live.nl"; password = "Dittoenbram1234!";}
For this
Replace this -
let parameters = [
"email": "windvaan#live.nl",
"password": "Dittoenbram1234!"
]
With this -
let parameters: NSDictionary = [
"email": "windvaan#live.nl",
"password": "Dittoenbram1234!"
]
Hope this will help you. :)

Related

fetch customerId for myApiClient

I am trying to fetch the customerId for myAPIClient using swift firebase and node.js. When I run my application it crashes and tells me that the customerId is nil. The reason that I am needing this customerId is in order to create the ephemeralKey.
The code that I use in node.js is,
exports.createEphemeralKey = functions.https.onRequest((req, res) => {
const stripe_version = req.body.api_version;
const customerId = req.body.customerId
if (!stripe_version) {
console.log('I did not see any api version')
res.status(400).end()
return;
}
stripe.ephemeralKeys.create(
{customer: customerId},
{stripe_version: apiVersion}
).then((key) => {
console.log("Ephemeral key: " + key)
res.status(200).json(key)
}).catch((err) => {
console.log('stripe version is ' + stripe_version + " and customer id is " + customerId + " for key: " + stripe_key + " and err is " + err.message )
res.status(500).json(err)
});
});
and the code that I am using inside of myApiClient is.
func createCustomerKey(withAPIVersion apiVersion: String, completion: #escaping STPJSONResponseCompletionBlock) {
let url = self.baseURL.appendingPathComponent("ephemeral_keys")
let defaults = UserDefaults.standard
let customerId = defaults.string(forKey: "customerId")
AF.request(url, method: .post, parameters: [
"api_version": apiVersion, "customer_id": customerId!
])
.validate(statusCode: 200..<300)
.responseJSON { responseJSON in
switch responseJSON.result {
case .success(let json):
completion(json as? [String: AnyObject], nil)
case .failure(let error):
completion(nil, error)
}
}
}
I have been stuck on creating this ephemeralKey for a while now and have asked other questions but have not gotten a real answer. Is there anything that I am missing? How can I actually access the customersId from stripe inside of my ios project.
You should first check that let customerId = defaults.string(forKey: "customerId") provides a valid customerId. You can do so by debugging your app and inspecting the value of customerId. Assuming that it is valid, I see a couple of things that may be causing issues for you:
The Alamofire library that you are using has 2 Parameter Encoders with different properties and options. You can see them here.
My hypothesis is that you are nor passing the customerId correclty to the body of the request. I suggest you start by trying the JSONParameterEncoder. Your request would be somehting like this:
AF.request(url, method: .post, parameters: [
"api_version": apiVersion, "customer_id": customerId!
],
encoder: JSONParameterEncoder.default)
It is possible that you also have to tweak the way your retrieve the customerID from the Cloud Function according to how you pass the info from the swift app. This docs may provide some insights on how to parse HTTP request info

Could not parse the ephemeral key response following protocol

I am trying to create an ephemeral key in my IOS app. I can successfully create a stripe customer that saves in my firebase console and on my stripe dashboard. However, when I try to create the ephemeral key, I am receiving the error in my ios console after trying to view the checkout controller.
'Could not parse the ephemeral key response following protocol STPCustomerEphemeralKeyProvider. Make sure your backend is sending the unmodified JSON of the ephemeral key to your app.
and on my firebase function logs I am seeing,
createEphemeralKey
Request has incorrect Content-Type.
createEphemeralKey
Invalid request, unable to process.
in my index.js file, the code that I am using is
exports.createEphemeralKey = functions.https.onCall(async(data, context) => {
var stripeVersion = data.api_version;
const customerId = data.customer_id;
return stripe.ephemeralKeys.create(
{customer: customerId},
{stripe_version: stripeVersion}
).then((key) => {
return key
}).catch((err) => {
console.log(err)
})
})
Below is how I create my stripe customer.
exports.createStripeCustomer = functions.auth.user().onCreate((user) => {
return stripe.customers.create({
email: user.email,
}).then((customer) => {
return admin.database().ref(`/stripe_customers/${user.uid}/customer_id`).set(customer.id);
});
});
and then myAPIClient looks like.
enum APIError: Error {
case unknown
var localizedDescription: String {
switch self {
case .unknown:
return "Unknown error"
}
}
}
static let sharedClient = MyAPIClient()
var baseURLString: String? = "https://myProject.cloudfunctions.net/"
var baseURL: URL {
if let urlString = self.baseURLString, let url = URL(string: urlString) {
return url
} else {
fatalError()
}
}
func createCustomerKey(withAPIVersion apiVersion: String, completion: #escaping STPJSONResponseCompletionBlock) {
let url = self.baseURL.appendingPathComponent("ephemeral_keys")
Alamofire.request(url, method: .post, parameters: [
"api_version": apiVersion,
])
.validate(statusCode: 200..<300)
.responseJSON { responseJSON in
switch responseJSON.result {
case .success(let json):
completion(json as? [String: AnyObject], nil)
case .failure(let error):
completion(nil, error)
}
}
}
On my checkOutVC, I have
var stripePublishableKey = "pk_test_testProjectKey"
var backendBaseURL: String? = "https://myProject.cloudfunctions.net"
let customerContext = STPCustomerContext(keyProvider: MyAPIClient())
init(price: Int, settings: Settings) {
if let stripePublishableKey = UserDefaults.standard.string(forKey: "StripePublishableKey") {
self.stripePublishableKey = stripePublishableKey
}
if let backendBaseURL = UserDefaults.standard.string(forKey: "StripeBackendBaseURL") {
self.backendBaseURL = backendBaseURL
}
let stripePublishableKey = self.stripePublishableKey
let backendBaseURL = self.backendBaseURL
assert(stripePublishableKey.hasPrefix("pk_"), "You must set your Stripe publishable key at the top of acceptWorker.swift to run this app.")
assert(backendBaseURL != nil, "You must set your backend base url at the top of acceptWorker.swift to run this app.")
Stripe.setDefaultPublishableKey(self.stripePublishableKey)
let config = STPPaymentConfiguration.shared()
config.appleMerchantIdentifier = self.appleMerchantID
config.companyName = self.companyName
config.requiredBillingAddressFields = settings.requiredBillingAddressFields
config.requiredShippingAddressFields = settings.requiredShippingAddressFields
config.shippingType = settings.shippingType
config.additionalPaymentOptions = settings.additionalPaymentOptions
config.cardScanningEnabled = true
self.country = settings.country
self.paymentCurrency = settings.currency
self.theme = settings.theme
MyAPIClient.sharedClient.baseURLString = self.backendBaseURL
let paymentContext = STPPaymentContext(customerContext: customerContext, configuration: config, theme: settings.theme)
self.paymentContext = STPPaymentContext(customerContext: customerContext)
super.init(nibName: nil, bundle: nil)
self.paymentContext.delegate = self
self.paymentContext.hostViewController = self
self.paymentContext.paymentAmount = 5000 // This is in cents, i.e. $50 USD
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
I apologize for the long lines of code but I am really running into a brick wall. Why isnt the backend creating the ephemeralKey for customers?
Two things are jumping out at me:
You’ve written a callable type function (using onCall) but you’re
trying to call it with a normal HTTP request. These functions need to
be called with Firebase’s client library
(https://firebase.google.com/docs/functions/callable#call_the_function).
This stack overflow answer provides some great links about this:
Firebase Cloud Function to delete user.
Your firebase function is parsing stripe_version and customer_id from
data, but your request is only sending api_version. Where in your
code are you sending stripe_version and customer_id?

Expiration of CachedURLResponse not working

I was trying to set custom headers for 'Cache-Control' to achieve a cache at client side(server side has 'Cache-Control: no-cache'). Trying to achieve following two major things.
Response of some of the end points should be cached in memory and
should have an expiration(user defined)
Once expiration time is over, then app should ignore the cache and should fetch data from server.
I followed this link and was able to achieve first target but somehow even after expiry app is still using cache and not triggering any API calls. Not sure if 'max-age' set in header is ignored by the app. Please guide me if I am missing something here.
Here are the code snippets.
Session Configuration:
let sessionConfiguration: URLSessionConfiguration = URLSessionConfiguration.ephemeral
sessionConfiguration.requestCachePolicy = .returnCacheDataElseLoad
sessionConfiguration.urlCache = .shared
self.currentURLSession = URLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: nil)
Request:
if let urlPath = URL(string: <WEB_API_END_POINT>){
var aRequest = URLRequest(url: urlPath, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 60)
aRequest.addValue("private", forHTTPHeaderField: "Cache-Control")
let aTask = self.currentURLSession.dataTask(with: aRequest)
aTask.resume()
}
Caching logic:
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, completionHandler: #escaping (CachedURLResponse?) -> Void) {
if proposedResponse.response.url?.path.contains("/employees") == true {
let updatedResponse = proposedResponse.response(withExpirationDuration: 60)
completionHandler(updatedResponse)
} else {
completionHandler(proposedResponse)
}
}
CachedURLResponse Extension:
extension CachedURLResponse {
func response(withExpirationDuration duration: Int) -> CachedURLResponse {
var cachedResponse = self
if let httpResponse = cachedResponse.response as? HTTPURLResponse, var headers = httpResponse.allHeaderFields as? [String : String], let url = httpResponse.url{
headers["Cache-Control"] = "max-age=\(duration)"
headers.removeValue(forKey: "Expires")
headers.removeValue(forKey: "s-maxage")
if let newResponse = HTTPURLResponse(url: url, statusCode: httpResponse.statusCode, httpVersion: "HTTP/1.1", headerFields: headers) {
cachedResponse = CachedURLResponse(response: newResponse, data: cachedResponse.data, userInfo: headers, storagePolicy: .allowedInMemoryOnly)
}
}
return cachedResponse
}
}
Was able to fix it at my own. Still sharing the answer in case if helps someone else in need.
Added 'Cache-Control' response header in server response and there we have 'max-age: 60', which indicates that response can be valid till 60 seconds only. So till 60 seconds, app will cache that data and after 60 seconds if making another request, this will fetch fresh data from server.
With that, At client side other than defining your cache policy, nothing else is required.
You can do this either for entire URL Session:
let sessionConfiguration: URLSessionConfiguration = URLSessionConfiguration.ephemeral
sessionConfiguration.requestCachePolicy = .useProtocolCachePolicy
sessionConfiguration.urlCache = .shared
self.currentURLSession = URLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: nil)
Or can do it for specific request.
if let urlPath = URL(string: <WEB_API_END_POINT>) {
var aRequest = URLRequest(url: urlPath, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 60)
let aTask = self.currentURLSession.dataTask(with: aRequest)
aTask.resume()
}

I want to consume login API (nodejs) using alamofire on swift

there is my code
#IBAction func btn_login(_ sender: Any) {
// let serverUrl = "http://192.168.1.34:1337/login"
let serverUrl = "http://10.0.0.10:1337/login"
guard let email = emailtf.text, !email.isEmpty else {return}
guard let password = passwordtf.text, !password.isEmpty else {return}
let loginRequest = [
"email" : email,
"password" : password
]
Alamofire.request(serverUrl, method: .post, parameters: loginRequest, encoding: JSONEncoding.default, headers: nil).validate().responseJSON { (responseObject) -> Void in
print(responseObject)
if responseObject.result.isSuccess {
let resJson = JSON(responseObject.result.value!)
print(resJson)
}
if responseObject.result.isFailure {
let error : Error = responseObject.result.error!
print(error)
}
}
}
and im getting this error: Error Domain=NSURLErrorDomain Code=-1001 "The request timed out."
<4548B0C2-A5AD-4F23-8A9D-6A02F84FE3A9>.<1> finished with error [-1001] Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={_kCFStreamErrorCodeKey=-2102, NSUnderlyingError=0x6000009e6b20 {Error Domain=kCFErrorDomainCFNetwork Code=-1001 "(null)" UserInfo={_kCFStreamErrorCodeKey=-2102, _kCFStreamErrorDomainKey=4}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <4548B0C2-A5AD-4F23-8A9D-6A02F84FE3A9>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <4548B0C2-A5AD-4F23-8A9D-6A02F84FE3A9>.<1>"
), NSLocalizedDescription=The request timed out.
Any help please?
Since iOS 10 (maybe even earlier?), you can only make HTTPS requests -- HTTP requests will trigger an error, just like in your code.
So change your server url to:
let serverUrl = "https://10.0.0.10:1337/login"

I want to consume login API (node.js) on my swift code using Alamofire

Im getting an error while im trying to login:
there's my login service, im sure it works so well because I tested it on postman, and I worked with it already on android, and it works perfectly, encrypted password and salt works so well ..
app.post('/login/',(req,res,next)=>{
var post_data = req.body;
//Extract email and password from request
var user_password = post_data.password;
var email = post_data.email;
con.query('SELECT * FROM user where email=?',[email],function (err,result,fields) {
con.on('error', function (err) {
console.log('[MYSQL ERROR]', err);
});
if (result && result.length)
{
var salt = result[0].salt;
var encrypted_password = result[0].encrypted_password;
var hashed_password = checkHashPassword(user_password, salt).passwordHash;
if (encrypted_password == hashed_password)
res.end(JSON.stringify(result[0]))
else
res.end(JSON.stringify('Wrong Password'))
}
else {
res.json('user not exists!!');
}
});
})
and there's my swift code on btn_login action, im using Alamofire, he shows me an error with the url, but i worked with same url on displaying data from my table at it worked fine
#IBAction func btn_login(_ sender: Any) {
let serverUrl = "https://127.0.0.1:1337/login"
guard let email = emailtf.text, !email.isEmpty else {return}
guard let password = passwordtf.text, !password.isEmpty else {return}
let loginRequest = [
"email" : email,
"password" : password
]
Alamofire.request(serverUrl, method: .post, parameters: loginRequest, encoding: JSONEncoding.default, headers: nil)
.validate(statusCode:200..<300)
.responseJSON { response in
switch response.result {
case .success(let value):
print(value)
break
case .failure(let error):
print(error)
break
}
}
}
and im getting this error on xcode console
["Email": "test", "Password": "test"]
2019-11-23 15:41:19.991572+0100 ProjetIOS[19982:799169] Task <F5DB6B8F-2658-4C83-BF64-663B04673CAC>.<1> finished with error [-1002] Error Domain=NSURLErrorDomain Code=-1002 "unsupported URL" UserInfo={NSLocalizedDescription=unsupported URL, NSErrorFailingURLStringKey=localhost:1337/login, NSErrorFailingURLKey=localhost:1337/login, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <F5DB6B8F-2658-4C83-BF64-663B04673CAC>.<1>"
), _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <F5DB6B8F-2658-4C83-BF64-663B04673CAC>.<1>, NSUnderlyingError=0x600002f809f0 {Error Domain=kCFErrorDomainCFNetwork Code=-1002 "(null)"}}
Request failed with error: Error Domain=NSURLErrorDomain Code=-1002 "unsupported URL" UserInfo={NSLocalizedDescription=unsupported URL, NSErrorFailingURLStringKey=localhost:1337/login, NSErrorFailingURLKey=localhost:1337/login, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <F5DB6B8F-2658-4C83-BF64-663B04673CAC>.<1>"
), _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <F5DB6B8F-2658-4C83-BF64-663B04673CAC>.<1>, NSUnderlyingError=0x600002f809f0 {Error Domain=kCFErrorDomainCFNetwork Code=-1002 "(null)"}}
Any help please? Thank you
put your local IP instead of localhost in url .
e.g. http://192.168.1.34:1337/
Also try adding headers in your Alamofire request. Here is an example:
let loginRequest = [
"Email" : self.emailtf.text! as String,
"Password" : self.passwordtf.text! as String
]
let headers = ["Content-Type" : "application/json"]
Alamofire.request(url, method : .post, parameters : loginRequest, encoding :
JSONEncoding.default , headers : headers).responseData { dataResponse in
print(dataResponse.request as Any) // your request
print(dataResponse.response as Any) // your response
}

Resources