Getting Hash Mismatch in pay u money Swift 3 - ios

Im New to Swift and I'm Integrating PayUmoney IOS SDK in swift, Im getting trouble When I'm doing in live its showing hash mismatch (Hash mismatch1) If I'm doing in test its showing invalid merchant key (Invalid key) I struck here from 2 weeks Did so many things and didn't get any solution for this can any one help it would be awesome. Below is my code, Thank you in Advance.
var params : PUMRequestParams = PUMRequestParams.shared()
var utils : Utils = Utils()
params.environment = PUMEnvironment.test;
params.firstname = txtFldName.text;
params.key = "bZf4AOjj";
params.merchantid = "5745303";
params.logo_url = "";
params.productinfo = "Product Info";
params.email = txtFldEmail.text;
params.phone = "";
params.surl = "https://www.payumoney.com/mobileapp/payumoney/success.php";
params.furl = "https://www.payumoney.com/mobileapp/payumoney/failure.php";
if(params.environment == PUMEnvironment.test){
generateHashForProdAndNavigateToSDK()
}
else{
calculateHashFromServer()
}
// assign delegate for payment callback.
params.delegate = self;
}
func generateHashForProdAndNavigateToSDK() -> Void {
let txnid = params.txnid!
let hashSequence : NSString = "\(params.key)|\(txnid)|\(params.amount)|\(params.productinfo)|\(params.firstname)|\(params.email)|||||||||||2uIsGhXWVw" as NSString
let data :NSString = utils.createSHA512(hashSequence as String!) as NSString
params.hashValue = data as String!;
startPaymentFlow();
}
// MARK:HASH CALCULATION
func prepareHashBody()->NSString{
return "SHA-512key=\(params.key!)&amount=\(params.amount!)&txnid=\(params.txnid!)&productinfo=\(params.productinfo!)&email=\(params.email!)&firstname=\(params.firstname!)" as NSString;
}
func calculateHashFromServer(){
let config = URLSessionConfiguration.default // Session Configuration
let session = URLSession(configuration: config) // Load configuration into Session
let url = URL(string: "https://test.payumoney.com/payment/op/v1/calculateHashForTest")!
var request = URLRequest(url: url)
request.httpBody = prepareHashBody().data(using: String.Encoding.utf8.rawValue)
request.httpMethod = "POST"
let task = session.dataTask(with: request, completionHandler: {
(data, response, error) in
if error != nil {
print(error!.localizedDescription)
} else {
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: []) as? [String: Any]{
print(json)
let status : NSNumber = json["status"] as! NSNumber
if(status.intValue == 0)
{
self.params.hashValue = json["result"] as! String!
OperationQueue.main.addOperation {
self.startPaymentFlow()
}
}
else{
OperationQueue.main.addOperation {
self.showAlertViewWithTitle(title: "Message", message: json["message"] as! String)
}
}
}
} catch {
print("error in JSONSerialization")
}
}
})
task.resume()
}

Hello Vinny do with webview its working for me. Before I also used this PayUmoney IOS SDK but faced so many problems so based on objective-c I did this so I think its useful to you. create a weak var webview and create class UIwebviewdelegate
class PayumoneyViewController: UIViewController, UIWebViewDelegate, UIAlertViewDelegate {
#IBOutlet weak var Webview: UIWebView!
and for test use below credentials
//test
var merchantKey = "40747T"
var salt = "ur salt"
var PayUBaseUrl = "https://test.payu.in"
For live
//Production
var merchantKey = “xxxxxx”
var salt = “xxxxx”
var PayUBaseUrl = "https://secure.payu.in"
let productInfo = “Myapp” //It can be Project name or anything else
let firstName = “Santoshi” //Details of user whose is purchasing order
let email = “santoshi#app.com" //Details of user whose is purchasing order
let phone = "xxxxxxxxx" //Details of user whose is purchasing order
let sUrl = "www.google.com" //By this URL we match whether payment got success or failure
let fUrl = "www.google.com" //By this URL we match whether payment got success or failure
let service_provider = "payu_paisa"
var txnid1: String! = "" //Its an unique id which can give order a specific order number.
let totalPriceAmount = "1.0"
Above viewdidload do like this
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
initPayment()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
}
In viewdidload do like this
override func viewDidLoad() {
super.viewDidLoad()
Webview.delegate = self
// Do any additional setup after loading the view.
}
Create payment and Generate Hash key
func initPayment() {
txnid1 = “Myapp\(String(Int(NSDate().timeIntervalSince1970)))"
//Generating Hash Key
let hashValue = String.localizedStringWithFormat("%#|%#|%#|%#|%#|%#|||||||||||%#",merchantKey,txnid1,totalPriceAmount,productInfo,firstName,email,salt)
let hash = self.sha1(string: hashValue)
let postStr = "txnid="+txnid1+"&key="+merchantKey+"&amount="+totalPriceAmount+"&productinfo="+productInfo+"&firstname="+firstName+"&email="+email+"&phone="+phone+"&surl="+sUrl+"&furl="+fUrl+"&hash="+hash+"&service_provider="+service_provider
let url = NSURL(string: String.localizedStringWithFormat("%#/_payment", PayUBaseUrl))
let request = NSMutableURLRequest(url: url! as URL)
do {
let postLength = String.localizedStringWithFormat("%lu",postStr.characters.count)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Current-Type")
request.setValue(postLength, forHTTPHeaderField: "Content-Length")
request.httpBody = postStr.data(using: String.Encoding.utf8)
Webview.loadRequest(request as URLRequest)
}
catch let error as NSError
{
print(error)
}
}
Finally Do this
func sha1(string:String) -> String {
let cstr = string.cString(using: String.Encoding.utf8)
let data = NSData(bytes: cstr, length: string.characters.count)
var digest = [UInt8](repeating: 0, count:Int(CC_SHA512_DIGEST_LENGTH))
CC_SHA512(data.bytes, CC_LONG(data.length), &digest)
let hexBytes = digest.map { String(format: "%02x", $0) }
return hexBytes.joined(separator: "")
}
func webViewDidFinishLoad(_ webView: UIWebView) {
let requestURL = self.Webview.request?.url
let requestString:String = (requestURL?.absoluteString)!
if requestString.contains("https://www.payumoney.com/mobileapp/payumoney/success.php") {
print("success payment done")
}else if requestString.contains("https://www.payumoney.com/mobileapp/payumoney/failure.php") {
print("payment failure")
}
}
func webView(_ webView: UIWebView, didFailLoadWithError error: Error) {
let requestURL = self.Webview.request?.url
print("WebView failed loading with requestURL: \(requestURL) with error: \(error.localizedDescription) & error code: \(error)")
if error._code == -1009 || error._code == -1003 {
showAlertView(userMessage: "Please check your internet connection!")
}else if error._code == -1001 {
showAlertView(userMessage: "The request timed out.")
}
}
func showAlertView(userMessage:String){
}

I have Faced Same problem and i have got solution of this problem.
In my code this line generate optional values --> let hashSequence : NSString = "(params.key!)|(txnid)|(params.amount!)|(params.productinfo!)|(params.firstname!)|(params.email!)|||||||||||(params.merchantid!)" as NSString
remove optional from the values.

Related

Integrating linkedin in ios App for getting some details

I am working on the linkedin based signup,I want to get the some profile details from linkedin.
If user has already linkedin installed in your it should open the app and get the details if not you should open the web view and get the details.
I downloaded sdk from developers.linkedin.com and I am using the below code.
#IBAction func Signup(_ sender: UIButton) {
if (self.isInstalled(appScheme: "linkedin://app")){
// App installed
let permissions = [LISDK_BASIC_PROFILE_PERMISSION,LISDK_EMAILADDRESS_PERMISSION]
print("persmission end")
LISDKSessionManager.createSessionWithAuth(permissions, state: nil, showGoToAppStoreDialog: true, successBlock: { (returnState) -> Void in
let session = LISDKSessionManager.sharedInstance().session
LISDKAPIHelper.sharedInstance().getRequest("https://api.linkedin.com/v1/people/~:(id,first-name,last-name,email-address,picture-url,public-profile-url,industry,positions,location)?format=json", success: { (response) -> Void in
if let data = response.data.dataUsingEncoding(NSUTF8StringEncoding) {
if let dictResponse = try? NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers){
print("success")
}
}
}, error: { (error) -> Void in
print("LINKEDIN error\(error)")
})
}) { (error) -> Void in
print("error login linkedin")
}
}else{
// App not installed
print("App is not installed")
//isBackFromWebViewCntr = true
let webViewCnt = self.storyboard!.instantiateViewController(withIdentifier: "LinkedinWebVC") as UIViewController
self.navigationController?.pushViewController(webViewCnt, animated: true)
}
}
using this i am checking the App installed or not if not i am presenting the webview controller and nin that I am using the below code
let linkedInKey = "815972uscqupkz"
let linkedInSecret = "MuWBM6SQOQrBosol"
let authorizationEndPoint = "https://www.linkedin.com/uas/oauth2/authorization"
let accessTokenEndPoint = "https://www.linkedin.com/uas/oauth2/accessToken"
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.isNavigationBarHidden = false
self.navigationController?.navigationBar.isTranslucent = false
Webviewobj.delegate = self
self.startAuthorization()
// let redirectURL = "https://com.appcoda.linkedin.oauth/oauth".addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
// Do any additional setup after loading the view.
}
func startAuthorization() {
let responseType = "code"
let redirectURL = "https://jobz.com".addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed)!
let state = "E3ZYKC1T6H2yP4zyEy8xIdB4qSMnhfxsdxshcdggdxgqVP7502" /// "linkedin\(Int(NSDate().timeIntervalSince1970))"
let scope = "r_basicprofile,r_emailaddress"
var authorizationURL = "\(authorizationEndPoint)?"
authorizationURL += "response_type=\(responseType)&"
authorizationURL += "client_id=\(linkedInKey)&"
authorizationURL += "redirect_uri=\(redirectURL)&"
authorizationURL += "state=\(state)&"
authorizationURL += "scope=\(scope)"
// logout already logined user or revoke tokens
logout()
// Create a URL request and load it in the web view.
let request = URLRequest(url: URL(string: authorizationURL)!)
Webviewobj.loadRequest(request)
}
func logout(){
let revokeUrl = "https://api.linkedin.com/uas/oauth/invalidateToken"
let request = URLRequest(url: URL(string: revokeUrl)!)
Webviewobj.loadRequest(request)
}
private func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
let url = request.url!
if url.host == "http://jobz.com" {
//rangeOfString("code")
if url.absoluteString.range(of: "code") != nil {
let urlParts = url.absoluteString.components(separatedBy: "?") //componentsSeparatedByString("?")
let code = urlParts[1].components(separatedBy: "=")[1]
//componentsSeparatedByString("=")[1]
requestForAccessToken(authorizationCode: code)
// requestForAccessToken(code)
}
}
return true
}
private func webViewDidFinishLoad(webView : UIWebView) {
}
func requestForAccessToken(authorizationCode: String) {
let grantType = "authorization_code"
let redirectURL = "https://jobz.com".addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
// Set the POST parameters.
var postParams = "grant_type=\(grantType)&"
postParams += "code=\(authorizationCode)&"
postParams += "redirect_uri=\(redirectURL)&"
postParams += "client_id=\(linkedInKey)&"
postParams += "client_secret=\(linkedInSecret)"
// Convert the POST parameters into a NSData object.
let postData = postParams.data(using: String.Encoding.utf8)
// Initialize a mutable URL request object using the access token endpoint URL string.
let request = NSMutableURLRequest(url: NSURL(string: accessTokenEndPoint)! as URL)
// Indicate that we're about to make a POST request.
request.httpMethod = "POST"
// Set the HTTP body using the postData object created above.
request.httpBody = postData
// Add the required HTTP header field.
request.addValue("application/x-www-form-urlencoded;", forHTTPHeaderField: "Content-Type")
// Initialize a NSURLSession object.
let session = URLSession(configuration: URLSessionConfiguration.default)
// Make the request.
let task: URLSessionDataTask = session.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 == 200 {
// Convert the received JSON data into a dictionary.
do {
let dataDictionary = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as! Dictionary<String, Any>
print("dataDictionary\(dataDictionary)")
let accessToken = dataDictionary["access_token"] as! String
UserDefaults.standard.set(accessToken, forKey: "LIAccessToken")
UserDefaults.standard.synchronize()
print("START sentData")
DispatchQueue.main.async {
self.navigationController?.popViewController(animated: true)
}
}
catch {
print("Could not convert JSON data into a dictionary.")
}
}else{
print("cancel clicked")
}
}
task.resume()
}
the linked website is opening in the webview if i give all the credentials and click on the login it shows their ,no delegates are getting called and in the log i am getting the below error
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in may also be helpful.
2018-06-01 15:10:51.499017+0530 JobzTop Assessor[19856:3088129] CredStore - performQuery - Error copying matching creds. Error=-25300,
query={
class = inet;
"m_Limit" = "m_LimitAll";
ptcl = htps;
"r_Attributes" = 1;
sdmn = "Password Protected Area";
srvr = "jobz.com";
sync = syna;
}
if copy the request url and past it in the chrome the linkedin login page is opening and if i give details and hit on signin its open my host url in thw webpage.
Please find the below attached images.

How to integrate Linkedin Login in iOS Without SDK?

In iOS most of them integrated LinkedIn Login via SDK(if LinkedIn App is not installed in your iPhone/iPad you cant login, LinkedIn SDK return a message to install LinkedIn App).
But during Apple Review, there may be a chance to reject our app.
So the only solution is to handle two situation.
1.LinkedIn Login With SDK
2.LinkedIn Login without SDK ( Using OAuth 2.0)
Step 1
First you need to check LinkedIn App is installed or not in your iPhone/iPad.
isInstalled("linkedin://app") // function call
func isInstalled(appScheme:String) -> Bool{
let appUrl = NSURL(string: appScheme)
if UIApplication.sharedApplication().canOpenURL(appUrl! as NSURL)
{
return true
} else {
return false
}
}
Step 2
Create a webviewController.swift
import UIKit
class WebViewController: UIViewController,UIWebViewDelegate {
#IBOutlet weak var webView: UIWebView!
let linkedInKey = "xxxxxx"
let linkedInSecret = "xxxxxx"
let authorizationEndPoint = "https://www.linkedin.com/uas/oauth2/authorization"
let accessTokenEndPoint = "https://www.linkedin.com/uas/oauth2/accessToken"
override func viewDidLoad() {
super.viewDidLoad()
webView.delegate = self
self.startAuthorization()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func startAuthorization() {
let responseType = "code"
let redirectURL = "https://com.appcoda.linkedin.oauth/oauth".stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.alphanumericCharacterSet())!
let state = "linkedin\(Int(NSDate().timeIntervalSince1970))"
let scope = "r_basicprofile,r_emailaddress"
var authorizationURL = "\(authorizationEndPoint)?"
authorizationURL += "response_type=\(responseType)&"
authorizationURL += "client_id=\(linkedInKey)&"
authorizationURL += "redirect_uri=\(redirectURL)&"
authorizationURL += "state=\(state)&"
authorizationURL += "scope=\(scope)"
// logout already logined user or revoke tokens
logout()
// Create a URL request and load it in the web view.
let request = NSURLRequest(URL: NSURL(string: authorizationURL)!)
webView.loadRequest(request)
}
func logout(){
let revokeUrl = "https://api.linkedin.com/uas/oauth/invalidateToken"
let request = NSURLRequest(URL: NSURL(string: revokeUrl)!)
webView.loadRequest(request)
}
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
let url = request.URL!
if url.host == "com.appcoda.linkedin.oauth" {
if url.absoluteString!.rangeOfString("code") != nil {
let urlParts = url.absoluteString!.componentsSeparatedByString("?")
let code = urlParts[1].componentsSeparatedByString("=")[1]
requestForAccessToken(code)
}
}
return true
}
func requestForAccessToken(authorizationCode: String) {
let grantType = "authorization_code"
let redirectURL = "https://com.appcoda.linkedin.oauth/oauth".stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.alphanumericCharacterSet())!
// Set the POST parameters.
var postParams = "grant_type=\(grantType)&"
postParams += "code=\(authorizationCode)&"
postParams += "redirect_uri=\(redirectURL)&"
postParams += "client_id=\(linkedInKey)&"
postParams += "client_secret=\(linkedInSecret)"
// Convert the POST parameters into a NSData object.
let postData = postParams.dataUsingEncoding(NSUTF8StringEncoding)
// Initialize a mutable URL request object using the access token endpoint URL string.
let request = NSMutableURLRequest(URL: NSURL(string: accessTokenEndPoint)!)
// Indicate that we're about to make a POST request.
request.HTTPMethod = "POST"
// Set the HTTP body using the postData object created above.
request.HTTPBody = postData
// Add the required HTTP header field.
request.addValue("application/x-www-form-urlencoded;", forHTTPHeaderField: "Content-Type")
// Initialize a NSURLSession object.
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
// Make the request.
let task: NSURLSessionDataTask = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
// Get the HTTP status code of the request.
let statusCode = (response as! NSHTTPURLResponse).statusCode
if statusCode == 200 {
// Convert the received JSON data into a dictionary.
do {
let dataDictionary = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
print("dataDictionary\(dataDictionary)")
let accessToken = dataDictionary["access_token"] as! String
NSUserDefaults.standardUserDefaults().setObject(accessToken, forKey: "LIAccessToken")
NSUserDefaults.standardUserDefaults().synchronize()
print("START sentData")
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.navigationController?.popViewControllerAnimated(true)
})
}
catch {
print("Could not convert JSON data into a dictionary.")
}
}else{
print("cancel clicked")
}
}
task.resume()
}
}
Step 3
LinkedIn login Button Click
#IBAction func linkedinButtonClicked(sender: AnyObject) {
if (self.isInstalled("linkedin://app")){
// App installed
let permissions = [LISDK_BASIC_PROFILE_PERMISSION,LISDK_EMAILADDRESS_PERMISSION]
print("persmission end")
LISDKSessionManager.createSessionWithAuth(permissions, state: nil, showGoToAppStoreDialog: true, successBlock: { (returnState) -> Void in
let session = LISDKSessionManager.sharedInstance().session
LISDKAPIHelper.sharedInstance().getRequest("https://api.linkedin.com/v1/people/~:(id,first-name,last-name,email-address,picture-url,public-profile-url,industry,positions,location)?format=json", success: { (response) -> Void in
if let data = response.data.dataUsingEncoding(NSUTF8StringEncoding) {
if let dictResponse = try? NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers){
print("success")
}
}
}, error: { (error) -> Void in
print("LINKEDIN error\(error)")
})
}) { (error) -> Void in
print("error login linkedin")
}
}else{
// App not installed
print("App is not installed")
isBackFromWebViewCntr = true
let webViewCnt = self.storyboard!.instantiateViewControllerWithIdentifier("WebViewController") as UIViewController
self.navigationController?.pushViewController(webViewCnt, animated: true)
}
}
It's sad that even LinkedIn is not providing a documentation or a sample project for this problem, I went through so many web pages and finally found something useful in this link. This website provides both tutorial and sample code. Download the sample code and replace following values generated in your LinkedIn developer portal
let linkedInKey = "your LinkedIn key"
let linkedInSecret = "your LinkedIn secret"
let callBackURL = "your callback url"
In WKNavigationDelegate replace the host url with your host url (in my case its my callback url without "https://")
request.url?.host == "com.elsner.linkedin.oauth" // Change this to
request.url?.host == "your callback url's host"

How to Method POST authentication login with Oauth swift

i tried build login authentication without oauth is work and then i build other project only oauth code for request roken is work too, and now i want to make project authentication login with oauth swift. im really stuck what must i do.
this is my code im trying :
import UIKit
import OAuthSwift
class ViewController: UIViewController {
#IBOutlet weak var passwordTxt: UITextField!
#IBOutlet weak var phonenumberTxt: UITextField!
var oauthswift: OAuth1Swift?
let dataShared = UserDefaults.standard
let fileUrl = NSURL(string: "8villages://app/oauth-callback")
override func viewDidLoad() {
super.viewDidLoad()
requestToken()
}
#IBAction func doLogin(_ sender: Any) {
let phonenumber = phonenumberTxt.text
let password = passwordTxt.text
if(phonenumber == "" || password == "")
{
return
}
login(phonenumber!, password!)
}
func login(_ number:String, _ pswrd:String) {
let url = URL(string: "https://accounts.8villages.com/authentication")
let session = URLSession.shared
let request = NSMutableURLRequest(url: url!)
request.httpMethod = "POST"
let paramToSend = "identifier=" + number + "&password=" + pswrd
request.httpBody = paramToSend.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(data, response, error) in
guard let _:Data = data else
{
return
}
let json:Any?
do {
json = try JSONSerialization.jsonObject(with: data!, options: [])
}
catch {
return
}
guard let server_response = json as? NSDictionary else {
return
}
if let data_block = server_response["data"] as? NSDictionary {
if let session_data = data_block["session"] as? String {
let preference = UserDefaults.standard
preference.set(session_data, forKey: "session")
DispatchQueue.main.async(
execute:self.LoginDone
)//
}
}
})
task.resume()
}
func LoginDone() {
self.performSegue(withIdentifier: "loginView", sender: self)
}
func requestToken() {
print ("hei")
var is8VillagesInstalled: Bool = {
let appUrl = URL(string: "8villages://app")
return UIApplication.shared.canOpenURL(appUrl!)
}()
self.oauthswift = OAuth1Swift.init(consumerKey: "**********",
consumerSecret: "**********",
requestTokenUrl: "https://oauth.8villages.com/tokens/request-token",
authorizeUrl: "8villages://app/?oauth_token_secret=",
accessTokenUrl: "https://accounts.8villages.com/tokens/access-token")
self.oauthswift?.authorizeURLHandler = OAuthSwiftOpenURLExternally.sharedInstance
self.oauthswift?.allowMissingOAuthVerifier = true
self.oauthswift?.postOAuthRequestToken(
callbackURL: fileUrl as! URL,
success: { credentials, response, parameters in
print("credentials", credentials)
print("response =", response!)
print("parameters =", parameters)
// request ke api login
}, failure: { error in
print("the error is", error.description, error.errorCode)
});
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
so, i dont know how to authentication login with oauth. how to make them work.

Trying to append JSON items to array but not working

I am trying to serialize a GET request then make a movie object, then appending that movie object to a movies array which I will use to show info on the UI.
I am new and have struggled with this problem for some time now :(
If you look at the self.movies?.append(movie) shouldnt that work? I dont see any reasons as to when i try to get the first item i get fatal error index out of bounds which means I the Array is not filled yet.... Dont know what i am doing wrong :(
import UIKit
class ViewController: UIViewController {
var movies:[Movie]? = []
#IBOutlet weak var uiMovieTitle: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
getMovieData()
print(self.movies?.count)
setUI()
}
#IBAction func yesBtn(_ sender: UIButton) {
print(movies?[5].title ?? String())
}
#IBAction func seenBtn(_ sender: UIButton) {
}
#IBAction func noBtn(_ sender: UIButton) {
}
#IBOutlet weak var moviePoster: UIImageView!
let urlString = "https://api.themoviedb.org/3/discover/movie?api_key=935f539acbfed4b9e5534ddeed3fb57e&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=1&with_genres=12"
func getMovieData(){
//Set up URL
let todoEndPoint: String = "https://api.themoviedb.org/3/discover/movie?api_key=935f539acbfed4b9e5534ddeed3fb57e&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=1&with_genres=12"
guard let url = URL(string: todoEndPoint) else {
print("Cant get URL")
return
}
let urlRequest = URLRequest(url: url)
//Setting up session
let config = URLSessionConfiguration.default
let session = URLSession.shared
//Task setup
let task = session.dataTask(with: urlRequest) { (data, URLResponse, error) in
//Checking for errors
guard error == nil else{
print("Error calling GET")
print(error)
return
}
//Checking if we got data
guard let responseData = data else{
print("Error: No data")
return
}
self.movies = [Movie]()
do{//If we got data, if not print error
guard let todo = try JSONSerialization.jsonObject(with: responseData, options:.mutableContainers) as? [String:AnyObject] else{
print("Error trying to convert data to JSON")
return
}//if data is Serializable, do this
if let movieResults = todo["results"] as? [[String: AnyObject]]{
//For each movieobject inside of movieresult try to make a movie object
for moviesFromJson in movieResults{
let movie = Movie()
//If all this works, set variables
if let title = moviesFromJson["title"] as? String, let movieRelease = moviesFromJson["release_date"] as? String, let posterPath = moviesFromJson["poster_path"] as? String, let movieId = moviesFromJson["id"] as? Int{
movie.title = title
movie.movieRelease = movieRelease
movie.posterPath = posterPath
movie.movieId = movieId
}
self.movies?.append(movie)
}
}
}//do end
catch{
print(error)
}
}
////Do Stuff
task.resume()
}
func setUI(){
//uiMovieTitle.text = self.movies![0].title
//print(self.movies?[0].title)
}
}
my Movie class:
import UIKit
class Movie: NSObject {
var title:String?
var movieRelease: String?
var posterPath:String?
var movieId:Int?
var movieGenre:[Int] = []
//public init(title:String, movieRelease:String, posterPath:String,movieId:Int) {
// self.movieId = movieId
//self.title = title
//self.movieRelease = movieRelease
//self.posterPath = posterPath
//self.movieGenre = [movieGenre]
//}
}
getMovieData calls the network asynchronously. Your viewDidLoad invokes this, then calls setUI() - but the networking is still ongoing when setUI is called.
Instead, call setUI when the networking is complete - after the self.movies?.append(movie) line. The UI code will need to happen on the main thread. So...
for moviesFromJson... // your existing code
...
self.movies?.append(movie)
}
// Refresh UI now movies have loaded.
DispatchQueue.main.async {
setUI()
}
import UIKit
class ViewController: UIViewController {
var movies:[Movie]? = []
#IBOutlet weak var uiMovieTitle: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
getMovieDataCall(completionHandler: {data, error in self. getMovieDataCallBack(data: data, error: error)})
}
func getMovieDataCallBack(data: Data?, error: Error?) {
if error == nil {
let dictionary = try! JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! Dictionary<String, AnyObject>
//do your appending here and then call setUI()
print("dictionaryMovie \(dictionary)")
} else {
showAlertView("", error?.localizedDescription)
}
}
func getMovieDataCall(completionHandler: #escaping (Data?, Error?) -> Void)){
//Set up URL
let todoEndPoint: String = "https://api.themoviedb.org/3/discover/movie?api_key=935f539acbfed4b9e5534ddeed3fb57e&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=1&with_genres=12"
guard let url = URL(string: todoEndPoint) else {
print("Cant get URL")
return
}
let urlRequest = URLRequest(url: url)
//Setting up session
let config = URLSessionConfiguration.default
let session = URLSession.shared
//Task setup
let task = session.dataTask(with: urlRequest) { (data, URLResponse, error) in
if error != nil {
NSLog("GET-ERROR", "=\(error)");
completionHandler(nil, error)
} else {
let dataString = String(data: data!, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue))
print(dataString!)
completionHandler(data, nil)
}
task.resume()
}
func setUI(){
}

OAuth2, Swift 3, Instagram

There seem to be lots of changes at IG. Many OAuth2 repos, all seem to have bugs, or really not easily converted to Swift3. Wondering if anyone has a solution for moving to Swift3 and working with the latest changes at Instagram?
Solutions most welcome. OAuth2 implementation seems to one of the more complicated things out there. Surprised that IG has not offered their own example docs on how to do this with iOS. They only have docs for web based solutions.
Maybe something brewing there? Zillions of coders they have on staff. But for now, on the hunt for a (dare I say?) simple solution.
thanks a million. :-)
For Swift 3:
Update: April 17 2017: Because of broken dependencies, the installation by Pods is no longer working. Therefore I have ripped the needed content and created a new Github project using a Bridging Header and stored all the needed files within the project. If you clone or download the github project, you'll instantly be able to login to Instagram.
To use those files in your project, simply drag and drop all the files from the SimpleAuth folder to your project, make sure to mark copy item if needed
Also you need to disable Disable implicit oAuth within the Instagram developer console.
Then you either copy/paste my code from the Bridging Header into your or you use mine. Set the Bridging Header at the Target's Build Settings.
Everything else works as before:
I have a struct for the Instagram Account:
struct InstagramUser {
var token: String = ""
var uid: String = ""
var bio: String = ""
var followed_by: String = ""
var follows: String = ""
var media: String = ""
var username: String = ""
var image: String = ""
}
The function to receive the token:
typealias JSONDictionary = [String:Any]
var user: InstagramUser?
let INSTAGRAM_CLIENT_ID = "16ee14XXXXXXXXXXXXXXXXXXXXXXXXX"
let INSTAGRAM_REDIRECT_URI = "http://www.davidseek.com/just_a_made_up_dummy_url" //just important, that it matches your developer account uri at Instagram
extension ViewController {
func connectInstagram() {
let auth: NSMutableDictionary = ["client_id": INSTAGRAM_CLIENT_ID,
SimpleAuthRedirectURIKey: INSTAGRAM_REDIRECT_URI]
SimpleAuth.configuration()["instagram"] = auth
SimpleAuth.authorize("instagram", options: [:]) { (result: Any?, error: Error?) -> Void in
if let result = result as? JSONDictionary {
var token = ""
var uid = ""
var bio = ""
var followed_by = ""
var follows = ""
var media = ""
var username = ""
var image = ""
token = (result["credentials"] as! JSONDictionary)["token"] as! String
uid = result["uid"] as! String
if let extra = result["extra"] as? JSONDictionary,
let rawInfo = extra ["raw_info"] as? JSONDictionary,
let data = rawInfo["data"] as? JSONDictionary {
bio = data["bio"] as! String
if let counts = data["counts"] as? JSONDictionary {
followed_by = String(describing: counts["followed_by"]!)
follows = String(describing: counts["follows"]!)
media = String(describing: counts["media"]!)
}
}
if let userInfo = result["user_info"] as? JSONDictionary {
username = userInfo["username"] as! String
image = userInfo["image"] as! String
}
self.user = InstagramUser(token: token, uid: uid, bio: bio, followed_by: followed_by, follows: follows, media: media, username: username, image: image)
} else {
// this handles if user aborts or the API has a problem like server issue
let alert = UIAlertController(title: "Error!", message: nil, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
if error != nil {
print("Error during SimpleAuth.authorize: \(error)")
}
}
}
}
Instagram also says:
Important
Even though our access tokens do not specify an expiration time, your
app should handle the case that either the user revokes access, or
Instagram expires the token after some period of time. If the token is
no longer valid, API responses will contain an
“error_type=OAuthAccessTokenException”. In this case you will need to
re-authenticate the user to obtain a new valid token. In other words:
do not assume your access_token is valid forever.
So handle the case of receiving the OAuthAccessTokenException
This Code bellow I use for facebook and google+, I think it'ill work for Instagram too, maybe some adjusts.
import UIKit
class Signup: UIViewController, UIWebViewDelegate {
let GOOGLE_ID = "xxxxxx.apps.googleusercontent.com"
let GOOGLE_SECRET = "xxxxxxx";
let GOOGLE_REDIRECT_URI="http://yourdomain.com/api/account/googlecallback"
let GOOGLE_TOKEN_URL = "https://accounts.google.com/o/oauth2/token";
let GOOGLE_OAUTH_URL = "https://accounts.google.com/o/oauth2/auth";
let GOOGLE_OAUTH_SCOPE = "profile email";
let GOOGLE_GET_PROFILE = "https://www.googleapis.com/userinfo/v2/me";
let FACEBOOK_ID = "xxxxx";
let FACEBOOK_REDIRECT_URI = "http://yourdomain.com/api/account/facebookcallback";
let FACEBOOK_OAUTH_URL = "https://www.facebook.com/dialog/oauth?client_id=";
let FACEBOOK_OAUTH_SCOPE = "public_profile,email"
let FACEBOOK_GET_PROFILE = "https://graph.facebook.com/me?access_token="
var currentURL: String = ""
var queryString: String = ""
var receivedToken: String = ""
var authCode: String = ""
var authComplete = false
var webV:UIWebView = UIWebView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
#IBAction func google(_ sender: AnyObject) {
AppVars.Provider = "Google"
webV.delegate = self
let url = GOOGLE_OAUTH_URL + "?redirect_uri=" + GOOGLE_REDIRECT_URI + "&response_type=code&client_id=" + GOOGLE_ID + "&scope=" + GOOGLE_OAUTH_SCOPE
let urlString :String = url.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
webV.loadRequest(URLRequest(url: URL(string:urlString)!))
self.view.addSubview(webV)
}
#IBAction func facebook(_ sender: AnyObject) {
AppVars.Provider = "Facebook"
webV.delegate = self
let url = FACEBOOK_OAUTH_URL + FACEBOOK_ID + "&redirect_uri=" + FACEBOOK_REDIRECT_URI + "&scope=" + FACEBOOK_OAUTH_SCOPE + "&display=popup&response_type=token"
let urlString :String = url.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
webV.loadRequest(URLRequest(url: URL(string:urlString)!))
self.view.addSubview(webV)
}
func webView(_ webView: UIWebView, didFailLoadWithError error: Error) {
self.showAlert(self, message: "Internet is not working")
}
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
return true;
}
func webViewDidStartLoad(_ webView: UIWebView) {
}
func webViewDidFinishLoad(_ webView: UIWebView) {
currentURL = (webView.request?.url!.absoluteString)!
if AppVars.Provider == "Google" {
googleSignup((webView.request?.url!)!)
} else {
facebookSignup((webView.request?.url!)!)
}
}
func googleSignup (_ returnCode: URL) {
let url = String(currentURL)
if (url?.range(of: "?code=") != nil && authComplete != true) {
authCode = getQueryItemValueForKey("code", url: returnCode)!
authComplete = true
let paramString = "code=" + authCode + "&client_id=" + GOOGLE_ID + "&client_secret=" +
GOOGLE_SECRET + "&redirect_uri=" + GOOGLE_REDIRECT_URI + "&grant_type=authorization_code"
self.requestServer(urlSource: GOOGLE_TOKEN_URL, params: paramString, requestType: "POST") { (dataResult, errorResult) -> () in
if errorResult != nil {
self.showAlert(self, message: "Internet is not working")
} else {
let dataString:NSString = NSString(data: dataResult as! Data, encoding: String.Encoding.utf8.rawValue)!
let dataResult2 = dataString.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: false)!
do {
let jsonDict = try JSONSerialization.jsonObject(with: dataResult2, options: .allowFragments) as! [String:Any]
if let token = jsonDict["access_token"] as? String {
self.requestServerSignup(self.GOOGLE_GET_PROFILE, param: token, requestType: "GET") { (dataResult, errorResult) -> () in
if errorResult != nil {
self.showAlert(self, message: "Internet is not working")
} else {
let dataString:NSString = NSString(data: dataResult as! Data, encoding: String.Encoding.utf8.rawValue)!
let dataResult2 = dataString.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: false)!
do {
let jsonDict = try JSONSerialization.jsonObject(with: dataResult2, options: .allowFragments) as! [String:Any]
if let name = jsonDict["name"] as? String {
AppVars.NameLogin = name
AppVars.PictureLogin = jsonDict["picture"] as! String
AppVars.EmailLogin = jsonDict["email"] as! String
self.performSegue(withIdentifier: "externalLoginSegue", sender: self)
// show picture, email and name for checking profile
}
} catch {
self.showAlert(self, message: "Internet is not working")
}
}
}
}
} catch {
self.showAlert(self, message: "Internet is not working")
}
}
}
self.webV.removeFromSuperview()
}
}
func facebookSignup(_ returnCode: URL) {
let url = String(currentURL)
if (url?.range(of: "access_token=") != nil && authComplete != true) {
let url2: String = returnCode.absoluteString.replacingOccurrences(of: "#access_token", with: "access_token")
let url3: URL = URL(string: url2)!
authCode = getQueryItemValueForKey("access_token", url: (url3))!
authComplete = true
let paramString = "code=" + authCode + "&client_id=" + GOOGLE_ID + "&client_secret=" +
GOOGLE_SECRET + "&redirect_uri=" + GOOGLE_REDIRECT_URI + "&grant_type=authorization_code"
self.requestServer(urlSource: self.FACEBOOK_GET_PROFILE + authCode + "&fields=name,picture,email", params: paramString, requestType: "GET") { (dataResult, errorResult) -> () in
if errorResult != nil {
self.showAlert(self, message: "Internet is not working")
} else {
let dataString:NSString = NSString(data: dataResult as! Data, encoding: String.Encoding.utf8.rawValue)!
let dataResult2 = dataString.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: false)!
do {
let jsonDict = try JSONSerialization.jsonObject(with: dataResult2, options: .allowFragments) as! [String:Any]
if let name = jsonDict["name"] as? String {
AppVars.NameLogin = name
if let picture = jsonDict["picture"] as? [String:Any] {
if let dataPicture = picture["data"] as? [String:Any] {
if let url = dataPicture["url"] as? String {
AppVars.PictureLogin = url
}
}
}
AppVars.EmailLogin = jsonDict["email"] as! String
self.performSegue(withIdentifier: "externalLoginSegue", sender: self)
// show picture, email and name for checking profile
}
} catch {
self.showAlert(self, message: "Internet is not working")
}
}
}
self.webV.removeFromSuperview()
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func getQueryItemValueForKey(_ key: String, url: URL) -> String? {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
return nil
}
guard let queryItems = components.queryItems else { return nil }
return queryItems.filter {
$0.name == key
}.first?.value
}
func requestServer(urlSource:String, params:String, requestType:String, result:#escaping (_ dataResult:NSData?, _ errorResult:NSError?) -> ()) {
let url: URL = URL(string: urlSource)!
var request = URLRequest(url:url)
request.httpMethod = requestType
request.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
if params.characters.count > 0 {
request.httpBody = params.data(using: String.Encoding.utf8)
}
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) -> Void in
DispatchQueue.main.async(execute: { () -> Void in
if error == nil {
result(data as NSData?, nil)
} else {
result(nil, error as NSError?)
}
})
}.resume()
}
}

Resources