How to Test APIs in Swift 4? - ios

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)
}

Related

post alamofire not firing

I have been frustrated with this problem. my button not firing when posting with alamofire, but when I test to print the button if it working or not. it is working, what went wrong actually I don't know what is the solution anyway? here I show you my code please help me
#objc func handleSave() {
print("save")
guard let fullName = infos?.fullName else { return }
guard let email = infos?.email else { return }
guard let phoneNumber = infos?.phoneNumber else { return }
guard let city = infos?.pobId else { return }
guard let birth = infos?.dob else { return }
guard let religion = infos?.religionId else { return }
guard let imageName = userDefaults.string(forKey: "profileImage") else { return }
guard let src = userDefaults.string(forKey: "src") else { return }
let parameter: [String: Any] = [
"full_name": fullName,
"email": email,
"pob_id": city,
"dob": birth,
"phone_number": phoneNumber,
"religion_id": religion,
"asset": [
"id": UUID().uuidString,
"filename": imageName,
"content_type": "image/png",
"src": src
]
]
progressHUD.show(in: self.view)
BasicInfoServices.shared.postBasicInfo(parameters: parameter) { (success) in
if success {
self.progressHUD.dismiss(animated: true)
self.presentGAlertOnMainThread(image: #imageLiteral(resourceName: "ic-message-success"), title: "Success".localized(), message: "Success save profile".localized(), completion: nil)
} else {
self.progressHUD.dismiss(animated: true)
self.presentGAlertOnMainThread(image: #imageLiteral(resourceName: "ic-message-failure"), title: "Failed".localized(), message: "Failed to save profile".localized(), completion: nil)
}
}
}
// This is my network services
func postBasicInfo(parameters: [String: Any], completion: #escaping (Bool) -> Void) {
AF.request(API_URL.BASIC_INFO.USER, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: HEADERS, interceptor: nil).responseData(completionHandler: { responseData in
debugPrint(responseData)
if responseData.error == nil {
completion(true)
} else {
completion(false)
print("Error post info")
return
}
guard let response = responseData.response, response.statusCode == 200 else {
completion(false)
print("Failed response")
return
}
guard let data = responseData.data else {
completion(false)
print("Failed to fetch data")
return
}
})
}

How to get data from array using Alamofire to display in CollectionCell?

I am creating an app which requires to pull data from API, the scenario is, I will get the json data below:
[
{
"chargeId": "33fbbbd0-2e33-11e9-a2cb-8a27ecbbcb73",
"chargeDate": "2019-02-12T03:28:44.000",
"vatRate": "NON-VAT",
"taxRate": 0.1,
"policyGroup": "Patient Participation",
"itemDescription": "Ecg at er/icu df",
"scdFlag": 0,
"amounts": null,
"scdDiscounts": 0,
"othDiscounts": 4.54,
"adjustment": 0,
"pfBill": 222.46,
"vatAmount": 0,
"taxableAmount": 11.12,
"merchantDiscount": 0,
"creditedAmount": 211.3,
"chargeAmount": null,
"previousCredits": null
},
{
"chargeId": "5a2cabc1-46c9-11e9-a2cf-863c7cdffd18",
"chargeDate": "2019-03-15T10:24:21.000",
"vatRate": "NON-VAT",
"taxRate": 0.1,
"policyGroup": "Patient Participation",
"itemDescription": "Professional Fees",
"scdFlag": 0,
"amounts": null,
"scdDiscounts": 0,
"othDiscounts": 0,
"adjustment": 0,
"pfBill": 1000,
"vatAmount": 0,
"taxableAmount": 100,
"merchantDiscount": 0,
"creditedAmount": 900,
"chargeAmount": null,
"previousCredits": null
}
]
I did pulled the data successfully by using the Alamofire code below:
typealias getPatientDetailsPerPayoutTaskCompletion = (_ patientDetailsPerPayout: [PatientPayoutDetails]?, _ error: NetworkError?) -> Void
static func getPatientDetailsPerPayout(periodId: Int, doctorNumber: String, parameterName: PatientParameter, hospitalNumber: String, completion: #escaping getPatientDetailsPerPayoutTaskCompletion) {
guard let patientDetailsPerPayoutURL = URL(string: "\(Endpoint.Patient.patientPayoutDetails)?periodId=\(periodId)&doctorNumber=\(doctorNumber)\(parameterName.rawValue)\(hospitalNumber)") else {
completion(nil, .invalidURL)
return
}
let sessionManager = Alamofire.SessionManager.default
sessionManager.session.getAllTasks { (tasks) in
tasks.forEach({ $0.cancel() })
}
Alamofire.request(patientDetailsPerPayoutURL, method: .get, encoding: JSONEncoding.default).responseJSON { (response) in
print(patientDetailsPerPayoutURL)
guard HelperMethods.reachability(responseResult: response.result) else {
completion(nil, .noNetwork)
return
}
guard let statusCode = response.response?.statusCode else {
completion(nil, .noStatusCode)
return
}
switch(statusCode) {
case 200:
guard let jsonData = response.data else {
completion(nil, .invalidJSON)
return
}
let decoder = JSONDecoder()
do {
let patientDetailsPayout = try decoder.decode([PatientPayoutDetails].self, from: jsonData)
completion(patientDetailsPayout, nil)
} catch {
completion(nil, .invalidJSON)
}
case 400: completion(nil, .badRequest)
case 404: completion(nil, .noRecordFound)
default:
print("**UNCAPTURED STATUS CODE FROM (getPatientDetailsPayout)\nSTATUS CODE: \(statusCode)")
completion(nil, .uncapturedStatusCode)
}
}
The JSON Data will display in a CollectionCell, and user will tapped the cell to view the data under one chargedId but unfortunately, when I tapped the cell the all data are pulled instead of one part of the array only. The code below is what I used to pull just part of the array:
typealias getSelectedPatientItemDetailsTaskCompletion = (_ selectedpatient: PatientPaymentDetails?, _ error: NetworkError?) -> Void
static func getPatientItemDetails(periodId: Int, doctorNumber: String, parameterName: PatientParameter, hospitalNumber: String, chargeId: String, completion: #escaping getSelectedPatientItemDetailsTaskCompletion) {
guard let patientDetailsPerPayoutURL = URL(string: "\(Endpoint.Patient.patientPayoutDetails)?periodId=\(periodId)&doctorNumber=\(doctorNumber)\(parameterName.rawValue)\(hospitalNumber)") else {
completion(nil, .invalidURL)
return
}
let sessionManager = Alamofire.SessionManager.default
sessionManager.session.getAllTasks { (tasks) in
tasks.forEach({ $0.cancel() })
}
Alamofire.request(patientDetailsPerPayoutURL, method: .get, encoding: JSONEncoding.default).responseJSON { (response) in
print(patientDetailsPerPayoutURL)
guard HelperMethods.reachability(responseResult: response.result) else {
completion(nil, .noNetwork)
return
}
guard let statusCode = response.response?.statusCode else {
completion(nil, .noStatusCode)
return
}
switch(statusCode) {
case 200:
guard let jsonData = response.data else {
completion(nil, .invalidJSON)
return
}
let decoder = JSONDecoder()
do {
let patientDetailsPayout = try decoder.decode(PatientPaymentDetails.self, from: jsonData)
completion(patientDetailsPayout, nil)
} catch {
completion(nil, .invalidJSON)
}
case 400: completion(nil, .badRequest)
case 404: completion(nil, .noRecordFound)
default:
print("**UNCAPTURED STATUS CODE FROM (getPatientDetailsPayout)\nSTATUS CODE: \(statusCode)")
completion(nil, .uncapturedStatusCode)
}
}
}
}
}
Didselect Function to pull data
switch indexPath.section {
case 0:
self.selectedCardIndex = indexPath
let selectedItem = selectedItemDescription.id
getItemDetails(parameter: .searchByChargedId, from: selectedItem)
let cardController = UserCardViewController.init(nibName: "UserCardViewController", bundle: nil)
present(cardController, animated: true, completion: nil)
default: break
}
}
getItemDetails Function
func getItemDetails(parameter: PatientParameter, from: String) {
APIService.PatientList.getPatientItemDetails(periodId: currentRemittance.periodId, doctorNumber: doctorNumber, parameterName: parameter, hospitalNumber: patient.hospitalNumber!, chargeId: from) { (getItem, error) in
guard let pageItemDescription = getItem, error == nil else {
SVProgressHUD.dismiss()
return
}
switch parameter {
case .selectedByChargedID:
if self.patientPayoutDetails == nil {
self.selectedPatientItemDescription = pageItemDescription
}else {
self.patientPayoutDetails.append(contentsOf: pageItemDescription.chargedId)
}
default: break
}
SVProgressHUD.dismiss()
}
}
Hope you can help me, sorry if I included almost all the code but I just want to show you the flow of my codes. Been working on it for almost 1 week. Thank you.
Both of your functions getPatientItemDetails and getPatientDetailsPerPayout are retrieving data from the same URL:
guard let patientDetailsPerPayoutURL = URL(string: "\(Endpoint.Patient.patientPayoutDetails)?periodId=\(periodId)&doctorNumber=\(doctorNumber)\(parameterName.rawValue)\(hospitalNumber)") else {
You probably have different URL for different endpoints; verify the URL for both methods making sure you use the appropriate ones.

How to return data from task in swift [duplicate]

As you can see, I'm receiving a JSON file, parsing it using SwiftyJSON, and trying to return totalTime, but it won't let me. How do I do this?
func googleDuration(origin: String, destination: String) -> Int{
// do calculations origin and destiantion with google distance matrix api
let originFix = origin.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.LiteralSearch, range: nil);
let destinationFix = destination.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.LiteralSearch, range: nil);
let urlAsString = "https://maps.googleapis.com/maps/api/distancematrix/json?origins="+originFix+"&destinations="+destinationFix;
println(urlAsString);
let url = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
let task = urlSession.dataTaskWithURL(url, completionHandler: {data, response, error -> Void in
if error != nil {
// If there is an error in the web request, print it to the console
println(error.localizedDescription)
}
println("parsing JSON");
let json = JSON(data: data);
if (json["status"].stringValue == "OK") {
if let totalTime = json["rows"][0]["elements"][0]["duration"]["value"].integerValue {
println(totalTime);
}
}
})
task.resume();
}
You should add your own completionHandler closure parameter and call it when the task completes:
func googleDuration(origin: String, destination: String, completionHandler: (Int?, NSError?) -> Void ) -> NSURLSessionTask {
// do calculations origin and destiantion with google distance matrix api
let originFix = origin.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.LiteralSearch, range: nil);
let destinationFix = destination.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.LiteralSearch, range: nil);
let urlAsString = "https://maps.googleapis.com/maps/api/distancematrix/json?origins="+originFix+"&destinations="+destinationFix
println(urlAsString)
let url = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
let task = urlSession.dataTaskWithURL(url) { data, response, error -> Void in
if error != nil {
// If there is an error in the web request, print it to the console
// println(error.localizedDescription)
completionHandler(nil, error)
return
}
//println("parsing JSON");
let json = JSON(data: data)
if (json["status"].stringValue == "OK") {
if let totalTime = json["rows"][0]["elements"][0]["duration"]["value"].integerValue {
// println(totalTime);
completionHandler(totalTime, nil)
return
}
let totalTimeError = NSError(domain: kAppDomain, code: kTotalTimeError, userInfo: nil) // populate this any way you prefer
completionHandler(nil, totalTimeError)
}
let jsonError = NSError(domain: kAppDomain, code: kJsonError, userInfo: nil) // again, populate this as you prefer
completionHandler(nil, jsonError)
}
task.resume()
return task
}
I'd also have this return the NSURLSessionTask in case the caller wants to be able to cancel the task.
Anyway, you'd call this like so:
googleDuration(origin, destination: destination) { totalTime, error in
if let totalTime = totalTime {
// use totalTime here
} else {
// handle error
}
}
Another example:
class func getExchangeRate(#baseCurrency: String, foreignCurrency:String, completion: ((result:Double?) -> Void)!){
let baseURL = kAPIEndPoint
let query = String(baseCurrency)+"_"+String(foreignCurrency)
var finalExchangeRate = 0.0
if let url = NSURL(string: baseURL + query) {
NSURLSession.sharedSession().dataTaskWithURL(url) { data, response, error in
if ((data) != nil) {
let jsonDictionary:NSDictionary = NSJSONSerialization.JSONObjectWithData(data!, options: nil, error: nil) as NSDictionary
if let results = jsonDictionary["results"] as? NSDictionary{
if let queryResults = results[query] as? NSDictionary{
if let exchangeRate = queryResults["val"] as? Double{
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {
dispatch_async(dispatch_get_main_queue()) {
completion(result: exchangeRate)
}
}
}
}
}
}
else {
completion(result: nil)
}
}.resume()
}
}
Call:
Currency.getExchangeRate(baseCurrency: "USD", foreignCurrency: "EUR") { (result) -> Void in
if let exchangeValue = result {
print(exchangeValue)
}
}
Another example:
func getJason(url: NSURL, completionHandler: (String?, NSError?) -> Void ) -> NSURLSessionTask {
var finalData: String!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in
if error != nil{
completionHandler(nil, error)
return
}
else{
if let urlContent = data{
do{
let jsonData = try NSJSONSerialization.JSONObjectWithData(urlContent, options: NSJSONReadingOptions.MutableContainers)
if let ip = jsonData["ip"]{
finalData = ip as? String
completionHandler(finalData, nil)
return
}
}catch{
print("EMPTY")
}
}
}
}
task.resume()
return task
}
Then i called it in the viewDidLoad
getJason(url) { (ipAddress, error) -> Void in
if error != nil{
print(error)
}
else{
if let ip = ipAddress{ //To get rid of optional
self.ipLabelDisplay.text = "Your Ip Address is: \(ip)"
}
}
}

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

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.

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