Refresh Bearer Token and resume current API - ios

I need to resume my WebService if my bearer token expire by http code 401 ,
below is my code.
When a 401 error occurs that means the Access Token has expired and I need to send a Refresh Token over to my server to obtain a new Access Token.
After getting a new Access Token I need to redo the previous request that got the 401 error.
Suppose I hit webservice1 and its give 401 http code , then new bearer token request will generate and the same API resume to work. How can I do it ?
import UIKit
import CryptoKit
class SharedWebService: NSObject {
static let sharedApiInstance = SharedWebService()
func generalApiMethod (parameter : NSDictionary ,completion: #escaping ((NSMutableArray?) -> Void))
{
var dashboarddata : NSMutableArray = NSMutableArray()
let urlString = String(format:"URL OF API HERE")
let url = URL(string: urlString)
guard let requestUrl = url else { fatalError() }
var request = URLRequest(url: requestUrl)
request.timeoutInterval = 60
let bearerToken = "current bearer token"
request.setValue(bearerToken, forHTTPHeaderField: "Authorization")
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Accept")
URLSession.shared.dataTask(with: request) {
(data, response, error) in
if let httpResponse = response as? HTTPURLResponse {
print(httpResponse.statusCode)
if httpResponse.statusCode == 401
{
// Refresh bearerToken get here
let bearerToken = self.getBearerTokenDevice() //fetch api to get new bearer token
return
}
}
guard error == nil else {
print(error!.localizedDescription);
DispatchQueue.main.async{
completion(dashboarddata)
}
return
}
guard let jsonData = data else { print("Empty data"); return }
if String(data: jsonData, encoding: String.Encoding.utf8) != nil
{
do {
let responseJSON = try? JSONSerialization.jsonObject(with: jsonData, options: [])
if let responseJSON = responseJSON as? [String: Any] {
//Success case do here reponse return
completion(dashboarddata)
}
}
}
}.resume()
}
func getBearerTokenDevice()-> String
{
//how to handle it
return "New bearer token from api fetch"
}
}

work arround is,
Always call Api at splah which fetches Bearer token from server, it will refresh the token every time user opens the app,
2.1 make Api call Queue to process Api calls (Use generics here)
2.2 if api is successfull, Ok. if not than call a special Api call yo get the token,
2.3 if after fetching the token, get last api from Api Queue and call it..
its just an idea, how i think, i think it will be done, might be different in your case
https://stevenpcurtis.medium.com/use-operationqueue-to-chain-api-calls-in-swift-71eefd6891ef
here is guide to make Api call chain

Related

How can i share post on Linkedin from iOS app

Here is my scenario. I have my valid accessToken, which I have already used to retrieve info from user profile. Below is the steps I have been trying to post using that token. I have been receiving statusCode = 401
linkedinHelper.authorizeSuccess({ [unowned self] (lsToken) -> Void in
self.writeConsoleLine("Login success lsToken: \(lsToken)")
print("\(lsToken.accessToken ?? "")")
let targetURLString = "https://api.linkedin.com/v1/people/~/shares?oauth2_access_token=\(lsToken.accessToken ?? "")&format=json"
let payloadStr: String = "{\"comment\":\"Check out developer.linkedin.com!\",\"visibility\":{\"code\":\"anyone\"}}"
// Initialize a mutable URL request object.
let request = NSMutableURLRequest(url: NSURL(string: targetURLString)! as URL)
// Indicate that this is a GET request.
request.httpMethod = "POST"
request.httpBody = payloadStr.data(using: String.Encoding.utf8)
// Add the access token as an HTTP header field.
request.addValue("Bearer \(lsToken.accessToken ?? "")", forHTTPHeaderField: "Authorization")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("json", forHTTPHeaderField: "x-li-format")
// Make the request.
let task: URLSessionDataTask = URLSession.shared.dataTask(with: request as URLRequest) { (data, response, error) -> Void in
// Get the HTTP status code of the request.
let statusCode = (response as! HTTPURLResponse).statusCode
if statusCode == 201 {
// Convert the received JSON data into a dictionary.
guard ((try? JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers)) as? [String: Any]) != nil else {
print("Not containing JSON")
return
}
print("successfully posted.")
}
}
task.resume()
}, error: { [unowned self] (error) -> Void in
self.writeConsoleLine("Encounter error: \(error.localizedDescription)")
}, cancel: { [unowned self] () -> Void in
self.writeConsoleLine("User Cancelled!")
})
LinkedIn v1 API is no longer supported. You will need to use their v2 APIs: https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/share-on-linkedin?context=linkedin/consumer/context
Your target URL should use the ugcPosts endpoint:
POST https://api.linkedin.com/v2/ugcPosts
In addition to Christopher's point, I'm afraid Linkedin doesn't appear to be processing the queue of people waiting for access to v2 of the API - we requested access 5 months ago, nothing has changed. We were using v1 of the API for 2 years without any issues, then one day they switched us off with no method of getting transferred to the new API.

Sending HTTP POST request with Swift to Discord Webhook results in Response Error

I'm trying to create a URL Request in Swift to send an HTTP POST request to a Discord Webhook, and the request completes in a failure defined as 'responseProblem'. I'm not sure where the response problem is originating from.
Discord should technically be able to accept HTTP requests, and a bunch of research into the issue has led me nowhere. I've worked with Discord webhooks before, but I've never played around with this kind of stuff before, so I'm kinda stuck for what I should do now.
import Foundation
enum APIError:Error {
case responseProblem
case decodingProblem
case encodingProblem
}
struct APIRequest {
let resourceURL: URL
init(endpoint: String) {
let resourceString = "<discord-webhook-url-removed-for-privacy>"
guard let resourceURL = URL(string: resourceString) else {fatalError()}
self.resourceURL = resourceURL
}
func save (_ messageToSave:Message, completion: #escaping(Result<Message, APIError>) -> Void ) {
do {
var urlRequest = URLRequest(url: resourceURL)
urlRequest.httpMethod = "POST"
urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
urlRequest.httpBody = try JSONEncoder().encode(messageToSave)
let dataTask = URLSession.shared.dataTask(with: urlRequest) { data, response, _ in
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200, let
jsonData = data else {
completion(.failure(.responseProblem)) // <<< 'responseProblem' is outputted in console as the error
return
}
do {
let messageData = try JSONDecoder().decode(Message.self, from: jsonData)
completion(.success(messageData))
} catch {
completion(.failure(.decodingProblem))
}
}
dataTask.resume()
} catch {
completion(.failure(.encodingProblem))
}
}
}
When I run this Swift program, I expected it to send a request to the Discord Webhook to send a message into it's designated channel. However, the only error outputted into the console is responseProblem. Please let me know if there is anything further I need to add to get the root cause of the problem (I'm still semi-fresh to Swift, and I normally work with JavaScript, so I'm not sure how to properly debug in Swift and Xcode.)
Swift app is built in iOS 12.2 because Xcode doesn't like this stuff in iOS 13
This is a simplified version of how I post to a Discord webhook with Swift. From your post I can't see how you're converting your custom Message struct into a JSON dictionary, but you need at least the key/value pair for "content" to successfully post a Discord Webhook. And of course the posts can be customized in many other ways (with a custom "username" etc.).
var messageString: String = ""
guard let url = URL(string: "your-full-discord-webhook-url") else { return }
let messageJson: [String: Any] = ["content": messageString]
let jsonData = try? JSONSerialization.data(withJSONObject: messageJson)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "content-type")
request.httpBody = jsonData
let task = URLSession.shared.dataTask(with: request)
task.resume()

How to pass bearer token to make Yelp API call with URLSessoin

U P D A T E D... The function with what works!
I would like to incorporate the yelp api into an app but can't successfully pass my authorization token on the URL string. Do I need to do something to connect the URLRequest to the URLSessoin call and its not using the header? Maybe the key value pairs is wrong? The below function returns:
error = {
code = "TOKEN_MISSING";
description = "An access token must be supplied in order to use this endpoint.";
};
I was able to use postman to get the yelp API call working, but only by clicking the "Header" section on postman and putting in Bearer and then my yelp key. I googled around a bit and found some links that indicate that you can add a header to the URLSession that I assume would work the way postman does but I haven't been able to get it to work.
I know there are some githubs with yelp API repos but I am trying to not install a large set of code that I don't understand into my app, when all I want is the JSON that I can see is coming through on postman. Can anyone help me understand how I would edit code similar to the Here example below so that I can get the Authorization/Bearer that yelp requires?
func getYelp() {
let appSecret = "Bearer <YELP APIKEY>"
let link = "https://api.yelp.com/v3/businesses/search?latitude=37.786882&longitude=-122.399972"
if let url = URL(string: link) {
// Set headers
var request = URLRequest(url: url)
request.setValue("Accept-Language", forHTTPHeaderField: "en-us")
request.setValue(appSecret, forHTTPHeaderField: "Authorization")
print("Attempting to get places around location from Yelp")
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if error != nil {
print(error!)
} else {
if let urlContent = data {
do {
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject // Added "as anyObject" to fix syntax error in Xcode 8 Beta 6
print("Printing all JSON/n/n//n--------------------------")
print(jsonResult)
print("Printing from results/n/n//n--------------------------")
if let description = ((jsonResult["search"] as? NSDictionary)?["context"] as? NSDictionary)?["href"] as? String {
} else {
print("JSON pull failed/n/n//n--------------------------")
}
} catch {
print("JSON Processing Failed/n/n//n--------------------------")
}
}
}
}
task.resume()
} else {
resultLabel.text = "Couldn't get results from Here"
}
}
You're mixing up between the headers and the url, you need to set your headers correctly
if let url = URL(string: "https://places.cit.api.here.com/places/v1/discover/around?at=37.776169%2C-122.421267&app_id=\(app_id)&app_code=\(app_code)") {
var request = URLRequest(url: url)
// Set headers
request.setValue("Accept-Language", forHTTPHeaderField: "en-us")
request.setValue("Authorization", forHTTPHeaderField: "Bearer " + token // Token here)
print("Attempting to get places around location")
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
// ...
Lets say you have an api with "https://google.com" (this is just an example with fake keys)
and an api key that is "ApiKey: 92927839238293d92d98d98d92".
You would then take this information and do this.
let uri = URL(string:"https://google.com")
if let unwrappedURL = uri {
var request = URLRequest(url: unwrappedURL)request.addValue("92927839238293d92d98d98d92", forHTTPHeaderField: "ApiKey")
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
// you should put in error handling code, too
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
// HERE'S WHERE YOUR DATA IS
print(json)
} catch {
print(error.localizedDescription)
}
}
}
dataTask.resume()
}
Please remember that you would replace the google.com with your GET address and the APIKey header with your own api key values.
Also, this will print out all the JSON like in PostMan.
If this works for you, then I also have a link on accessing the JSON Objects.

iOS curl -X POST integration in swift

Hi I want to integrate the Curl POST api in my code I don't have any idea about this could any please guide me how to integrate this in swift language
The below web service call I have integrate in my code, Have tried but didn't get the result
curl -X POST http://stf.rortechnologies.com/api/session.js --data '{"user": {"email":"XXXXXX", "password":"XXXXXX"}}' -H "Content-Type:application/json"
let parameters = ["email":"admin.test#stf.com", "password":"password"]
let header = ["user": parameters]
//create the url with URL
let url = URL(string: "http://stf.rortechnologies.com/api/session.js")! //change the url
//create the session object
let session = URLSession.shared
//now create the URLRequest object using the url object
var request = URLRequest(url: url)
request.httpMethod = "POST" //set http method as POST
do {
request.httpBody = try JSONSerialization.data(withJSONObject: header, options: .prettyPrinted) // pass dictionary to nsdata object and set it as request body
} catch let error {
print(error.localizedDescription)
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
//create dataTask using the session object to send data to the server
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard error == nil else {
return
}
guard let data = data else {
return
}
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
print(json)
// handle json...
}
} catch let error {
print(error.localizedDescription)
}
})
task.resume()
here am getting the null response
App Transport Security (ATS)
You are calling a http url and not a https url. In production always https should be used. This is enforced by iOS.
For testing purposes one can declare exceptions in the info.plist, the documentation can be found here:
https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW33
JSON Encoding/Decoding
In Swift there is a convenient way to encode/decode JSON with JSONEncode/JSONDecoder. A simple solution might look like the one below.
Define Parameter Structs
struct Login: Encodable {
let user: User
}
struct User: Encodable {
let email: String
let password: String
}
Define Return Struct
struct Result: Decodable {
//add expected JSON fields here
}
Rest Call
private func makeRestCall() {
let login = Login(user: User(email: "admin.test#stf.com", password: "password"))
guard let loginJson = try? JSONEncoder().encode(login) else { return }
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
guard let url = URL(string: "<add some valid url here>") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = loginJson
let dataTask = session.dataTask(with: request) { (data, response, error) in
if let response = response as? HTTPURLResponse {
if response.statusCode == 200 {
guard let data = data else {
print ("call failed - no data returned")
return
}
guard let result = try? JSONDecoder().decode(Result.self, from: data) else {
print ("json decoding failed")
return
}
print ("call succesfully returned \(result)")
} else {
print ("call failed with status \(response.statusCode)")
}
} else {
print ("call failed: no http response")
}
}
dataTask.resume()
}
Check in a HTTPS Proxy
To make sure that you send the correct data, you could use a HTTPS proxy software. There it would look like this:

how to send https GET request with oauth

i build project swift with OAuth and i already got Acces Token from my API Login and then i want get data from API JSon with my acces token. so, how to use OAuth get request http method with OAuth and my acces token
i have json parse standar without OAuth
let url = NSURL(string: "https://conversation.8villages.com/1.0/contents/articles?state=published")
let request = NSMutableURLRequest(url: url! as URL)
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request as URLRequest) { data,response,error in
guard error == nil && data != nil else {
print("error", error!)
return
}
let httpStatus = response as? HTTPURLResponse
if httpStatus!.statusCode == 200
{
if data?.count != 0
{
let responString = try! JSONSerialization.jsonObject(with: data!, options: .allowFragments)
print(responString)
}
else{
print("No got data from URL")
}
}
else
{
print("error httpstatus code is ", httpStatus!.statusCode)
}
}
task.resume()
i'm used Librabry https://github.com/OAuthSwift/OAuthSwift
in there i got Signed Request (Readme.md) like this
oauthswift.client.get("https://api.linkedin.com/v1/people/~",
success: { response in
let dataString = response.string
print(dataString)
},
failure: { error in
print(error)
}
)
but im confused how to add my Acces Token, Consumer Key and My Consumer Secret to acces my API JSON
If you have got the access token, it seems you have already used your consumer key and consumer secret and you are authenticated. You need to pass the bearer access token in the authorization header of API get call.
I'm not familiar with OAuthSwift framework but I guess the error is in the following row:
request.addValue("application/json", forHTTPHeaderField: "Authorization")
Maybe it should be:
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue(tokenType + " " + accessToken, forHTTPHeaderField: "Authorization")
Where tokenType for a OAuth Authorization Code grant type always is the string Bearer and accessToken is of course the access token you have got from earlier.
EDIT:
Compare with the Microsoft Azure AD:
GET /v1.0/me/messages
Host: https://graph.microsoft.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUz...

Resources