How to read HttpResponseMessage in IOS from web API? - ios

We have asp.net Web API developed. In Web API controllers there are the Filters which generating HttpResponseMessage as response. These API's are consuming by IOS device application. All actions in API are returning HttpResponseMessage. If Filter executes without any error then execution of action performing properly and getting HttpResponseMessage in IOS code. But if any exceptional case in filter happens say token is invalid then HttpResponseMessage is not able to fetch in IOS. Why should this happening ?
Tested in Fiddler and in REST Client, I can see the HttpResponseMessage generated from action as well as from Filters (if fails). IOS getting nil response if Filter fails. Any suggestion? (I am not IOS guy, but IOS team unable to resolving this.)
Update:
let postDataTask : NSURLSessionDataTask = session.dataTaskWithRequest(request) { (data, responce, error) -> Void in
if (error == nil)
{
let strData = NSString(data: data!, encoding: NSUTF8StringEncoding)
let _ : NSError?
var json : NSDictionary!
do
{
json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves) as? NSDictionary
}
catch _ as NSError
{}
var issucceeded : Bool = false
print("Responce - \(json)")
if(json != nil)
{
issucceeded = true
// if((json.valueForKey("RESPONSE")) != nil)
// {
// if(json.valueForKey("RESPONSE") as! NSString == "Success")
// {
// issucceeded = true
// }
// }
postCompleted(succeeded: issucceeded, responseDict: json)
}
else // Responce is not in proper JSON format
{
postCompleted(succeeded: false, responseDict: ["HasErrors":"1","ErrorMessage":k_Alert_24,"TryAgain":"True"])
}
}
else
{
postCompleted(succeeded: false, responseDict: ["HasErrors":"1","ErrorMessage":k_Alert_24,"TryAgain":"True"])
}
}
postDataTask.resume()

Related

How to successfully pass credit card information to heroku using SquareAPI? Getting NSCocoaErrorDomain issue code 3840

I'm trying to create a iOS app that processes credit card payments using the Square API.
I'm using a Heroku server as the backend.
My issue is when I try to process the credit card payment using the Card Entry view controller. I'm getting a NSCocoaErrorDomain issue code 3840 saying the "JSON text did not start with array or object and option to allow fragments not set."
Here is the snippet of code I used to set up the card entry form:
let theme = SQIPTheme()
theme.errorColor = .red
theme.tintColor = Color.primaryAction
theme.keyboardAppearance = .light
theme.messageColor = Color.descriptionFont
theme.saveButtonTitle = "Pay"
let cardEntryForm = SQIPCardEntryViewController(theme: theme)
return cardEntryForm
And here is the snippet of code I used to process the payment:
ChargeApi.processPayment(cardDetails.nonce) { (transactionID, errorDescription) in
guard let errorDescription = errorDescription else {
// No error occured, we successfully charged
completionHandler(nil)
return
}
}
Also here is the ChargeApi (code that is doing the processing of the payment):
class ChargeApi {
static public func processPayment(_ nonce: String, completion: #escaping (String?, String?) -> Void) {
let url = URL(string: Constants.Square.CHARGE_URL)!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let json = ["nonce": nonce]
let httpBody = try? JSONSerialization.data(withJSONObject: json)
print(json)
request.addValue("Application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = httpBody
URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error as NSError?{
if error.domain == NSURLErrorDomain {
DispatchQueue.main.async {
completion("", "Could not contact host")
}
} else {
DispatchQueue.main.async {
completion("", "Something went wrong")
}
}
} else if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
DispatchQueue.main.async {
completion("success", nil)
}
} else {
DispatchQueue.main.async {
completion("", json["errorMessage"] as? String)
}
}
} catch {
DispatchQueue.main.async {
completion("", "Failure")
}
}
}
}.resume()
}
I believe the way it works, is that after submission the credit card info is wrapped up in a value called a "nonce" and it is sent to the Sqaure API to be processed.
The API looks for this "nonce" in the following json format ["nonce": (some nonce value)].
However after successfully following the steps in the InAppPaymentsSample example:
https://developer.squareup.com/docs/in-app-payments-sdk/quick-start/step-2
I'm trying to something similar with my test app as far as talking to the Heroku server and sending test card information to the server, however I'm getting this NSCocoaErrorDomain issue.
Please help.

swift JSON login REST with post and get response example

It's my first experience with REST in iOS development with swift. I couldn't find any working or straight (simple) example for doing what i need here.
I have a login backend (https://myaddress.com/rest/login), where I need to pass 2 params: login and password. When I pass good values (user exists in database) I get 2 variables as a result: token (string) and firstLogin (bool). So when I get those values I know that login is successful and I can log in into my app.
So I am begging you for an example (just a simple function) of how to achieve that. If I get working code example I will know how to use it for other rest services in my app. I tried many solutions from tutorials I found, but any of them was working for me.. So to not waste my time searching I would like someone experienced to show me the way to achieve that.
I am not sure if Alamofire is so good to use, I know that swift 4 has it's own build neetwork services and to work with json. Any solution that works would be great.
Also, side question - if I would prefer to use Alamofire, do I need to use swiftyJSON also? Or it's just for parsing?
You can use URLSession if you don't like to import Alamofire in your Project to perform a simple task.
here are some method : GET, POST, DELETE METHODS and tutorial
GET METHOD
func makeGetCall() {
// Set up the URL request
let todoEndpoint: String = "https://jsonplaceholder.typicode.com/todos/1"
guard let url = URL(string: todoEndpoint) else {
print("Error: cannot create URL")
return
}
let urlRequest = URLRequest(url: url)
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// make the request
let task = session.dataTask(with: urlRequest) {
(data, response, error) in
// check for any errors
guard error == nil else {
print("error calling GET on /todos/1")
print(error!)
return
}
// make sure we got data
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let todo = try JSONSerialization.jsonObject(with: responseData, options: [])
as? [String: Any] else {
print("error trying to convert data to JSON")
return
}
// now we have the todo
// let's just print it to prove we can access it
print("The todo is: " + todo.description)
// the todo object is a dictionary
// so we just access the title using the "title" key
// so check for a title and print it if we have one
guard let todoTitle = todo["title"] as? String else {
print("Could not get todo title from JSON")
return
}
print("The title is: " + todoTitle)
} catch {
print("error trying to convert data to JSON")
return
}
}
task.resume()
}
POST METHOD
func makePostCall() {
let todosEndpoint: String = "https://jsonplaceholder.typicode.com/todos"
guard let todosURL = URL(string: todosEndpoint) else {
print("Error: cannot create URL")
return
}
var todosUrlRequest = URLRequest(url: todosURL)
todosUrlRequest.httpMethod = "POST"
let newTodo: [String: Any] = ["title": "My First todo", "completed": false, "userId": 1]
let jsonTodo: Data
do {
jsonTodo = try JSONSerialization.data(withJSONObject: newTodo, options: [])
todosUrlRequest.httpBody = jsonTodo
} catch {
print("Error: cannot create JSON from todo")
return
}
let session = URLSession.shared
let task = session.dataTask(with: todosUrlRequest) {
(data, response, error) in
guard error == nil else {
print("error calling POST on /todos/1")
print(error!)
return
}
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let receivedTodo = try JSONSerialization.jsonObject(with: responseData,
options: []) as? [String: Any] else {
print("Could not get JSON from responseData as dictionary")
return
}
print("The todo is: " + receivedTodo.description)
guard let todoID = receivedTodo["id"] as? Int else {
print("Could not get todoID as int from JSON")
return
}
print("The ID is: \(todoID)")
} catch {
print("error parsing response from POST on /todos")
return
}
}
task.resume()
}
DELETE METHOD
func makeDeleteCall() {
let firstTodoEndpoint: String = "https://jsonplaceholder.typicode.com/todos/1"
var firstTodoUrlRequest = URLRequest(url: URL(string: firstTodoEndpoint)!)
firstTodoUrlRequest.httpMethod = "DELETE"
let session = URLSession.shared
let task = session.dataTask(with: firstTodoUrlRequest) {
(data, response, error) in
guard let _ = data else {
print("error calling DELETE on /todos/1")
return
}
print("DELETE ok")
}
task.resume()
}
Thanks #MAhipal Singh for you answer. I'll post here example with Alamafire that I used so it's all in one stack question. It's easier than I though, solutions I tried to use before were not working cause I had problems with pinning certificate about I forgot..
func loginRest(login:String, password:String, deviceId:String){
let urlStr = restServices.REST_MAIN_URL + restServices.REST_LOGIN
let params = ["login":login, "password":password, "deviceId":deviceId]
let paramsJson = try! JSONSerialization.data(withJSONObject: params)
var headers: HTTPHeaders = ["Content-Type": "application/json"]
Alamofire.request(urlStr, method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers).responseJSON { (response) in
switch response.result {
case .success:
print("SUKCES with \(response)")
case .failure(let error):
print("ERROR with '\(error)")
}
}
If the post is proper the response is (console print):
SUKCES with SUCCESS: {
firstLogin = 1;
token = "dfkafjkfdsakfadsjfksjkfaadjfkjdfkjfskjfdkafjakfjakfjsafksjdafjy878328hjh";
}

How to get the value of the variable that is assigned in a closure (Swift)

I'm using the Twitter REST API in Swift, and I am trying to get the value of a variable that is assigned inside of a Twitter Request closure, so that I can use that value outside of the closure.
I acquired this code from the Twitter REST API tutorial for Swift, located at: https://dev.twitter.com/twitterkit/ios/access-rest-api
func jsonAvailable() -> Bool {
// Swift
let client = TWTRAPIClient()
let statusesShowEndpoint = "https://api.twitter.com/1.1/statuses/show.json"
let params = ["id": "20"]
var clientError : NSError?
var jsonAvailable: Bool = false
let request = client.urlRequest(withMethod: "GET", url:
statusesShowEndpoint, parameters: params, error: &clientError)
client.sendTwitterRequest(request) { (response, data, connectionError)-> Void in
if connectionError != nil {
print("Error: \(connectionError)")
}
do {
let json = try JSONSerialization.jsonObject(with: data!, options: [])
print("json: \(json)")
jsonAvailable = true
} catch let jsonError as NSError {
print("json error: \(jsonError.localizedDescription)")
}
print("Value of jsonAvailable: \(jsonAvailable)")
return jsonAvailable
//always returns false, even if it is changed to true inside of the closure
}
In the last line, jsonAvailable is always false, even when it is changed to true inside of the closure. How can I get the value of jsonAvailable at the end of the function, even as it is modified inside of the sendTwitterRequest closure?
I have tried writing this closure in a separate function and then calling the function to get the value, but because it is a custom closure that requires the client to be called by "sendTwitterRequest" I have found it difficult to pass all these required parameters to fit the API.
Thanks for the help!
Your closure is async. What happens is that you go through all the function body before sendTwitterRequest assigns true to jsonAvailable, resulting in jsonAvailable being false. What you need to do is having a callback instead, providing the json status if you'd like (or the json itself as a nillable object).
EDIT: You could have something like this
func jsonAvailable(callback: ((_ isJsonAvailable: Bool) -> Void)) {
client.sendTwitterRequest(request) { (response, data, connectionError)-> Void in {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: [])
print("json: \(json)")
callback(true)
} catch let jsonError as NSError {
print("json error: \(jsonError.localizedDescription)")
callback(false)
}
}
}
jsonAvailable(callback: { (_ isJsonAvailable: Bool) in
print(isJsonAvailable)
})

trying to parse json get error: use of undeclared type 'Foundation'

I have this strange error when I try to parse JSON. I do an api call via an async call, to get cities slug, with a completion handler:
//Load cities slug via api call
let apiCall : webApi = webApi()
apiCall.loadCitySlugs(){(success) in
//Slug loaded in background
//Call google api to compare the slug
apiCall.compareSlugFromGoogleApi()
In the function compareSlugFromGoogleApi() there are some configuration things like the url of the async call, the post params, so nothing really relevant except the async call function:
/**
Simple async call. Returns json NSMutableArray as AnyObject
*/
func asyncCall(url : String, completed : complHandlerAsyncCall)/* -> AnyObject*/{
//Set async call params
let request = NSMutableURLRequest(URL: NSURL(string: url)!)
request.HTTPMethod = "POST"
request.HTTPBody = postParam.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
guard error == nil && data != nil else {
// check for fundamental networking error
print("error=\(error)")
return
}
if let httpStatus = response as? NSHTTPURLResponse where httpStatus.statusCode != 200 {
// check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
asyncJson = responseString?.parseJSONString as! NSMutableArray
flagAsyncCall = true // true if download succeed,false otherwise
completed(success: flagAsyncCall!)
}
task.resume()
//return json! as AnyObject
}
In the async call function, I call to an extension parseJSONString that return a json object easy to parse:
extension NSString
{
var parseJSONString: AnyObject?
{
let data = self.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
if let jsonData = data
{
// Will return an object or nil if JSON decoding fails
do
{
let message = try NSJSONSerialization.JSONObjectWithData(jsonData, options:.MutableContainers)
if let jsonResult = message as? NSMutableArray
{
return jsonResult //Will return the json array output
}
else
{
return nil
}
}
catch let error as NSError
{
print("An error occurred: \(error)")
return nil
}
}
else
{
// Lossless conversion of the string was not possible
return nil
}
}
}
And here is where I'm having a problem with the Google call. Everything looks more or less ok, but it returns a nil value from the extension and then, back in the async call, it throws me this error:
still the response object is not empty:
It throws an error because parseJSONString returns nil, and when you unwrap that ! it crashes. You should check if parseJSONString returns non nil. if let parsed = parseJSONString { do something }
Use NSDictionary instead of NSMutableArray.

Get ACAccount after login with Twitter SDK from Fabric Swift

I need a way to get ACAccount which is created, or taken by Twitter SDK provided by Fabric while login with:
Twitter.sharedInstance().logInWithCompletion { (session: TWTRSession?, error: NSError?) -> Void in
if let userSession = session {
}
else {
}
}
I need this account, to use while posting in background with SLRequest. Without account is not working.
If you are already using Twitter SDK, no need to deal with ACAccount anymore.
Just follow their instructions, and you can build a request manually:
Build TW Request manually
Here is that code updated for Swift 2:
if let userID = Twitter.sharedInstance().sessionStore.session()?.userID {
let client = TWTRAPIClient(userID: userID)
let statusesShowEndpoint = "https://api.twitter.com/1.1/statuses/update.json"
var clientError : NSError?
let request = client.URLRequestWithMethod("POST", URL: statusesShowEndpoint, parameters: dictMessage, error: &clientError)
if let err = clientError {
print("Error: \(err)")
}
else {
client.sendTwitterRequest(request) { (response, data, connectionError) -> Void in
if (connectionError == nil) {
do {
if let dictTWData = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.AllowFragments) as? [String: AnyObject] {
print("\(dictTWData)")
}
}
catch _ as NSError {
}
catch {
fatalError()
}
}
else {
print("Error: \(connectionError)")
}
}
}
}

Resources