Unable to get Authcode after google SignIn in iOS (swift) - ios

Problem:
In iOS app I am using google signIn to facilitate the process of getting the authCode, which later is sent into the server and it is exchanged for a refresh_token in google calendar API. The bad news is that user.serverAuthCode is always nil.
What happens now:
I have followed the google's documentation for implementing it. At this moment when I click the google sign-in button I go to a page where I fill in my credentials and I consent to all the scopes. Then it redirects me back to iOS app but I don't get the authCode.
Code:
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate{
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if (error == nil) {
print("Authcode is : \(user.serverAuthCode)") // This is nil :(
}
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
GIDSignIn.sharedInstance().clientID = "MY_CLIENT_ID"
GIDSignIn.sharedInstance().delegate = self
GIDSignIn.sharedInstance().scopes.append("https://www.googleapis.com/auth/calendar")
GIDSignIn.sharedInstance().scopes.append("https://www.googleapis.com/auth/calendar.events")
return true
}
}
I've also added URL type in target info as described in google's documentation
Lastly I have a viewController which uses GIDSignInButton to start the log-in process.
Is there anything I am missing? I took a look at this post but I could not solve the problem. Thank you for your time!

After some time spending on searching the solution was found :)
Problem fixed after adding:
GIDSignIn.sharedInstance()?.serverClientID = "SERVER_CLIENT"
If you don't know how to get these: SERVER_CLIENT vs CLIENT_ID

Related

Swift 5 App - AdMob returns Timeout (XCode)

I want to show a Reward Ad (Google AdMob) in my swift app. (XCode 11.4, Swift 5, Storyboard), but whenever I load the ad it shows the following error message in the console:
Error Domain=com.google.admob Code=5
"Request Error: The Google Ad request was unable to be fulfilled before a timeout occurred."
UserInfo={NSLocalizedDescription=Request Error: The Google Ad request was unable to be fulfilled before a timeout occurred., gad_response_info=<GADResponseInfo: 0x6000030f8090>}
I basically followed the tutorial from Google AdMob Tutorial.
I added my GADApplicationIdentifier to the info.plist
In the AppDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
GADMobileAds.sharedInstance().start(completionHandler: nil)
}
In the ViewController I followed the steps from the new RewardAd Tutorial and basically copied and pasted it, except for replacing the AdId with my own (I'm running it in a Simulator, so using own AdID's should not be a problem if I understood correctly...)
This is where it fails:
override func viewDidLoad() {
super.viewDidLoad()
rewardedAd = GADRewardedAd(adUnitID: adId)
rewardedAd?.load(GADRequest()) { error in
if let error = error {
print("Error while loading ad: \(error.description)")
// Timeout occurs here!!!
} else {
if self.rewardedAd?.isReady == true {
self.rewardedAd?.present(fromRootViewController: self, delegate:self)
}
}
}
}
Any idea why I get the timeout?
Note:
I also created a sample SwiftUI app with the same rewarded app structure, which also gets the timeout
and yes my internet connection works just fine
I tried the example AdIds from AdMob too, then I get the following error:
Error while loading ad: Error Domain=com.google.admob Code=1
"Request Error: No ad to show."
UserInfo={NSLocalizedDescription=Request Error: No ad to show., gad_response_info=<GADResponseInfo: 0x6000018f4d80>}
I used in the 'info.plist' : ca-app-pub-3940256099942544~1458002511
and for the AdID : ca-app-pub-3940256099942544/1712485313 both from the above mentioned tutorial pages from Google.
I think you should use your own appID with test Ad ID.

How to fix `The user canceled the sign-in flow.` with Sign In with Google on iOS?

I am integrating Sign in with Google with iOS Firebase SDK. When a user taps on the GIDSignIn button, the app shows an alert showing "MyAPP" want to use Google.com for Sign in, but the alert dismisses quickly. The error I get in the didSignInFor user: GIDGoogleUser method as follows:
2019-02-21 16:54:13.104279+0530 MyApp[18743:185089] [Warning] Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior (<SFAuthenticationViewController: 0x7fa327945800>)
[DEBUG] Google sign-in error: Error Domain=com.google.GIDSignIn Code=-5 "The user canceled the sign-in flow." UserInfo={NSLocalizedDescription=The user canceled the sign-in flow.}
I have followed the Authenticate Using Google Sign-In on iOS documentation.
I also tried the answer at Google Sign-In crashes on iOS 9 attempting to call canOpenURL, but it is not working.
The AppDelegate implements GIDSignInUIDelegate as well.
If I change the button from GIDSignInButton to UIButton, it works, but the style is lost.
I also faced the issue before. The GIDSignInButton object don't need addTarget in code or linking #IBAction. The GIDSignInButton class already handle that action for us.
If you are trying to to run GIDSignIn.sharedInstance()?.signIn() in #IBAction. You can run GIDSignIn.sharedInstance()?.presentingViewController = self just before signIn instead of insideviewDidLoad().
#IBAction func googleLogin(_ sender: UIButton) {
GIDSignIn.sharedInstance()?.presentingViewController = self
GIDSignIn.sharedInstance()?.signIn()
}
In Appdelegate try using the below code
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
let result = GIDSignIn.sharedInstance().handle(url,
sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String,
annotation: options[UIApplication.OpenURLOptionsKey.annotation])
return result
}

Quickblox Webrtc Video Calling Swift - Documentation understanding

I am trying to integrate the Quickblox Webrtc Video Calling feature into a iOS Swift App. However, i'm having a lot of trouble with their SDK & api documentation, and it seems they don't have a tech team to help people with questions about their platforms, so maybe we can all help each other, so here are a few questions that I've noticed a lot of people have been asking on both StackOverFlow and Github regarding their webrtc SDK. Please restrict answers to the Swift language. The docs link is
http://quickblox.com/developers/SimpleSample-videochat-ios
My code so far:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
//Firebase config
FIRApp.configure()
//Quickblox config
QBSettings.setApplicationID(xxxxx)
QBSettings.setAuthKey("xxxxxxxxxxx")
QBSettings.setAuthSecret("xxxxxxxx-xxxx")
QBSettings.setAccountKey("xxxxxxxxxxxxxxxxxxxx")
return true
}
Thats my appdelegate.swift now for the part that giving me problems the actual videochatviewcontroller. The documentation is very vague at the start all is says is:
// Initialize QuickbloxWebRTC and configure signaling
// You should call this method before any interact with
QuickbloxWebRTC QBRTCClient.initializeRTC() // Call this method when
you finish your work with QuickbloxWebRTC
QBRTCClient.deinitializeRTC()
I do not know if I am to call this in my appdelegate.swift or if I should call this in VideoChatViewController's viewDidLoad method or should I create a new method altogether?
Secondly,the docs say to CALL USERS use this method, but its not a method, just random variables, also it doesn't tell tell whether it goes to the viewDidLoad or to a newly created method :
QBRTCClient.instance().addDelegate(self) // self class must conform to QBRTCClientDelegate protocol
// 2123, 2123, 3122 - opponent's
let opponentsIDs = [3245, 2123, 3122]
let newSession = QBRTCClient.instance().createNewSessionWithOpponents(opponentsIDs, withConferenceType: QBRTCConferenceType.Video)
// userInfo - the custom user information dictionary for the call. May be nil.
let userInfo :[String:String] = ["key":"value"]
newSession.startCall(userInfo)
Next, they are vague regarding the method to receive a new session, below they refer to self.session which they never explain where this variable is from or what it consist of
func didReceiveNewSession(session: QBRTCSession!, userInfo: [NSObject : AnyObject]!) {
if self.session != nil {
// we already have a video/audio call session, so we reject another one
// userInfo - the custom user information dictionary for the call from caller. May be nil.
let userInfo :[String:String] = ["key":"value"]
session.rejectCall(userInfo)
}
else {
self.session = session
}
}
Does quickblox require authenticated Quickblox users to use their webrtc or can I Authenticate users with Firebase or parse?
Where do I use QBRTCConfig in the appdelegate or the viewDidLoad? I have tried both and have seen it used in both methods.

iOS swift You must specify clientID Exception in google integration

Code:
let signIn = GPPSignIn.sharedInstance()
signIn.shouldFetchGooglePlusUser = true
signIn.clientID = "912597493260-qg351fl8olmnmjl8qobos8n6u909jp0o.apps.googleusercontent.com"
signIn.scopes = [kGTLAuthScopePlusLogin];
signIn.trySilentAuthentication();
GIDSignIn.sharedInstance().signInSilently()
signIn.delegate = self
due to uncaught exception 'NSInvalidArgumentException', reason: 'You must specify |clientID| for |GIDSignIn|
I double checked my code.Even i set client-id getting this exception.Where i went wrong?any help will be appreciated.thanks in advance
I was following Google's own guide for adding Sign-In here. I followed it step by step - integrated the google configuration file too. As per the guide, if the configuration file was included, setting the client id manually was not required. Unfortunately, I encountered exactly the same error when I run the app and hit the Sign-In button:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'You must specify |clientID| for |GIDSignIn|'
Solution:
For some reason, clientID was not automatically picked from the configuration file. We should instead configure the GIDSignIn object directly, (using the client ID found in the GoogleService-Info.plist file) in the app delegate's application:didFinishLaunchingWithOptions: method:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Initialize sign-in
var configureError: NSError?
GGLContext.sharedInstance().configureWithError(&configureError)
assert(configureError == nil, "Error configuring Google services: \(configureError)")
GIDSignIn.sharedInstance().clientID = "Cliend id From GoogleService-Info.plist file"
GIDSignIn.sharedInstance().delegate = self
return true
}
Also, if you are using Firebase, you can do it this way too:
GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
It looks like the auto-generated config file, GoogleService-Info.plist, will include the wrong credentials by default; it includes the Web Client credentials instead of the iOS app credentials.
You need to correct the Client ID and the Reverse Client ID in the GoogleService-Info.plist.
Since these credentials are also used in your app's URLSchemes, you need to correct this there too.
I was also facing the same issue. I followed every step as per the documentation by https://firebase.google.com/docs/auth/ios/google-signin#swift_9 .
At last, I tried adding Client ID manually on my Controller's viewDidLoad and it worked after a long struggle.
Refer the code below. Replace your project specific Client-ID from GoogleService-info.plist in place of ClientID :
class IntroController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
GIDSignIn.sharedInstance().clientID = "*ClientID*"
GIDSignIn.sharedInstance()?.presentingViewController = self
GIDSignIn.sharedInstance().signIn()
}
}
The clientId definitely does get picked up from the .plist file. If it appears not to be, it is likely that your code is attempting to use the sign-in object before it has been properly configured. Set a breakpoint on your configureWithError line, and make sure that it gets hit before any attempt to set a delegate, perform silent sign-in, etc.
Looks like the sign in method has now been updated by google, I was implementing the Google Calendar for iOS app and I found the following code for Sign In:
func applicationDidFinishLaunching(_ application: UIApplication) {
// Initialize sign-in
var configureError: NSError?
GGLContext.sharedInstance().configureWithError(&configureError)
assert(configureError == nil, "Error configuring Google services: \(configureError!)")
}
in their document which gave the same error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'You must specify |clientID| for |GIDSignIn|'
I took the lines which were inside:
func applicationDidFinishLaunching(_ application: UIApplication)
and put them in this method and sign in worked:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
Code for refernce:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// Initialize sign-in
var configureError: NSError?
GGLContext.sharedInstance().configureWithError(&configureError)
assert(configureError == nil, "Error configuring Google services: \(configureError!)")
return true
}
You may need to obtain GoogleService-Info.plist from https://console.firebase.google.com rather than https://console.developers.google.com/.
Using Firebase remember also that you have to call Firebase.configure() function before you set the clientId. Otherwise, it won't work.

User able to access app even after removing app from Facebook App Settings

I have an app that uses both Parse and FacebookSDK. Currently running into the problem where the user is still able to access my app even when going to their Facebook account App settings and removing my app from accessing their Facebook data.
So:
1: New user does a fresh install.
2: User signs up via Facebook Login and accesses app
3: User goes to Facebook.com>App Settings>Remove my app
4: Back on iPhone, user closes app, reopens and gains access again
Here is where I check the current status of the user and let them have access if they hold a currentAccessToken
override func viewDidLoad() {
super.viewDidLoad()
if FBSDKAccessToken.currentAccessToken() != nil{
moveToNextView() //Segue to next viewController
}
}
And my AppDelegate.swift:
import UIKit
import Parse
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
Parse.setApplicationId("ID", clientKey:"KEY")
PFFacebookUtils.initializeFacebookWithApplicationLaunchOptions(launchOptions)
FBSDKProfile.enableUpdatesOnAccessTokenChange(true)
return FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
}
func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject?) -> Bool {
return FBSDKApplicationDelegate.sharedInstance().application(application, openURL: url, sourceApplication: sourceApplication, annotation: annotation)
}
}
I'm guessing the currentAccessToken is cached somewhere on the app or device, but unsure about how to clear/refresh that cache.
I have found the solution to this problem. It's not as straight forward as one would think. The Facebook developer console allows you to set a Deauthorize Callback URL which can be found here:
Select your app in the Facebook developer dashboard
Click Facebook Login on the side menu
Click Settings
Then uou can supply Deauthorize Callback URL
Whenever someone removes your app from facebook it generates a POST to your callback URL with a signed request parameter. You can parse the signed request to retrieve the user's facebookid. Once you have the user's facebookid you can update a record in your database that you can use to check on their next login.
Here is an example in PHP of how to handle the deauthorize callback
<?php
function parse_signed_request($signed_request) {
list($encoded_sig, $payload) = explode('.', $signed_request, 2);
$secret = "appsecret"; // Use your app secret here
// decode the data
$sig = base64_url_decode($encoded_sig);
$data = json_decode(base64_url_decode($payload), true);
// confirm the signature
$expected_sig = hash_hmac('sha256', $payload, $secret, $raw = true);
if ($sig !== $expected_sig) {
error_log('Bad Signed JSON signature!');
return null;
}
return $data;
}
$signed_request = $_POST['signed_request'];
$data = parse_signed_request($signed_request);
$user_id = $data['user_id'];
//now use the user_id to look up your user in your database
//update a field called deauthorized to true so that you
//can check this value upon next login
?>
Now next time you check the your access token include a check in your database to see if the user has removed the app.
- (void)viewDidLoad
{
[super viewDidLoad];
if ([FBSDKAccessToken currentAccessToken]) {
// User is logged in, but they may have
// removed the app so we have to check our
// db using a REST API call to see if is deauthorized
// If the user has deauthorized then log them out
// send them back to the login/signup page
// if not, carry on as usual
//also don't forget to set the deauthorized value to 0
//whenever a user successfully logs into facebook
}
}
Hopefully this will help someone in the future.

Resources