Stripe Payment Context Not Connecting to Backend API with Swift 3 & Nodejs - ios

I am using Xcode 8, Swift 3, NodeJS and Atom
Following Stripe's Standard iOS Integration Guide https://stripe.com/docs/mobile/ios/standard I am getting a simple localized error when calling paymentContextDidChange.
This method is called when the user is ready to select a payment, which in turn allows me to call Stripe's prebuilt UI STPPaymentMethodsViewController. But I cannot call the function. Instead I am calling the method didFailToLoadWithError which typically occurs when there is a network connection error, OR there is an issue connecting to my backend.
Here is my client-side code in Xcode:
// MARK: - STPPaymentContextDelegate
func paymentContextDidChange(_ paymentContext: STPPaymentContext) {
if let paymentMethod = paymentContext.selectedPaymentMethod {
self.paymentRow.detail = paymentMethod.label
}
else {
self.paymentRow.detail = "Select Payment"
}
self.totalRow.detail = self.numberFormatter.string(from: NSNumber(value: Float(self.paymentContext.paymentAmount)/100))!
}
func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: #escaping STPErrorBlock) {
MyAPIClient.sharedClient.completeCharge(paymentResult, amount: paymentContext.paymentAmount, completion: completion)
}
func paymentContext(_ paymentContext: STPPaymentContext, didFinishWith status: STPPaymentStatus, error: Error?) {
let title: String
let message: String
switch status {
case .error:
title = "Error ⚠ī¸"
message = error?.localizedDescription ?? "☚ī¸"
case .success:
title = "Success 🎉"
message = "You Have Successfully Made a Charge 🛩"
case .userCancellation:
return
}
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
let action = UIAlertAction(title: "Heard", style: .default, handler: nil)
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
}
func paymentContext(_ paymentContext: STPPaymentContext, didFailToLoadWithError error: Error) {
let alertController = UIAlertController(
title: "Error ⚠ī¸",
message: error.localizedDescription,
preferredStyle: .alert
)
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: { action in
_ = self.navigationController?.popViewController(animated: true)
})
let retry = UIAlertAction(title: "Retry", style: .default, handler: { action in
self.paymentContext.retryLoading()
})
alertController.addAction(cancel)
alertController.addAction(retry)
self.present(alertController, animated: true, completion: nil)
}
Here's my API-Adapter in XCode
class MyAPIClient: NSObject, STPBackendAPIAdapter {
static let sharedClient = MyAPIClient()
let session: URLSession
var baseURLString: String? = nil
var defaultSource: STPCard? = nil
var sources: [STPCard] = []
override init() {
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 5
self.session = URLSession(configuration: configuration)
super.init()
}
func decodeResponse(_ response: URLResponse?, error: NSError?) -> NSError? {
if let httpResponse = response as? HTTPURLResponse
, httpResponse.statusCode != 200 {
return error ?? NSError.networkingError(httpResponse.statusCode)
}
return error
}
func retrieveCustomer(_ completion: #escaping STPCustomerCompletionBlock) {
let userInfo = "Could Not Retrieve Stripe Customer"
guard let key = Stripe.defaultPublishableKey(), !key.contains("#")
else {
let error = NSError(domain: StripeDomain, code: 50, userInfo: [NSLocalizedDescriptionKey: "\(userInfo)"])
completion(nil, error)
print("\(userInfo)")
return
}
guard let baseURLString = baseURLString, let baseURL = URL(string: baseURLString)
else {return}
let path = "/customer"
let url = baseURL.appendingPathComponent(path)
let request = URLRequest.request(url, method: .GET, params: [:])
let task = self.session.dataTask(with: request) { (data, urlResponse, error) in
DispatchQueue.main.async {
let deserializer = STPCustomerDeserializer(data: data, urlResponse: urlResponse, error: error)
if let error = deserializer.error {
completion(nil, error)
return
} else if let customer = deserializer.customer {
completion(customer, nil)
}
}
}
task.resume()
}
func attachSource(toCustomer source: STPSource, completion: #escaping STPErrorBlock) {
guard let baseURLString = baseURLString, let baseURL = URL(string: baseURLString)
else {
if let token = source as? STPToken, let card = token.card {
self.sources.append(card)
self.defaultSource = card
}
completion(nil)
return
}
let path = "/customer/sources"
let url = baseURL.appendingPathComponent(path)
let params = ["source": source.stripeID]
let request = URLRequest.request(url, method: .POST, params: params as [String : AnyObject])
let task = self.session.dataTask(with: request) { (data, urlResponse, error) in
DispatchQueue.main.async {
if let error = self.decodeResponse(urlResponse, error: error as NSError?) {
completion(error)
return
}
completion(nil)
}
}
task.resume()
}
func selectDefaultCustomerSource(_ source: STPSource, completion: #escaping STPErrorBlock) {
guard let baseURLString = baseURLString, let baseURL = URL(string: baseURLString)
else {
if let token = source as? STPToken {
self.defaultSource = token.card
}
completion(nil)
return
}
let path = "/customer/default_source"
let url = baseURL.appendingPathComponent(path)
let params = ["source": source.stripeID]
let request = URLRequest.request(url, method: .POST, params: params as [String : AnyObject])
let task = self.session.dataTask(with: request) { (data, urlResponse, error) in
DispatchQueue.main.async {
if let error = self.decodeResponse(urlResponse, error: error as NSError?) {
completion(error)
return
}
completion(nil)
}
}
task.resume()
}
// MARK: - Complete Charge
func completeCharge(_ result: STPPaymentResult, amount: Int, completion: #escaping STPErrorBlock) {
let userInfo = "Could Not Complete Stripe Customer Charge"
guard let baseURLString = baseURLString, let baseURL = URL(string: baseURLString)
else {
let error = NSError(domain: StripeDomain, code: 50, userInfo: [NSLocalizedDescriptionKey: "\(userInfo)"])
completion(error)
return
}
let path = "charge"
let url = baseURL.appendingPathComponent(path)
let params: [String: Any] = ["source": result.source.stripeID as Any, "amount": amount as Any]
let request = URLRequest.request(url, method: .POST, params: params as [String : AnyObject])
let task = self.session.dataTask(with: request) { (data, urlResponse, error) in
DispatchQueue.main.async {
if let error = self.decodeResponse(urlResponse, error: error as NSError?) {
completion(error)
return
}
completion(nil)
}
}
task.resume()
}
}
Here is my backend code in NodeJS
const stripe = require('stripe')('sk_test_....');
const express = require('express');
const app = express();
// Request Token API
const token = request.body.stripeToken;
// Retrieve Customer API
app.get('/customer', function(request, response) {
var customerId = 'cus_...';
stripe.customers.retrieve(customerId, function(err, customer) {
if (err) {
response.status(402).send('Error retrieving customer.');
console.log('Could not retrieve customer');
} else {
response.json(customer);
console.log('Success retrieving customer');
}
})
});
// Attach a new payment source API
app.post('/customer/sources', function(request, response) {
var customerId = 'cus_...';
stripe.customers.createSource(customerId, {
source: request.body.source
}, function(err, source) {
if (err) {
response.status(402).send('Error attaching source.');
console.log('Could not attach new payment source');
} else {
response.status(200).end();
console.log('Success attaching new payment source');
}
});
});
// Select a new default payment source API
app.post('/customer/default_source', function(request, response) {
var customerId = 'cus_...';
stripe.customers.update(customerId, {
default_source: request.body.defaultSource
}, function(err, customer) {
if (err) {
response.status(402).send('Error setting default source.');
console.log('Could not select default payment source');
} else {
response.status(200).end();
console.log('Success selecting default payment source');
}
});
});
Any help would be greatly appreciated.

Related

How Can i get Charge and how to pass token id to charge in swift (IOS)

How to generate stripe token id, charge id in swift. Please, could anyone help on the generation of stripe payment in swift?
First do stripe payment configuration
let configuration = STPPaymentConfiguration.shared()
configuration.additionalPaymentMethods = .all
configuration.appleMerchantIdentifier = "Your stripe identifier"
configuration.canDeletePaymentMethods = true
configuration.createCardSources = false
let customerContext = STPCustomerContext(keyProvider: MyAPIClient.sharedClient)
paymentMethodViewController = STPPaymentMethodsViewController(configuration: configuration,
theme: STPTheme.init(),
customerContext: customerContext,
delegate: self)
self.navigationController?.pushViewController(controller, animated: true)
Code For ApiClient to generate ephermal key
class MyAPIClient: NSObject, STPEphemeralKeyProvider {
static let sharedClient = MyAPIClient()
func createCustomerKey(withAPIVersion apiVersion: String, completion: #escaping STPJSONResponseCompletionBlock) {
let url = AppConstant.Server.EPHERMAL_KEY
let user = UserDefaultManager.shared.readUser()
let header: HTTPHeaders = ["api_token": user.apiToken ?? "",
"Content-Type": "application/json"]
Alamofire.request(url,
method: .get,
parameters: [
"api_version": apiVersion,
"id": user.id ?? -1
],
headers: header)
.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)
}
}
}
}
Then in delegate method
func paymentMethodsViewController(_ paymentMethodsViewController: STPPaymentMethodsViewController, didSelect paymentMethod: STPPaymentMethod) {
var paymentStripeId: String?
if let source = paymentMethod as? STPSource {
paymentStripeId = source.stripeID
} else if let card = paymentMethod as? STPCard {
self.stpCard = card
}
}
Try out this method. From stripe document.
let cardParams = STPCardParams()
cardParams.number = "4242424242424242"
cardParams.expMonth = 10
cardParams.expYear = 2021
cardParams.cvc = "123"
STPAPIClient.shared().createToken(withCard: cardParams) { (token: STPToken?, error: Error?) in
guard let token = token, error == nil else {
// Present error to user...
return
}
submitTokenToBackend(token, completion: { (error: Error?) in
if let error = error {
// Present error to user...
}
else {
// Continue with payment...
}
})
}

How to Test APIs in Swift 4?

I am new to Unit Testing. I have tested a lot of functions and I understood the concept, now I want to check the APIs. Is it possible? I guess so. This is the API:
func sendRequest(path: String, params: Dictionary<String, Any>, showSpinner: Bool, completionHandler: #escaping (JSON, Error?) -> Void) {
if Constants.IS_SIMULATOR {
print("Path: \(path)")
print("Params: \(params)")
}
if Constants.APP_DEL.reachability?.connection == .none {
completionHandler(JSON.null, NSError(domain: "No internet", code: 4, userInfo: nil))
return
}
UIApplication.shared.isNetworkActivityIndicatorVisible = true
if showSpinner {
HUD.show(.labeledProgress(title: "Loading...", subtitle: "Please wait"))
}
if let jsonData = try? JSONSerialization.data(withJSONObject: params, options: .prettyPrinted) {
let url = NSURL(string: String(format: "%#%#", Constants.TEST_URL, path))!
let request = NSMutableURLRequest(url: url as URL)
request.httpMethod = "POST"
request.httpBody = jsonData
request.timeoutInterval = 120
let task = URLSession.shared.dataTask(with: request as URLRequest){ data, response, error in
DispatchQueue.main.async {
if error != nil {
print(" ........ \(String(describing: error?.localizedDescription))")
UIApplication.shared.isNetworkActivityIndicatorVisible = false
if showSpinner {
HUD.flash(.labeledError(title: "Server issue", subtitle: "Invalid response"), delay: 2.0)
}
completionHandler(JSON.null, NSError(domain: "Invalid response", code: 420, userInfo: nil))
return
}
if (data?.isGzipped)! {
let decompressedData: Data = try! data!.gunzipped()
var json: JSON = JSON.null
do {
json = try JSON(data: decompressedData)
}
catch {
print(error)
}
if Constants.IS_SIMULATOR {
print("Response: \(json)")
}
UIApplication.shared.isNetworkActivityIndicatorVisible = false
if json["status"].int == 200 {
if showSpinner {
HUD.flash(.success, delay: 0.5)
}
completionHandler(json["data"], nil)
}
else if json["status"].int == 202 {
if showSpinner {
HUD.hide()
}
completionHandler(JSON.null, NSError(domain: json["message"].string!, code: json["status"].int!, userInfo: nil))
}
else if json["status"].int == 310 {
if showSpinner {
HUD.hide()
}
completionHandler(json["data"], nil)
}
else if json["status"].int == 403 {
if showSpinner {
HUD.hide()
}
GeneralHelper.sharedInstance.displayAlertMessage(titleStr: "Session expired", messageStr: "Kindly login again.")
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {
let domain = Bundle.main.bundleIdentifier!
UserDefaults.standard.removePersistentDomain(forName: domain)
UserDefaults.standard.synchronize()
Constants.APP_DEL.navC?.popToRootViewController(animated: false)
})
completionHandler(JSON.null, NSError(domain: json["message"].string!, code: json["status"].int!, userInfo: nil))
}
else {
if showSpinner {
HUD.flash(.labeledError(title: "", subtitle: json["message"].string!), delay: 2.0)
}
completionHandler(JSON.null, NSError(domain: json["message"].string!, code: json["status"].int!, userInfo: nil))
}
}
else {
let backToString = String(data: data!, encoding: String.Encoding.utf8) as String?
if Constants.IS_SIMULATOR {
print("Invalid response: \(String(describing: backToString))")
}
UIApplication.shared.isNetworkActivityIndicatorVisible = false
if showSpinner {
HUD.flash(.labeledError(title: "Server issue", subtitle: "Invalid response"), delay: 2.0)
}
completionHandler(JSON.null, NSError(domain: "Invalid response", code: 420, userInfo: nil))
}
}
}
task.resume()
}
}
So, to test this I have done this:
func testAPIWorking() {
params = ["ios_token": "dq6YJkKwEx0:APA91bFeOTfJRFd5G78xMkv3AvjSLA7ey2dJxTZZAtMuuC50CqWILNzNjdgqVpNpDn7R4I0DLoydIVDYKubpGfgfu1bwz1H3VNU4D88ek8PJTAjxrd3CWkW78g0sNv6EZDLlTqUFeNxh", "api_token": "kfRSHL0bVP1fSmxNY3NfEGs8g0ktKCbTsPRRbfarh3a5ISIcZLu3qdK07MJ9H4rJ", "player_id": 8083]
ServiceHelper.sharedInstance.sendRequest(path: "home", params: self.params, showSpinner: false) { (result, error) in
if error != nil {
XCTFail("Fail")
}
else {
}
}
}
I have added a breakpoint at task and it prints the task but then when I try to move to next line, instead of going into Dispatch it takes me out of that and stops at task.resume() and because of this I am not able to test the errors or expected results. Any help?
Here you have a completion handler, the api call in not sync. So you should to wait the result in your test. In Xcode you could use XCTestExpectation.
For example:
func testAPIWorking()
{
let expectation = XCTestExpectation.init(description: "Your expectation")
params = ["ios_token": "dq6YJkKwEx0:APA91bFeOTfJRFd5G78xMkv3AvjSLA7ey2dJxTZZAtMuuC50CqWILNzNjdgqVpNpDn7R4I0DLoydIVDYKubpGfgfu1bwz1H3VNU4D88ek8PJTAjxrd3CWkW78g0sNv6EZDLlTqUFeNxh", "api_token": "kfRSHL0bVP1fSmxNY3NfEGs8g0ktKCbTsPRRbfarh3a5ISIcZLu3qdK07MJ9H4rJ", "player_id": 8083]
ServiceHelper.sharedInstance.sendRequest(path: "home", params: self.params, showSpinner: false) { (result, error) in
if error != nil
{
XCTFail("Fail")
}
// The request is finished, so our expectation
expectation.fulfill()
}
// We ask the unit test to wait our expectation to finish.
self.waitForExpectations(timeout: 20)
}

Cannot figure out why Error is always turning out to be 'nil'

I am creating an Event Manager App, It requires to input valid Event Code to check the Event Details. I am working on the Sign in page where in the user will input the event code. When the page is error free, I tried to clean, build and run the app, insert breakpoints to check the executions of my codes. But seems that I am encountering an issue, where in, whether I input valid code or not, It just loaded and after it loads, It goes back to a clear textfield, no error alerts or not event dispatch the Dashboard Page. I really can't figure out what's wrong with my codes. I am new in swift. I really need hep to fix it. Below are my codes for your reference and image of eventDetails from breakpoint. Thankyou
APIService.swift
typealias JSONDictionary = Dictionary<String, AnyObject>
class APIService: NSObject, URLSessionDataDelegate {
enum Path {
case SubmitEventCode
}
typealias APICallback = ((AnyObject?, NSError?) -> ())
let responseData = NSMutableData()
var statusCode: Int = -1
var callback: APICallback! = nil
var path: Path! = nil
func validatePasscode(eventcode: String!, callback: #escaping APICallback)
{
let url = PASSCODE_CHECKER_URL //https://hopprLab.com/API/events/PasscodeChecker
makeHTTPPostRequest(path: Path.SubmitEventCode, callback: callback, url: url)
}
func connection(_ connection: URLSession, didReceive response: URLResponse){
let httpResponse = response as! HTTPURLResponse
statusCode = httpResponse.statusCode
switch (httpResponse.statusCode) {
case 201, 200, 401:
self.responseData.length = 0
default:
print("ignore")
}
}
func connection(_ connection: URLSession, didReceive data: Data) {
self.responseData.append(data)
}
func connectionDidFinishLoading(_ connection: URLSession) {
let error: NSError? = nil
let json = try? JSONSerialization.jsonObject( with: responseData as Data, options:[]) as AnyObject
if ((data) != nil) {
callback(nil, error)
return
}
switch(statusCode, self.path!) {
case(200, Path.SubmitEventCode):
callback(self.handleValidatePasscode(json: json!) as AnyObject,nil)
default:
//UnknownError
callback(nil, nil)
}
}
func handleAuthError(json: AnyObject) -> NSError {
if let eventObj = json as? JSONDictionary {
//
if let messageObj: AnyObject = eventObj["error"] {
if let message = messageObj as? String {
return NSError(domain: "validatePasscode", code: 200, userInfo: ["error": message])
}
}
}
return NSError(domain: "validatePasscode", code: 200, userInfo: ["error": "unknown auth error"])
}
func handleValidatePasscode(json: AnyObject) -> Event? {
if let eventObj = json as? JSONDictionary{
if let eventid: AnyObject = eventObj["event_id"]{
if let eventJson = eventid as? JSONDictionary {
if let eventpass = Event.init(JSON: eventJson){
return eventpass
}
}
}
}
return nil
}
//private
func makeHTTPPostRequest(path: Path, callback: #escaping APICallback, url: String) {
self.path = path
self.callback = callback
var request = URLRequest(url: NSURL(string: url)! as URL)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "content-type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration)
let dataTask = session.dataTask(with: request, completionHandler: {
(data: Data?, response: URLResponse?, error: Error?) -> Void in
if data != nil {
DispatchQueue.main.async {
callback(nil,nil)
}
}
})
dataTask.resume()
}
}
Event.swift
struct Event {
let id: String
let name: String
let location: String
let startDateTime: Date
let endDateTime: String
let deleteFlag: Bool?
let deleteDateTime: String?
let dateCreated: String?
let hasRaffle: Bool?
let registrationReq: Bool?
let participantCount: Int
let closedFlag: Bool?
let closedDateTime: String?
let reopenFlag: Bool?
let reopenDateTime: String?
init?(JSON: [String: AnyObject]) {
guard let eventID = JSON["event_id"] as? String,
let eventName = JSON["event_name"] as? String,
let eventLocation = JSON["event_location"] as? String,
let startDateTime = JSON["start_datetime"] as? String,
let endDateTime = JSON["end_datetime"] as? String,
let participantCount = JSON["participant_count"] as? Int else {
return nil
}
self.id = eventID
self.name = eventName
self.location = eventLocation
self.endDateTime = endDateTime
self.participantCount = participantCount
validatePasscode Function
func validateEventPasscode() {
//Show Loading
self.view.squareLoading.start(0.0)
let api = APIService()
api.validatePasscode(eventcode: eventCode) { (data, error) in
guard let eventDetails = self.event, error == nil else {
if let networkError = error {
if networkError != error {
_ = SCLAlertView(appearance: appearance).showError("Ooops", subtitle: "Please enter valid event code")
else {
_ = SCLAlertView(appearance: appearance).showError("Network Error", subtitle: "Network Error")
}
}
guard eventDetails.deleteFlag else {
_ = SCLAlertView(appearance: appearance).showError("Ooops!", subTitle: "Please enter a valid event passcode")
self.view.squareLoading.stop(0.0)
return
}
if eventDetails.closedFlag == true && eventDetails.reopenFlag == false {
_ = SCLAlertView(appearance: appearance).showError("Closed Event", subTitle: "Please check the status of your event and try again")
self.view.squareLoading.stop(0.0)
return
}
}
}

IOS Swift - go to another view after getting stats from NURLSESSION

Once the user click the login button, i will call the func LoginClicked and get the status from api:
func LoginClicked(sender: AnyObject)
{
data_request{
(response) -> () in
let arrResponse = response.componentsSeparatedByString("|")
if (arrResponse[2] == "1"){
self.performSegueWithIdentifier("Login", sender: self)
}
else {
let alert = UIAlertController(title: "Login Failed", message: "Invalid Login!", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
}
func data_request(completion : (response:NSString) -> ()){
let txtUI : String = txtUsername!.text!
let txtPWD : String = txtPassword!.text!
let url = NSURL(string: "http://myweb.net/?UI=\(txtUI)&PW=\(txtPWD)")!
let request = NSURLRequest(URL: url)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(request, completionHandler: {
(
let data, let response, let error) in
guard let _:NSData = data, let _:NSURLResponse = response where error == nil else {
print("error")
return
}
let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding)
completion(response : dataString!)
})
task.resume()
}
If success, it will move to another view. Otherwise, show failed alert. it will hit the error BAD_EXECUTION_INSTRUCTION when calling self. in both condition.
After get the hint from #Tj3n, it can do the proper action now.
func data_request(completion : (response:NSString) -> ()){
let txtUI : String = txtUsername!.text!
let txtPWD : String = txtPassword!.text!
let url = NSURL(string: "http://myweb.net/?UI=\(txtUI)&PW=\(txtPWD)")!
let request = NSURLRequest(URL: url)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(request, completionHandler: {
(
let data, let response, let error) in
guard let _:NSData = data, let _:NSURLResponse = response where error == nil else {
print("error")
return
}
dispatch_async(dispatch_get_main_queue(), {
let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding)
//let response = dataString?.componentsSeparatedByString("|")
//print(dataString)
//print(response![2])
let arrResponse = dataString!.componentsSeparatedByString("|")
if (arrResponse[2] == "1"){
self.performSegueWithIdentifier("Login", sender: self)
}
else {
let alert = UIAlertController(title: "Login Failed", message: "Invalid Login!", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
})
let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding)
completion(response : dataString!)
})
task.resume()
}

Calling a func in other swift file which works with web-service returns nothing

In swift 2 When I'm communicating with a web-service and when I write these codes in button action it works fine.
let alert = UIAlertController(title: "Alert", message: "Message", preferredStyle: .Alert)
let ok = UIAlertAction(title: "OK", style: .Default, handler: { (action) -> Void in })
alert.addAction(ok);
let request = NSMutableURLRequest(URL: NSURL(string: "http://www.myaddress.com/web-service/iostest.aspx")!)
request.HTTPMethod = "POST"
var postString = String();
postString = "uid=1";
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
guard error == nil && data != nil else {
alert.title="Error"
alert.message = "Connection error"
dispatch_async(dispatch_get_main_queue()){
self.presentViewController(alert, animated: true, completion: nil)
}
return
}
if let httpStatus = response as? NSHTTPURLResponse where httpStatus.statusCode != 200 {
alert.title="Error"
alert.message = "Server error"
dispatch_async(dispatch_get_main_queue()){
self.presentViewController(alert, animated: true, completion: nil)
}
}
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
alert.title="Info"
alert.message = responseString as? String
dispatch_async(dispatch_get_main_queue()){
self.presentViewController(alert, animated: true, completion: nil)
}
}
task.resume()
As I said this works fine but as I want to do this from different ViewControls as well I have created a swift file which contains a struct and a static func in that struct that returns the the "responseString" so I could alert it in the view control. Something like this:
struct globalClass {
static func sendInfo(url: String, data: String) -> (answer: String, errorCode: Int32) {
var res = String();
var err = Int32();
err = 0;
let request = NSMutableURLRequest(URL: NSURL(string: url)!);
request.HTTPMethod = "POST";
let postString: String = data;
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
guard error == nil && data != nil else {
err = 1;
return;
}
if let httpStatus = response as? NSHTTPURLResponse where httpStatus.statusCode != 200 {
err = 2;
return;
}
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding);
res = (responseString as? String)!;
}
task.resume();
return (res, err);
}
But now when I call this func from my button it shows me an empty alert very fast that it seems like it didn't get anything from web-service and didn't even try too.
I put these in the button action:
#IBAction func btnData(sender: AnyObject) {
let y: String = "uid=1";
let res = globalClass.sendInfo("http://www.myaddress.com/web-service/iostest.aspx", data: y);
let alert = UIAlertController(title: "", message: "", preferredStyle: .Alert);
let OK = UIAlertAction(title: "OK", style: .Default, handler: nil);
alert.addAction(OK);
if (res.errorCode==0) {
alert.title = "Info";
alert.message = res.answer;
} else if (res.errorCode==1) {
alert.title = "Error";
alert.message = "Error connecting to server";
} else {
alert.title = "Error";
alert.message = "Server returned an error";
}
dispatch_async(dispatch_get_main_queue()){
self.presentViewController(alert, animated: true, completion: nil);
};
}
Thanks for the help,
Afshin Mobayen Khiabani
globalClass.sendInfo uses async call - dataTaskWithRequest. The result of the request will be delivered in completion of this method. But you don't wait for that result, instead you try to use sendInfo like a sync function.
To be able to deliver the result from dataTaskWithRequest's completion, put your own completion into sendInfo and invoke this completion (closure) when the result is delivered. An example
struct GlobalClass {
static func sendInfo(url: String, data: String, completion: (answer: String?, errorCode: Int32?) -> Void) {
// you code here which prepares request
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
// you parse the result here
// you deliver the result using closure
completion(string, error)
}
task.resume();
}
}
And an example of usage:
func usage() {
GlobalClass.sendInfo("url", data: "data") { (answer, errorCode) in
// your answer and errorCode here
// handle the result
}
}
static func sendInfo(url: String, data: String, completion: (answer: String, errorCode: Int32) -> ()){
//Your code..
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding);
res = (responseString as? String)!;
completion(answer: res, errorCode: err)
}
task.resume()
}
Then when you call the sendInfo, call like so:
sendInfo(url: "your url", data: "your data") { (result, error) in
//you use your result and error values as u want.
}

Resources