Make a Get Request api to a JWT enabled endpoint - swift

I am trying to make a get request to a JWT enabled API but I am getting my result in bytes instead of objects
after the user logs in it is expected to go the next page where it lists user projects, at the moment I've created a button which when clicked, should send a Get Request with the JWT token received from the API function below.
I have tried:
func getUserProject(token: String, completion: #escaping (Result<[Project], NetworkError>) -> Void) {
guard let url = URL(string: "https://api.t.io/api/manage/user/projects?archived=false&logic=and&page=0&size=10") else {
completion(.failure(.invalidURL))
print("unable to connect URL🆘")
return
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data, error == nil else {
completion(.failure(.noData))
print("Bad🥺")
return
}
guard let Projects = try? JSONDecoder().decode([Project].self, from: data) else {
completion(.failure(.decodingError))
print(data)
print("Error Decoding Data🥵")
return
}
completion(.success(Projects))
}
task.resume()
}
This is the error I am getting from my logs:
this is my project model:
let createdBy: String
let createdDate: EdDate
let lastModifiedBy: String
let lastModifiedDate: EdDate
let id: Int
let name: String
let customID: JSONNull?
let shared: Bool
let salt: JSONNull?
let isCOMRequestAutomatic: Bool
let client: Client
let company: Company
let projectKeywords: [JSONAny]
let projectStartDate, projectEndDate: JSONNull?
enum CodingKeys: String, CodingKey {
case createdBy, createdDate, lastModifiedBy, lastModifiedDate, id, name, status
case imageURL
case customID
case shared, salt
case isCOMRequestAutomatic
case client, company, projectKeywords, projectStartDate, projectEndDate
}
} ```

Related

URLRequest Error "The given data was not valid JSON."

Trying to make a POST request with headers and params
Codeable code:
struct WelcomeMessage: Codable {
let receivedMessages: [ReceivedMessage]
}
// MARK: - ReceivedMessage
struct ReceivedMessage: Codable, Identifiable {
let ackID: String
let message: Message
let id = UUID()
enum CodingKeys: String, CodingKey {
case ackID
case message
}
}
// MARK: - Message
struct Message: Codable {
let data, messageID, publishTime: String
enum CodingKeys: String, CodingKey {
case data
case messageID
case publishTime
}
}
Service code:
class GetMessages: ObservableObject {
private var project_id: String = "redacted"
private var project_name: String = "redacted"
#Published var messages = [ReceivedMessage]()
func getMessages() {
guard let url = URL(string: "https://pubsub.googleapis.com/v1/projects\(project_id)/subscriptions\(project_name):pull") else {return}
var request = URLRequest(url: url)
let parameters : [String:Any] = [
"returnImmediately": false,
"maxMessages": 10]
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("Bearer ya29.redacted", forHTTPHeaderField: "Authorization")
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
return
}
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard error == nil else {print(error!.localizedDescription); return }
// guard let data = data else {print("empty data"); return }
let theData = try! JSONDecoder().decode(WelcomeMessage.self, from: data!)
print(theData)
DispatchQueue.main.async {
self.messages = theData.receivedMessages
}
}
.resume()
}
}
The response to the request should return some JSON data that looks like:
{
"receivedMessages": [
{
"ackId": "UdfdsfdsfdsfdsfdgfhgfjJHGkjkjhKgjhgjFhgfDFgfdgDFGDFdfgFDGfd",
"message": {
"data": "//BASE-64 ENCODED STRING HERE",
"messageId": "4130086024457484",
"publishTime": "2022-02-16T15:03:49.372Z"
}
}
]
}
Error message as above, not sure why it's saying the data is not valid JSON?
Additional opinionated question...Should I just be using AlamoFire for this?
If the json response you show is correct, then in ReceivedMessage change ackID to ackId (note the small "d"),
or use
enum CodingKeys: String, CodingKey {
case ackID = "ackId"
case message
}
Similarly for messageID in Message.

Why doesn't the text from the YELP API display on to my label?

Basically Im receiving reviews for a specific restaurant using the YELP API and I want to display the reviews to my SKLabel. I can see the reviews in my console printing out but when I try to get the text value from my struct it doesn't appear on to the SKLabel. Here is the code Im working with. Thank you!
extension GameScene {
func fetchReviews(id: String, //Required
locale: String, //Optional
completionHandler: #escaping ([Reviews]?, Error) -> Void) {
// MARK: Retrieve venues from Yelp API
let apikey = "API KEY"
/// create URL
let baseURL = "https://api.yelp.com/v3/businesses/\(id)/reviews"
let url = URL(string: baseURL)
print("this is the url for reviews : \(url)")
/// Creating request
var request = URLRequest(url: url!)
request.setValue("Bearer \(apikey)", forHTTPHeaderField: "Authorization")
request.httpMethod = "GET"
URLSession.shared.dataTask(with: request) { (data, response, error) in
if let err = error {
print(err.localizedDescription)
}
do {
let json = try JSONSerialization.jsonObject(with: data!, options: []) as! [String: Any]
print(">>>>>", json, #line, "<<<<<<<<<")
} catch {
print("caught")
}
}.resume()
}
}
//Reviews.swift
struct Reviews {
var text : String?
var locale : String?
var id : String?
var total : Int?
var rating: Int?
}
//GameScene.swift
fetchReviews(id: venue.id!, locale: "en_US") { (response, error) in
for review in response! {
reviewLabel.text = review.text //Doesn't work here
reviewLabel.position = CGPoint(self.size.width / 2, self.size.height / 2)
reviewLabel.fontColor = .white
reviewLabel.fontSize = 20
addChild(reviewLabel)
}
}

How to make an API call with Swift?

So I'm practising trying to make API calls with Swift. The code is as below:
struct Example: Codable {
let userID: String
let ID: String
let title: String
let completed: String
}
func getJson(completion: #escaping (Example)-> ()) {
let urlString = "https://jsonplaceholder.typicode.com/todos/1"
if let url = URL(string: urlString) {
URLSession.shared.dataTask(with: url) {data, res, err in
if let data = data {
let decoder = JSONDecoder()
if let json: Example = try? decoder.decode(Example.self, from: data) {
completion(json)
}
}
}.resume()
}
}
getJson() { (json) in
print(json.ID)
}
However, I am unable to print out anything when getJson is called.
The example API I used is found here.
The tutorial that I used to help me write the code is found here.
Modified your variable name and data type exactly as your API response.
struct Example: Codable {
let userId: Int
let id: Int
let title: String
let completed: Bool
}
func getJson(completion: #escaping (Example)-> ()) {
let urlString = "https://jsonplaceholder.typicode.com/todos/1"
if let url = URL(string: urlString) {
URLSession.shared.dataTask(with: url) {data, res, err in
if let data = data {
let decoder = JSONDecoder()
do {
let json: Example = try! decoder.decode(Example.self, from: data)
completion(json)
}catch let error {
print(error.localizedDescription)
}
}
}.resume()
}
}
getJson() { (json) in
print(json.id)
}
You can also use CodingKey and can change your response during the init period.
struct Example: Codable {
var userID: Int
var ID: Int
var title: String
var completed: Bool
enum CodingKeys: String, CodingKey {
case userID = "userId"
case ID = "id"
case title = "title"
case completed = "completed"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
userID = try values.decode(Int.self, forKey: .userID)
ID = try values.decode(Int.self, forKey: .ID)
title = try values.decode(String.self, forKey: .title)
completed = try values.decode(Bool.self, forKey: .completed)
title = "Title: \(title)"
}
}
func getJson(completion: #escaping (Example)-> ()) {
let urlString = "https://jsonplaceholder.typicode.com/todos/1"
if let url = URL(string: urlString) {
URLSession.shared.dataTask(with: url) {data, res, err in
if let data = data {
do {
let json: Example = try JSONDecoder().decode(Example.self, from: data)
completion(json)
}catch let error {
print(error.localizedDescription)
}
}
}.resume()
}
}
getJson() { (json) in
print("ID: \(json.ID) \(json.title)")
}
import UIKit
class ApiManager: NSObject {
static func postApiRequest(url: String,parameters:[String: Any],completion: #escaping([String: Any])-> Void) {
let session = URLSession.shared
let request = NSMutableURLRequest(url: NSURL(string: url)! as URL)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
do{
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: JSONSerialization.WritingOptions())
let task = session.dataTask(with: request as URLRequest as URLRequest, completionHandler: {(data, response, error) in
if let response = response {
let nsHTTPResponse = response as! HTTPURLResponse
let statusCode = nsHTTPResponse.statusCode
print ("status code = \(statusCode)")
}
if let error = error {
print ("\(error)")
}
if let data = data {
do{
let jsonResponse = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions())
print ("data = \(jsonResponse)")
}catch _ {
print ("OOps not good JSON formatted response")
}
}
})
task.resume()
}catch _ {
print ("Oops something happened buddy")
}
}
}

Error while doing phone number verification using twilio in swift

I want to verify phone number by getting one time password. But I am getting some error. Please look into the code bellow and help me to resolve it. I am using Twilio for mobile verification. And Alamofire for API request. But the error I am getting like:-
Authentication Error - No credentials provided.
The data couldn’t be read because it isn’t in the correct format
My code is:-
Here is my model class: -
...struct SendVerificationCode: Codable {
let status: String?
let payee: String?
let dateUpdated: Date?
let sendCodeAttempts: [SendCodeAttempt]?
let accountSid, to: String?
let amount: Int?
let valid: Bool?
let lookup: Lookup?
let url: String?
let sid: String?
let dateCreated: Date?
let serviceSid, channel: String?
enum CodingKeys: String, CodingKey {
case status, payee
case dateUpdated = "date_updated"
case sendCodeAttempts = "send_code_attempts"
case accountSid = "account_sid"
case to, amount, valid, lookup, url, sid
case dateCreated = "date_created"
case serviceSid = "service_sid"
case channel
}
}
struct Lookup: Codable {
let carrier: Carrier?
}
struct Carrier: Codable {
let mobileCountryCode, type: String?
let errorCode: String?
let mobileNetworkCode, name: String?
enum CodingKeys: String, CodingKey {
case mobileCountryCode = "mobile_country_code"
case type
case errorCode = "error_code"
case mobileNetworkCode = "mobile_network_code"
case name
}
}
struct SendCodeAttempt: Codable {
let channel, time: String?
}...
Api Request:-
...func sendcode(mobileWithCode: String, completion: #escaping sendTwillioVerificationCodeCompletion) {
let url = URL(string: SEND_TWILIO_VERIFICATION_CODE)
var urlRequest = URLRequest(url: url!)
urlRequest.httpMethod = HTTPMethod.post.rawValue
urlRequest.addValue(userNameData, forHTTPHeaderField: "Username")
urlRequest.addValue(PasswordData, forHTTPHeaderField: "Password")
urlRequest.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
Alamofire.request(urlRequest).responseJSON { (response) in
if let error = response.result.error {
debugPrint(error.localizedDescription)
completion(nil)
return
}
guard let data = response.data else { return completion(nil)}
Common.sharedInstance().printRequestOutput(data: data)
let jsonDecoder = JSONDecoder()
do {
let clear = try jsonDecoder.decode(SendVerificationCode.self, from: data)
completion(clear)
} catch {
debugPrint(error.localizedDescription)
completion(nil)
}
}
}...
But i am getting error:-
{"code": 20003, "detail": "Your AccountSid or AuthToken was incorrect.", "message": "Authentication Error - No credentials provided", "more_info": "https://www.twilio.com/docs/errors/20003", "status": 401}
"The data couldn’t be read because it isn’t in the correct format."
Also i have tried the following code:-
import Foundation
semaphore = DispatchSemaphore (value: 0)
let parameters = "To=+919778882332&Channel=sms"
let postData = parameters.data(using: .utf8)
var request = URLRequest(url: URL(string: myUrl)!,timeoutInterval: Double.infinity)
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.addValue(requestData, forHTTPHeaderField: "Authorization")
request.httpMethod = "POST"
request.httpBody = postData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
print(String(describing: error))
return
}
print(String(data: data, encoding: .utf8)!)
semaphore.signal()
}
task.resume()
semaphore.wait()
But i am getting error like
"Invalid parameter"
Twilio developer evangelist here.
It looks as though your code is trying to call the Twilio API directly from the device and that you weren't setting your Account SID or Auth Token in that.
The issue here is that you should not store or access your auth token from within your application. That would make your account sid and auth token vulnerable to be stolen and then used to abuse your account.
Instead, you should create a server side application that talks to the Twilio API and then call that from your application.
As Jamil pointed out, there is a blog post you can follow on performing phone verification in iOS with Twilio Verify and Swift and I recommend you go through that. It includes an example server side application to call the Twilio Verify API built in Python, but you could build your own too.
Here is sample code:
import UIKit
class ViewController: UIViewController {
static let path = Bundle.main.path(forResource: "Config", ofType: "plist")
static let config = NSDictionary(contentsOfFile: path!)
private static let baseURLString = config!["serverUrl"] as! String
#IBOutlet var countryCodeField: UITextField! = UITextField()
#IBOutlet var phoneNumberField: UITextField! = UITextField()
#IBAction func sendVerification() {
if let phoneNumber = phoneNumberField.text,
let countryCode = countryCodeField.text {
ViewController.sendVerificationCode(countryCode, phoneNumber)
}
}
static func sendVerificationCode(_ countryCode: String, _ phoneNumber: String) {
let parameters = [
"via": "sms",
"country_code": countryCode,
"phone_number": phoneNumber
]
let path = "start"
let method = "POST"
let urlPath = "\(baseURLString)/\(path)"
var components = URLComponents(string: urlPath)!
var queryItems = [URLQueryItem]()
for (key, value) in parameters {
let item = URLQueryItem(name: key, value: value)
queryItems.append(item)
}
components.queryItems = queryItems
let url = components.url!
var request = URLRequest(url: url)
request.httpMethod = method
let session: URLSession = {
let config = URLSessionConfiguration.default
return URLSession(configuration: config)
}()
let task = session.dataTask(with: request) {
(data, response, error) in
if let data = data {
do {
let jsonSerialized = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]
print(jsonSerialized!)
} catch let error as NSError {
print(error.localizedDescription)
}
} else if let error = error {
print(error.localizedDescription)
}
}
task.resume()
}
}
For more please check this link: Link.

Multiple NSURLSession dependent download tasks

I wanted to learn some NSURLSession basics and I was wondering how to handle multiple requests from an API. Like if you ask for a user resource on Github's api, it has an avatar_url, and then wanted to use that avatar_url to make another request. I have this so far:
let reposEndpoint = URL(string: "users/crystaltwix", relativeTo: baseURL)
var reposRequest = URLRequest(url: reposEndpoint!)
reposRequest.allHTTPHeaderFields = [
"accept": "application/vnd.github.v3+json",
"content-type": "application/json"
]
session?.dataTask(with: reposRequest) { data, response, error in
guard let response = response, let data = data else {
print("something went wrong")
return
}
print("response: \(response)")
// print("data: \(data)")
let decoder = JSONDecoder()
do {
let user = try decoder.decode(User.self, from: data)
print(user)
// avatar URL
let avatarURL = URL(string: user.avatarURL)
let avatarEndpoint = URLRequest(url: avatarURL!)
self.session?.dataTask(with: avatarEndpoint) { data, response, error in
guard let response = response, let data = data else {
print("something went wrong inner")
return
}
let avatarImage = UIImage(data: data)
let userModel = UserModel(login: user.login, avatar: avatarImage!, name: user.name, bio: user.bio)
}
} catch let error {
print("Error: \(error.localizedDescription)")
print(error)
completion(nil, nil, error)
}
}.resume()
struct User: Codable {
let login: String
let avatarURL: String
let name: String
let bio: String
private enum CodingKeys: String, CodingKey {
case login
case avatarURL = "avatar_url"
case name
case bio
}
}
So the first request to the user works and I make my URLRequest with the user.avatarURL fine, but then in the next
self.session?.dataTask(with: avatarEndpoint) { // nothing happens here }
No request is made there. What's the best way to handle this scenario?
I see your code, i think you forgot to call resume method in second request.
self.session?.dataTask(with: avatarEndpoint) { data, response, error in
guard let response = response, let data = data else {
print("something went wrong inner")
return
}
let avatarImage = UIImage(data: data)
let userModel = UserModel(login: user.login, avatar: avatarImage!, name: user.name, bio: user.bio)
}.resume()
resume method means start requesting.