"Bad Request" response on PredictHQ API ["error": invalid_client] "authentication" request - ios

Currently looking to make an authentication request on the PredictHQ API using Alamofire.
I have generated client credentials in the developer's console, and transferred them to my project. The problem lies when I use the URL https://api.predicthq.com/oauth2/token/ in my function *receiveJSONData() to receive a network status of my requests.
Below are examples I followed from the PredictHQ website https://developer.predicthq.com/oauth2/ on how the cURL request should be formatted as.
1) I first created my networkServiceClient class
import UIKit
import Alamofire
class networkServiceClient {
private let token = "GENERATED_TOKEN_FROM_PREDICTHQ_API".data(using: String.Encoding.utf8)!.base64EncodedString()
private let secret = "GENERATED_SECRET_KEY_FROM_PREDICTHQ_API".data(using: String.Encoding.utf8)!.base64EncodedString()
private let id = "GENERATED_ID_FROM_PREDICTHQ_API".data(using: String.Encoding.utf8)!.base64EncodedString()
private let contentType = "application/json"
private var authURL = URL(string: "https://api.predicthq.com/oauth2/token/")
private var tokenDataURL = URL(string: "https://api.predicthq.com/v1/events/")
typealias webResponse = ([[String: Any]]?, Error?) -> Void
func receiveJSONData(completion: #escaping webResponse){
let loginString = String(format: "Basic %#:%#", id, secret)
let postHeaders:HTTPHeaders = ["Authorization": loginString, "Accept": contentType]
let params: [String : Any] = ["grant_type": "client_credentials", "scope": "account events signals"]
//post authentication request
AF.request(authURL!, method: .post, parameters: params, headers: postHeaders).responseJSON { (response) in
if let error = response.error {
completion(nil, error)
} else if let jsonArray = response.value as? [[String: Any]] {
completion(jsonArray, nil)
} else if let jsonDict = response.value as? [String: Any] {
completion([jsonDict], nil)
}
}
}
2) Then I implemented it in my TestingViewingController
import UIKit
import Alamofire
class TestingViewController: UIViewController {
private let networkingClient = networkServiceClient()
override func viewDidLoad() {
super.viewDidLoad()
networkingClient.receiveJSONData() { (json, error) in
if let error = error {
print(error.localizedDescription)
} else if let json = json {
print(json.description)
}
}
}
}
3) Lastly, here is my output in the TestingViewController console.
"error": invalid_client

Hey it might be because you're base64 encoding the client_id and secret separately but they should be encoded together (separated by colon) like the screenshot shows.

Related

Send image from iOS Alamofire to Vapor

I have an application where user can update their profile picture.
The application is developed in SwiftUI and used Alamofire to perform API request and the server is developed with Vapor.
When I tried to send the picture to the server, I got this error:
[ WARNING ] Value required for key 'filename'. [request-id: A5083FA1-657C-4777-A7FF-9D02E2A66703]
Here is the code from Vapor:
private func updatePicture(req: Request) async throws -> Response {
let file = try req.content.decode(File.self)
guard let fileExtension = file.extension else { throw Abort(.badRequest)}
return .init(status: .accepted, headers: getDefaultHttpHeader(), body: .empty)
}
And here is the iOS code:
func uploadFiles(urlParams: [String], method: HTTPMethod, user: User, image: UIImage, completionHandler: #escaping ((Data?, HTTPURLResponse?, Error?)) -> Void) {
guard let formattedUrl = URL(string: "\(url)/\(urlParams.joined(separator: "/"))") else {
completionHandler((nil, nil, nil))
return
}
var headers: HTTPHeaders?
headers = ["Authorization" : "Bearer \(user.token)"]
let multiPart: MultipartFormData = MultipartFormData()
multiPart.append(image.jpegData(compressionQuality: 0.9), withName: "data", fileName: "filename", mimeType: "image/jpeg" )
AF.upload(multipartFormData: multiPart, to: formattedUrl, method: .patch, headers: headers).response { data in
print(data)
}.resume()
}
I followed vapor and Alamofire documentation and I still get this issue.
Is anyone can help me with this issues ?
On the Vapor side you have to use struct with File field inside
struct PayloadModel: Content {
let data: File
}
private func updatePicture(req: Request) async throws -> HTTPStatus {
let payload = try req.content.decode(PayloadModel.self)
guard let fileExtension = payload.data.extension else {
throw Abort(.badRequest)
}
return .accepted
}

POST body values are not recognized

I am calling a POST API but the post values are not getting recognised.
I am using alamofire for API handling
public func POSTRequest(postData : AnyObject?,APIName : String?,headers: HTTPHeaders?,success:#escaping (JSON) -> Void, failure:#escaping (String,String) -> Void)
{
let url = URL(string: APIName!)!
var urlRequest = URLRequest(url: url)
urlRequest.httpBody = postData as? Data
urlRequest.httpMethod = "POST"
urlRequest.allHTTPHeaderFields = headers
let manager = Alamofire.SessionManager.default
manager.session.configuration.timeoutIntervalForRequest = 30.0
manager.request(urlRequest).responseJSON { ( responseData) -> Void in
if responseData.result.isSuccess {
let statusCode = responseData.response?.statusCode
if(statusCode == 200){
let resJson = JSON(responseData.result.value!)
success(resJson)
}
else {
let resJson = JSON(responseData.result.value!)
let errorMsg = resJson["message"].stringValue
let errorTitle = resJson["title"].stringValue
failure(errorMsg,errorTitle)
}
}
if responseData.result.isFailure {
let error : Error = responseData.result.error!
failure(error.localizedDescription,"")
}
}
}
// MARK: Login
func userLogin(postData: AnyObject, completionHandler:#escaping (JSON) -> Void,failure:#escaping (String,String) -> Void)
{
let apiName = GlobalConstants.rootUrl + GlobalConstants.loginUrl
let httpHeaders: HTTPHeaders = getHeaders()
ApiRequestHandler.shared.POSTRequest(postData: postData, APIName: apiName, headers: httpHeaders, success: { (JSON) in
print("JSON \(JSON)")
completionHandler(JSON)
})
{ (Errormsg,ErrorTitle) in
print("Error \(Errormsg)")
failure(Errormsg,ErrorTitle)
}
}
I am creating the post data here
let postDataDct = NSMutableDictionary()
postDataDct.setValue(self.userNameTextField.text, forKey: "username")
postDataDct.setValue(self.phoneNumberTextField.text, forKey: "mobile")
guard let data = try? JSONSerialization.data(withJSONObject:
postDataDct , options: JSONSerialization.WritingOptions.prettyPrinted)
as NSData else {
return;
}
print(NSString(data: data as Data, encoding:
String.Encoding.utf8.rawValue)! as String)
header creation here
func getHeaders() -> HTTPHeaders {
let headers: HTTPHeaders = ["Content-Type": "application/json" ,"Authorization" : "Basic dGFuaXNocWdfgaFF%waXJvb3Q6dzBEMFlkSnR0"]
return headers
}
I am calling the login method here
ApiManager.shared.userLogin(postData: postDataDct as AnyObject, completionHandler: { (JSON) in
print(JSON)
})
{ (ErrorMsg, ErrorTitle) in
print(ErrorMsg)
}
Please help me to find the issue.
API is giving me error response, saying user name or password is empty.
For Posting Alamofire have its own Parameters Class Try to use that like this with make share using the exact keys as on server.
let parameter: Parameters = [
"Email": txtfld_email.text!,
"Password": txtfld_password.text!
]
Cross check your keys:
let postDataDct = NSMutableDictionary()
postDataDct.setValue(self.userNameTextField.text, forKey: "username")
postDataDct.setValue(self.phoneNumberTextField.text, forKey: "mobile")
You are making the request with username and mobile It seems like the keys will be username and password according to the response you are getting name or password is empty.
let postDataDct = NSMutableDictionary()
postDataDct.setValue(self.userNameTextField.text, forKey: "username")
postDataDct.setValue(self.phoneNumberTextField.text, forKey: "password")
Suggestion:
Don't use NSMutableDictionary in Swift, use its native Swift
counterpart, Dictionary

Uploading video to Youtube using its REST API without the iOS SDK

Still a noob, so bear with me. I am using SWIFT 3 and the V3 YouTube Data API with REST. I can pull a list of my videos so my connection and authorization is working just fine.
I can't seem to figure out how to upload though. I found an old post that was very similar to mine (Setting snippet data for youtube upload via REST API using Swift).
I'm confused where they are getting that token variable from and how they pass it into this function. Also, not sure how to set the upload variable that is right before the post. Any help is appreciated!
func uploadVideo(token: String, callback: #escaping (Bool) -> Void){
let headers = ["Authorization": "Bearer \(token)"]
let path = Bundle.main.path(forResource: "intro", ofType: "mov")
let videodata: NSData = NSData.dataWithContentsOfMappedFile(path!)! as! NSData
upload(
.POST,
"https://www.googleapis.com/upload/youtube/v3/videos?part=snippet",
headers: headers,
multipartFormData: { multipartFormData in
multipartFormData.appendBodyPart(data:"{'snippet':{'title' : 'TITLE_TEXT', 'description': 'DESCRIPTION_TEXT'}}".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!, name :"snippet", mimeType: "application/json")
multipartFormData.appendBodyPart(data: videodata, name: "intro", fileName: "intro.mov", mimeType: "application/octet-stream")
},
encodingCompletion: { encodingResult in
switch encodingResult {
case .Success(let upload, _, _):
upload.responseJSON { request, response, error in
print(response)
callback(true)
}
case .Failure(_):
callback(false)
}
})
}
Updated code from https://github.com/mfriedl89/Developer5/blob/master/Conari/Conari/. It's just a poc mostly, refactoring required (e.g: Use Codable for JSON parsing instead of force-unwrapping). And a proper OAuth2 flow should be implemented using SFSafariViewController/SFAuthenticationSession (depending on the targeted iOS version) or Google SignIn
import Foundation
class YoutubeTokenProvider {
// TODO: Store token, evaluate ttl, retry request if needed
static func getAccessToken(callback: #escaping (String?) -> Void){
/* Remark (Security)
Storing the data inside here is not secure, but still better than requesting it from a custom request.
Two possibilities:
1. Storing all data here
2. Storing all data on an external server and only requesting the access token from this server
Option 1 was chosen based on the assumption that decompiling the app should be more difficult than just
monitoring the request and getting the access token (which allows the attacker to do anything with our
YouTube account). The disadvantage is that an attacker gets everything after a successful decompilation
and not only the access token.
*/
let client_secret = "..."
let grant_type = "refresh_token"
let refresh_token = "..."
let client_id = "..."
var request = URLRequest(url: URL(string: "https://accounts.google.com/o/oauth2/token")!)
request.httpMethod = "POST"
let postString = "client_secret=" + client_secret +
"&grant_type=" + grant_type +
"&refresh_token=" + refresh_token +
"&client_id=" + client_id
request.httpBody = postString.data(using: .utf8)
URLSession.shared.dataTask(with: request, completionHandler: {data, response, error in
guard let data = data, error == nil else {
callback(nil)
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {
callback(nil)
return
}
do {
let jsonData = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! YouTubeManager.JSON
let accessToken = jsonData["access_token"]! as? String
callback(accessToken)
} catch {
callback(nil)
}
return
}).resume()
}
}
import Foundation
struct YoutubeVideo {
let title, thumbnailUrl, videoId: String
}
import Foundation
import Alamofire
/// This file will act as our YouTube manager.
class YouTubeManager {
typealias JSON = [String:AnyObject]
typealias SearchByTitleCallback = (_ response: [YoutubeVideo], _ success:Bool, _ message:String) -> Void
typealias PostVideoCallback = (String, Bool) -> Void
/** Singletone instance. */
static let sharedManager = YouTubeManager()
let apiKey = "..."
let channelID = "..."
let searchApiUrl = "https://www.googleapis.com/youtube/v3/search"
let identifier = "videoID: "
func parseIdentifier(input: String) -> String? {
let seperator = "videoID: "
if input.contains(self.identifier) {
let videoID = input.components(separatedBy: seperator)
return videoID.last
}
return nil
}
func searchVideoByTitle(title: String, completionHandler: #escaping SearchByTitleCallback) -> Void {
let eTitle = title.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
let urlString = searchApiUrl + "?part=snippet&q=" + eTitle + "&type=video&key=" + apiKey
let targetURL = URL(string: urlString)!
var returnArray = [YoutubeVideo]()
let task = URLSession.shared.dataTask(with: targetURL) { (data, response, error) in
guard error == nil else {
completionHandler(returnArray, false, "error = \(error!)")
return
}
guard let data = data else {
completionHandler(returnArray, false, "error = data is nil")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {
completionHandler(returnArray, false, "response = \(response!)")
return
}
do {
let resultsDict = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! JSON
let items = resultsDict["items"] as! [JSON]
returnArray = items.map { item in
let snippetDict = item["snippet"] as! JSON
let title = snippetDict["title"] as! String
let thumbnail = ((snippetDict["thumbnails"] as! JSON)["default"] as! JSON)["url"] as! String
let videoid = (item["id"] as! JSON)["videoId"] as! String
return YoutubeVideo(title: title, thumbnailUrl: thumbnail, videoId: videoid)
}
completionHandler(returnArray, true, "")
} catch {
completionHandler(returnArray, false, "error serializing JSON: \(error)")
}
}
task.resume()
}
func postVideoToYouTube(uploadUrl: String, videoData: Data, title: String, callback: #escaping PostVideoCallback){
YoutubeTokenProvider.getAccessToken { (accessToken) in
guard let accessToken = accessToken
else { return }
let headers: HTTPHeaders = ["Authorization": "Bearer \(accessToken)"]
Alamofire.upload(
multipartFormData: { multipartFormData in
let metadata = "{'snippet':{'title' : '\(title)', 'description': 'This video was uploaded using Mr Tutor.'}}".data(using: .utf8, allowLossyConversion: false)!
multipartFormData.append(metadata, withName: "snippet", mimeType: "application/json")
multipartFormData.append(videoData, withName: "video", fileName: "sample.mp4", mimeType: "application/octet-stream")
},
to: "https://www.googleapis.com/upload/youtube/v3/videos?part=snippet",
headers: headers,
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.responseJSON { response in
do {
let jsonData = try JSONSerialization.jsonObject(with: response.data!, options: .allowFragments) as! JSON
let videoID = jsonData["id"] as! String
let identifierFinal = self.identifier + videoID
callback(identifierFinal, true)
} catch {
print("error serializing JSON: \(error)")
callback("", false)
}
print("Success")
}
case .failure(_):
print("Failure")
callback("", false)
}
})
}
}
}

Implementing google translation api in swift 3 iOS

Hi i am new to iOS development and i am trying to implement google translation API within my app. I found some sample code online from GitHub https://github.com/prine/ROGoogleTranslate. I downloaded the sample code and followed the instructions provided by obtaining an api key from google cloud translate and placing it within the code however the code is not working, iv looked at the comments on the GitHub site and found that it has
worked for other developers. I really don't know what i am doing wrong in the code.
ROGoogleTranslateParams.swift
import Foundation
public struct ROGoogleTranslateParams {
public init() {
}
public init(source:String, target:String, text:String) {
self.source = source
self.target = target
self.text = text
}
public var source = "de"
public var target = "en"
public var text = "Hallo"
}
/// Offers easier access to the Google Translate API
open class ROGoogleTranslate {
/// Store here the Google Translate API Key
public var apiKey = "YOUR_API_KEY"
///
/// Initial constructor
///
public init() {
}
///
/// Translate a phrase from one language into another
///
/// - parameter params: ROGoogleTranslate Struct contains all the needed parameters to translate with the Google Translate API
/// - parameter callback: The translated string will be returned in the callback
///
open func translate(params:ROGoogleTranslateParams, callback:#escaping (_ translatedText:String) -> ()) {
guard apiKey != "" else {
print("Warning: You should set the api key before calling the translate method.")
return
}
if let urlEncodedText = params.text.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) {
if let url = URL(string: "https://translation.googleapis.com/language/translate/v2?key=\(self.apiKey)&q=\(urlEncodedText)&source=\(params.source)&target=\(params.target)&format=text") {
let httprequest = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
guard error == nil else {
print("Something went wrong: \(error?.localizedDescription)")
return
}
if let httpResponse = response as? HTTPURLResponse {
guard httpResponse.statusCode == 200 else {
if let data = data {
print("Response [\(httpResponse.statusCode)] - \(data)")
}
return
}
do {
// Pyramid of optional json retrieving. I know with SwiftyJSON it would be easier, but I didn't want to add an external library
if let data = data {
if let json = try JSONSerialization.jsonObject(with: data, options:JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary {
if let jsonData = json["data"] as? [String : Any] {
if let translations = jsonData["translations"] as? [NSDictionary] {
if let translation = translations.first as? [String : Any] {
if let translatedText = translation["translatedText"] as? String {
callback(translatedText)
}
}
}
}
}
}
} catch {
print("Serialization failed: \(error.localizedDescription)")
}
}
})
httprequest.resume()
}
}
}
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet var text:UITextField!
#IBOutlet var fromLanguage:UITextField!
#IBOutlet var toLanguage:UITextField!
#IBOutlet var translation:UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func translate(_ sender: UIButton) {
let translator = ROGoogleTranslate()
translator.apiKey = "YOUR_API_KEY" // Add your API Key here
var params = ROGoogleTranslateParams()
params.source = fromLanguage.text ?? "de"
params.target = toLanguage.text ?? "en"
params.text = text.text ?? "Hallo"
translator.translate(params: params) { (result) in
DispatchQueue.main.async {
self.translation.text = "\(result)"
}
}
}
}
These are classes are used.
The result i get when i press the 'translate' button is the following:
Response [403] - 355 bytes
your help is appreciated. The code is available to download from the url provided
Thank you
I'm the author of the library you mentioned above :). I guess you get the 403 because your Google Api Account is not yet activated correctly. Google has changed the policy of the Translation api and its not free anymore. So you problably didn't add the credit card informations in the Api account and therefor get the 403 error?
Try this "POST" method function not the 'Get' method as you implemented -
open func translateTest(params: GoogleAITranslateParams, targetLanguage: String, callback:#escaping (_ translatedText:String) -> ()) {
guard apiKey != "" else {
return
}
var request = URLRequest(url: URL(string: "https://translation.googleapis.com/language/translate/v2?key=\(self.apiKey)")!)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue(Bundle.main.bundleIdentifier ?? "", forHTTPHeaderField: "X-Ios-Bundle-Identifier")
let jsonRequest = [
"q": params.text,
"source": "en",
"target": targetLanguage,
"format": "text"
] as [String : Any]
if let jsonData = try? JSONSerialization.data(withJSONObject: jsonRequest, options: .prettyPrinted) {
request.httpBody = jsonData
let task: URLSessionDataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard error == nil else {
print("Something went wrong: \(String(describing: error?.localizedDescription))")
return
}
if let httpResponse = response as? HTTPURLResponse {
guard httpResponse.statusCode == 200 else {
if let data = data {
print("Response [\(httpResponse.statusCode)] - \(data)")
}
return
}
do {
if let data = data {
if let json = try JSONSerialization.jsonObject(with: data, options:JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary {
if let jsonData = json["data"] as? [String : Any] {
if let translations = jsonData["translations"] as? [NSDictionary] {
if let translation = translations.first as? [String : Any] {
if let translatedText = translation["translatedText"] as? String {
callback(translatedText)
}
}
}
}
}
}
} catch {
print("Serialization failed: \(error.localizedDescription)")
}
}
}
task.resume()
}
}

Send SMS with Twilio in Swift

I try to use Twilio as an service provider but they have no examples for Swift that I understand.
My task is to send SMS to a number using Twilio API with Swift.
I have a Twilio.com account - and that one is working. But how do I do this in Swift code in a easy manner.
Twilio does provide a library - but that is meant for C# not for Swift (and using a bridging header seems too complicated!)
Here is the C# example, I need a easy Swift example.
// Download the twilio-csharp library from twilio.com/docs/csharp/install
using System;
using Twilio;
class Example
{
static void Main(string[] args)
{
// Find your Account Sid and Auth Token at twilio.com/user/account
string AccountSid = "AC5ef8732a3c49700934481addd5ce1659";
string AuthToken = "{{ auth_token }}";
var twilio = new TwilioRestClient(AccountSid, AuthToken);
var message = twilio.SendMessage("+14158141829", "+15558675309", "Jenny please?! I love you <3", new string[] {"http://www.example.com/hearts.png"});
Console.WriteLine(message.Sid);
}
}
Twilio evangelist here.
To send a text message from Swift you can just make a request directly to Twilios REST API. That said, I would not recommend doing this from an iOS app (or any other client app) as it requires you to embed your Twilio account credentials in the app, which is dangerous. I would instead recommend sending the SMS from a server side application.
If you do want to send the message from your app, there are a couple of Swift libraries I know of that simplify making HTTP requests:
Alamofire - from Mattt Thompson, creator of AFNetworking - used in the example here: https://www.twilio.com/blog/2016/11/how-to-send-an-sms-from-ios-in-swift.html
SwiftRequest - from Ricky Robinett of Twilio
To make the request using SwiftRequest, it would look like this:
var swiftRequest = SwiftRequest();
var data = [
"To" : "+15555555555",
"From" : "+15555556666",
"Body" : "Hello World"
];
swiftRequest.post("https://api.twilio.com/2010-04-01/Accounts/[YOUR_ACCOUNT_SID]/Messages",
auth: ["username" : "[YOUR_ACCOUNT_SID]", "password" : "YOUR_AUTH_TOKEN"]
data: data,
callback: {err, response, body in
if err == nil {
println("Success: \(response)")
} else {
println("Error: \(err)")
}
});
Hope that helps.
recently I have gone through Twilio docs and few SO post.
you can send SMS with following code snip in Swift 2.0
func sendSMS()
{
let twilioSID = "your Sender ID here"
let twilioSecret = "your token id here"
//Note replace + = %2B , for To and From phone number
let fromNumber = "%2B14806794445"// actual number is +14803606445
let toNumber = "%2B919152346132"// actual number is +919152346132
let message = "Your verification code is 2212 for signup with <app name here> "
// Build the request
let request = NSMutableURLRequest(URL: NSURL(string:"https://\(twilioSID):\(twilioSecret)#api.twilio.com/2010-04-01/Accounts/\(twilioSID)/SMS/Messages")!)
request.HTTPMethod = "POST"
request.HTTPBody = "From=\(fromNumber)&To=\(toNumber)&Body=\(message)".dataUsingEncoding(NSUTF8StringEncoding)
// Build the completion block and send the request
NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: { (data, response, error) in
print("Finished")
if let data = data, responseDetails = NSString(data: data, encoding: NSUTF8StringEncoding) {
// Success
print("Response: \(responseDetails)")
} else {
// Failure
print("Error: \(error)")
}
}).resume()
}
if everything goes fine..You should receive message like this..
Here is the new Swift example for Passwordless authentication. For the complete tutorial, Click Here
let url = "http://localhost:8000"
var swiftRequest = SwiftRequest()
var params:[String:String] = [
"token" : token!.text
]
swiftRequest.post(url + "/user/auth/", data: params, callback: {err, response, body in
if( err == nil && response!.statusCode == 200) {
if((body as NSDictionary)["success"] as Int == 1) {
self.showAlert("User successfully authenticated!");
} else {
self.showAlert("That token isn't valid");
}
} else {
self.showAlert("We're sorry, something went wrong");
}
})
If You are using server side Swift with Perfect.org
See this Blog
http://perfecttwilio.blogspot.in
The answer by "Devin Rader" is perfect. For any other users like me, the following is the full converted code for SwiftRequest for swift 3.0. The original code is by Ricky Robinett.
Please let us know if any errors.
Thankx..
//
// SwiftRequest.swift
// SwiftRequestTest
//
// Created by Ricky Robinett on 6/20/14.
// Copyright (c) 2015 Ricky Robinett. All rights reserved.
//
// ***********************************************************
//
// Modification for Swift 3.0 by Sanjay Sampat on 21.Jun.2017
//
// ***********************************************************
import Foundation
public class SwiftRequest {
var session = URLSession.shared
public init() {
// we should probably be preparing something here...
}
// GET requests
public func get(url: String, auth: [String: String] = [String: String](), params: [String: String] = [String: String](), callback: ((_ err: NSError?, _ response: HTTPURLResponse?, _ body: AnyObject?)->())? = nil) {
let qs = dictToQueryString(data: params)
request(options: ["url" : url, "auth" : auth, "querystring": qs ], callback: callback )
}
// POST requests
public func post(url: String, data: [String: String] = [String: String](), auth: [String: String] = [String: String](), callback: ((_ err: NSError?, _ response: HTTPURLResponse?, _ body: AnyObject?)->())? = nil) {
let qs = dictToQueryString(data: data)
request(options: ["url": url, "method" : "POST", "body" : qs, "auth" : auth] , callback: callback)
}
// Actually make the request
func request(options: [String: Any], callback: ((_ err: NSError?, _ response: HTTPURLResponse?, _ body: AnyObject?)->())?) {
if( options["url"] == nil ) { return }
var urlString = options["url"] as! String
if( options["querystring"] != nil && (options["querystring"] as! String) != "" ) {
let qs = options["querystring"] as! String
urlString = "\(urlString)?\(qs)"
}
let url = NSURL(string:urlString)
let urlRequest = NSMutableURLRequest(url: url! as URL)
if( options["method"] != nil) {
urlRequest.httpMethod = options["method"] as! String
}
if( options["body"] != nil && options["body"] as! String != "" ) {
var postData = (options["body"] as! String).data(using: String.Encoding.ascii, allowLossyConversion: true)
urlRequest.httpBody = postData
urlRequest.setValue("\(postData!.count)", forHTTPHeaderField: "Content-length")
}
// is there a more efficient way to do this?
if( options["auth"] != nil && (options["auth"] as! [String: String]).count > 0) {
var auth = options["auth"] as! [String: String]
if( auth["username"] != nil && auth["password"] != nil ) {
let username = auth["username"]
let password = auth["password"]
var authorization = "\(username!):\(password!)"
if let data = authorization.data(using: String.Encoding.utf8) {
//authorization = "Basic " + data.base64EncodedString(options: [])
authorization = "Basic " + data.base64EncodedString()
}
urlRequest.setValue(authorization, forHTTPHeaderField: "Authorization")
}
}
let task = session.dataTask(with: urlRequest as URLRequest, completionHandler: {body, response, err in
let resp = response as! HTTPURLResponse?
if( err == nil) {
if let gotResponse = response {
if(gotResponse.mimeType == "text/html") {
let bodyStr = NSString(data: body!, encoding:String.Encoding.utf8.rawValue)
return callback!(err as NSError?, resp, bodyStr)
} else if(gotResponse.mimeType == "application/xml") {
let bodyStr = NSString(data: body!, encoding:String.Encoding.utf8.rawValue)
return callback!(err as NSError?, resp, bodyStr)
} else if(gotResponse.mimeType == "application/json") {
// ss pending
do {
let jsonAnyObject:AnyObject = try JSONSerialization.jsonObject(with: (body! as NSData) as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as! [String: AnyObject] as AnyObject
return callback!(err as NSError?, resp, jsonAnyObject as AnyObject);
} catch _ {
}
}
}
}
return callback!(err as NSError?, resp, body as AnyObject)
})
task.resume()
}
func request(url: String, callback: ((_ err: NSError?, _ response: HTTPURLResponse?, _ body: AnyObject?)->())? = nil) {
request(options: ["url" : url ], callback: callback )
}
private func dictToQueryString(data: [String: String]) -> String {
var qs = ""
for (key, value) in data {
let encodedKey = encode(value: key)
let encodedValue = encode(value: value)
qs += "\(encodedKey)=\(encodedValue)&"
}
return qs
}
private func encode(value: String) -> String {
let queryCharacters = NSCharacterSet(charactersIn:" =\"#%/<>?#\\^`{}[]|&+").inverted
if let encodedValue:String = value.addingPercentEncoding(withAllowedCharacters: queryCharacters) {
return encodedValue
}
//let encodedValue:String = value.stringByAddingPercentEncodingWithAllowedCharacters(queryCharacters)!
return value
}
}
Sample code to use above class as mentioned by "Devin Rader"
let URL = "https://api.twilio.com/2010-04-01/Accounts/\(myUserIdForBulkSmsMessageSending)/Messages"
var swiftRequest = SwiftRequest();
var data = [
"To" : "+\(replaceNumberToSendSms)",
"From" : "+\(replaceNumberFromSendSms)",
"Body" : message,
"MediaUrl" : theUrlEncodedMessage
];
//print( "=========VV==========" )
//print( "URL: \(URL) " )
//print( "data: \(String(describing: data))" )
//print( "auth: \(myUserIdForBulkSmsMessageSending) \(myUserPasswordForBulkSmsMessageSending)")
//print( "=====================" )
swiftRequest.post(url: URL,
data: data,
auth: ["username" : myUserIdForBulkSmsMessageSending, "password" : myUserPasswordForBulkSmsMessageSending],
callback: {err, response, body in
if err == nil {
print("Success: \(String(describing: response))")
if let currentBody = body {
// SSTODO PENDING TO HANDLE SUCCESS OF TWILLO OR ERRORS HANDLING OF TWILLO.
//print( "=====================" )
//print( " currentBody: \(currentBody) " )
//print( " currentBodyString: \(String(describing: currentBody)) ")
//print( "=========^^==========" )
}
} else {
print("Error: \(String(describing: err))")
}
});
Swift 3 version:
func sendSMS()
{
print("Starting...")
let twilioSID = "ENRET YOUR SID"
let twilioSecret = "YOUR TOKEN"
//Note replace + = %2B , for To and From phone number
let fromNumber = "%29999999"// actual number is +9999999
let toNumber = "%29999999"// actual number is +9999999
let message = "Your verification code is 2212 for signup with"
// Build the request
let request = NSMutableURLRequest(url: URL(string:"https://\(twilioSID):\(twilioSecret)#api.twilio.com/2010-04-01/Accounts/\(twilioSID)/SMS/Messages")!)
request.httpMethod = "POST"
request.httpBody = "From=\(fromNumber)&To=\(toNumber)&Body=\(message)".data(using: .utf8)
// Build the completion block and send the request
URLSession.shared.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) in
print("Finished")
if let data = data, let responseDetails = NSString(data: data, encoding: String.Encoding.utf8.rawValue) {
// Success
print("Response: \(responseDetails)")
} else {
// Failure
print("Error: \(error)")
}
}).resume()
}

Resources