QuickBlox video chat: QBRequest.logInWithUserEmail vs QBChat.instance().connectWithUser - ios

I have a simple QuickBlox chat app built by following the iOS tutorial:
http://quickblox.com/developers/Sample-webrtc-ios#Sources
I've successfully created a user and logged them in. However, I run into an error when I try to initiate a session: "You have to be logged in in order to use Chat API".
let newSession: QBRTCSession = QBRTCClient.instance().createNewSessionWithOpponents(["12498970"], withConferenceType: QBRTCConferenceType.Video)
I'm able to resolve this by adding QBChat.instance().connectWithUser each time I open it:
QBChat.instance().connectWithUser(user!) { (error) in
if error != nil {
print("error: \(error)")
}
else {
print("login to chat succeeded")
}
}
But somehow this seems weird because I have to either cache the password or prompt the user to enter their password each time the app opens. It seems strange that the QBSession.currentSession().currentUser is still valid, but the QBChat user has been invalidated. What is the best practice for accomplishing this? In all the samples, the passwords are hardcoded. This doesn't seem like a great solution.

I ended up following examples in Q-municate, which is an app the Quickblox folks built to basically demonstrate their whole package, as well as provide an actual solution for whatever your chat needs are. I have some other custom stuff and don't need a lot of the functionality so I'm still trying to dig through the details of how they implement it. The link to Q-municate:
http://quickblox.com/developers/Q-municate#1._Get_the_source_code.
In their login flow, they use the QMApi module written for Q-municate:
[[QMApi instance] loginWithEmail:email
password:password
rememberMe:weakSelf.rememberMeSwitch.on
completion:^(BOOL success)
{
[SVProgressHUD dismiss];
if (success) {
[[QMApi instance] setAutoLogin:weakSelf.rememberMeSwitch.on
withAccountType:QMAccountTypeEmail];
[weakSelf performSegueWithIdentifier:kTabBarSegueIdnetifier
sender:nil];
}
}];
In loginWithEmail, their settingsManager caches this login:
[weakSelf.settingsManager setLogin:email andPassword:password];
which is actually just a way to cache the password in SSKeyChain.
[SSKeychain setPassword:password forService:kQMAuthServiceKey account:login];
Later, when you return to the app, they call autologin:
if (!self.isAuthorized) {
if (self.settingsManager.accountType == QMAccountTypeEmail && self.settingsManager.password && self.settingsManager.login) {
NSString *email = self.settingsManager.login;
NSString *password = self.settingsManager.password;
[self loginWithEmail:email password:password rememberMe:YES completion:completion];
}
else if (self.settingsManager.accountType == QMAccountTypeFacebook) {
[self loginWithFacebook:completion];
}
else {
if (completion) completion(NO);
}
}
else {
if (completion) completion(YES);
}
where self.settingsManager.password pulls the password from SSKeychain:
NSString *password = [SSKeychain passwordForService:kQMAuthServiceKey account:self.login];
autoLogin is called when the main chat tab is loaded. That makes our classic call to connectToChat:
[[QMApi instance] autoLogin:^(BOOL success) {
if (!success) {
[[QMApi instance] logoutWithCompletion:^(BOOL succeed) {
//
[weakSelf performSegueWithIdentifier:#"SplashSegue" sender:nil];
}];
} else {
// subscribe to push notifications
[[QMApi instance] subscribeToPushNotificationsForceSettings:NO complete:^(BOOL subscribeToPushNotificationsSuccess) {
if (!subscribeToPushNotificationsSuccess) {
[QMApi instance].settingsManager.pushNotificationsEnabled = NO;
}
}];
[weakSelf connectToChat];
}
}];
So technically the docs are doing the right thing by logging in to chat every time the app opens and chat is no longer connected. There's just a much more complex but secure way to store that password so the user doesn't have to reenter it.
TLDR: The way it works in my code (and in swift) is:
On login:
QBRequest.logInWithUserEmail(email, password: password, successBlock: { (response, user) in
SSKeychain.setPassword(password, forService: "kMyAppLoginServiceKey", account: email)
}) { (errorResponse) in
print("Error: \(errorResponse)")
self.simpleAlert("Could not log in", defaultMessage: nil, error: nil)
}
Whenever the chat view loads:
if !QBChat.instance().isConnected() {
QBRTCClient.initializeRTC()
QBRTCClient.instance().addDelegate(self)
let user = QBSession.currentSession().currentUser
let password = SSKeychain.passwordForService("kMyAppLoginServiceKey", account: user?.email!)
user!.password = password
QBChat.instance().addDelegate(self)
QBChat.instance().connectWithUser(user!) { (error) in
if error != nil {
print("error: \(error)")
}
else {
print("login to chat succeeded")
}
}
}

Related

Unable to localize Face Id popup for non-matching face

It seems like Face Id is ignoring localizedFallbackTitle and localizedReason. However localizedCancelTitle is working fine. Does anyone know how to get it work?
My code:
LAContext *context = [[LAContext alloc] init];
if ([context respondsToSelector:#selector(setLocalizedCancelTitle:)]) {
context.localizedCancelTitle = [Language get:CANCEL alter:nil];
}
if ([context respondsToSelector:#selector(setLocalizedFallbackTitle:)])
{
context.localizedFallbackTitle = [Language get:TRY_AGAIN alter:nil];
}
NSError *error = nil;
if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&error]) {
[context evaluatePolicy:LAPolicyDeviceOwnerAuthentication
localizedReason:[Language get:AUTHRNTICATE_USING_YOUR_FACE alter:nil] reply:^(BOOL success, NSError *error) {
//code
}
Screenshot:
I want to localize everything on this popup if possible.
Note: Attached screenshot is taken on simulator. I have also checked
it on real device but the result is same. Also, for Touch id it is working properly.
According to this Post, there is no API for changing the Reason in between the auth process.
localizedReason
The app-provided reason for requesting authentication, which displays in > the authentication dialog presented to the user.
You can use BiometricAuthentication to show your message.
BioMetricAuthenticator.authenticateWithBioMetrics(reason: "", success: {
// authentication successful
}, failure: { [weak self] (error) in
// do nothing on canceled
if error == .canceledByUser || error == .canceledBySystem {
return
}
// device does not support biometric (face id or touch id) authentication
else if error == .biometryNotAvailable {
self?.showErrorAlert(message: error.message())
}
// show alternatives on fallback button clicked
else if error == .fallback {
// here we're entering username and password
self?.txtUsername.becomeFirstResponder()
}
// No biometry enrolled in this device, ask user to register fingerprint or face
else if error == .biometryNotEnrolled {
self?.showGotoSettingsAlert(message: error.message())
}
// Biometry is locked out now, because there were too many failed attempts.
// Need to enter device passcode to unlock.
else if error == .biometryLockedout {
// show passcode authentication
}
// show error on authentication failed
else {
self?.showErrorAlert(message: error.message())
}
})

hypertrack - always show Device disconnected on dashboard objective c iOS

i am creating action "visit" after new user create in objective c iOS but on dashboard device disconnected show against user nor can see location through url or action id. here is screen short.
-(void)createUser{
[HyperTrack initialize:#"......."];
[HyperTrack requestAlwaysAuthorizationWithCompletionHandler:^(BOOL authorized) {
//handle authorization status, if needed
}];
[HyperTrack requestMotionAuthorization];
[HyperTrack getOrCreateUser:#"test1" _phone:#"12345" :#"12" completionHandler:^(HyperTrackUser * _Nullable user, HyperTrackError * _Nullable error) {
if (user) {
// Handle getOrCreateUser API success here
NSLog(#"User Created");
NSLog(#"%#",user.id);
[self createAction:user.id];
} else {
// Handle createUser error here, if required
NSLog(#"User Created error %#",error);
}
}];
}
-(void)createAction:(NSString *)userId{
HyperTrackActionParams* actionParams = [[HyperTrackActionParams alloc] init];
[actionParams setType:#"visit"];
[actionParams setUserId:userId];
[HyperTrack createAndAssignAction:actionParams :^(HyperTrackAction * action, HyperTrackError * error) {
if (error) {
// Handle createAction API error here
return;
}
if (action) {
// Handle createAction API success here
NSLog(#"%#",action.id);
NSLog(#"%#",action.trackingUrl);
}
}];
}
My pod was outdated. I reinstalled pod with pod 'HyperTrack', '~> 0.7'. Now its working fine.

iOS Parse.com Facebook Login - valid email address error

Using the sample code, I continually get a nil user and an error in the block for the login:
2015-11-09 17:39:16.017[90448:3746935] [Error]: You must use a valid email address. (Code: 142, Version: 1.9.1)
Printing description of error:
Error Domain=Parse Code=142 "You must use a valid email address." UserInfo=0x7fb403c39f20 {code=142, temporary=0, error=You must use a valid email address., NSLocalizedDescription=You must use a valid email address.}
Here's the code:
FBSDKAccessToken *accessToken = [FBSDKAccessToken currentAccessToken];
if (accessToken)
{
[PFFacebookUtils logInInBackgroundWithAccessToken:accessToken block:^(PFUser * _Nullable user, NSError * _Nullable error) {
if (!user) {
NSLog(#"Facebook login cancelled.");
} else {
NSLog(#"User now has publish permissions!");
[self loadFBAccountInfo];
}
}];
}
else
{
[PFFacebookUtils logInInBackgroundWithReadPermissions:#[ #"email", #"public_profile" ] block:^(PFUser *user, NSError *error) { // , #"publish_actions"
if (!user) {
NSLog(#"Facebook login cancelled.");
} else {
NSLog(#"User now has publish permissions!");
[self loadFBAccountInfo];
}
}];
}
Any help is appreciated!
Sorry, the error was in the cloud code which required an email address - when a user is first created via the Facebook route, it doesn't have an email address...
Parse.Cloud.beforeSave(Parse.User, function(request, response) {
var email = request.object.get('email');
if (!email) {
response.error('You must use a valid email address.');
} else {
response.success();
}
});

PFUser currentUser returns nil after Facebook Login with Parse SDK

I'm currently developing an iOS App that allows login via Facebook/Parse. The login flow goes like this:
login button is touched and Facebook login window opens;
user logs in and gives permission on Facebook;
app retrieves Facebook accessToken information;
app performs [PFFacebookUtils logInInBackGroundWithAccessToken:(accessToken) ^block{}] to make the Parse login authentication;
app goes to a view and tries to perform a query to fetch data associated with that user from Parse.
The problem is: after logInInBackgroundWithAccessToken is performed, [PFUser currentUser] returns nil, and I get a "Cannot do a comparison query for type: (null)" error.
Ok! Solved it!
After digging into the code a little further I realized that the
[PFFacebookUtils logInInBackgroundWithAccessToken:(accessToken) block:^(nullable__block){}];
callback wasn't being executed for unknown reasons.
To solve this I added a check into the view's -viewDidLoad method to see if there was a valid [FBSDKAccessToken currentAccessToken] but NOT a valid [PFUser currentUser], and if so I forced the method to be executed.
If you need a more thorough explanation, don't hesitate to leave your comment! Thanks!
if ((currentUser != nil)) {
if (PFFacebookUtils.isLinkedWithUser(currentUser!)) {
proccessCurrent(currentUser!)
} else {
PFFacebookUtils.linkUserInBackground(currentUser!, withReadPermissions: self.facebookReadPermissions, block: {(success, error) in
if (success) {
self.proccessCurrent(currentUser!)
} else {
self.presentWalkThroughViewController()
}
})
}
} else {
//var currentToken: FBSDKAccessToken = FBSDKAccessToken.currentAccessToken()
if (FBSDKAccessToken.currentAccessToken() == nil) {
self.presentWalkThroughViewController()
} else {
var currentToken: FBSDKAccessToken = FBSDKAccessToken.currentAccessToken()
PFFacebookUtils.logInInBackgroundWithAccessToken(currentToken, block: {(user, error) in
if (error == nil) {
user?.pinInBackgroundWithBlock({(success, error) in
if (success) {
self.proccessCurrent(user!)
} else {
self.presentWalkThroughViewController()
}
})
} else {
self.presentWalkThroughViewController()
}
})
}
}
The methods processCurrentUser, continue to process my program execution and presentWalkThroughViewController presents the login screens. Is this a safe way to handle skipping the login screen on facebook once a user is already logged in?

How to get user email address via Twitter API in iOS?

I have tried multiple SDK's but was unable to get an email ID from any of the resources. I have tried FHSTwitterEngine for this purpose but I didn't get the solution.
FHSTwitterEngine *twitterEngine = [FHSTwitterEngine sharedEngine];
NSString *username = [twitterEngine loggedInUsername]; //self.engine.loggedInUsername;
NSString *key = [twitterEngine accessToken].key;
NSString *secrete = [twitterEngine accessToken].secret;
if (username.length > 0)
{
NSDictionary *userProfile = [[FHSTwitterEngine sharedEngine] getProfileUsername:username];
NSLog(#"userProfile: %#", userProfile);
EDIT
After Twitter has updated APIs, Now user can get Email using TWTRShareEmailViewController class.
// Objective-C
if ([[Twitter sharedInstance] session]) {
TWTRShareEmailViewController* shareEmailViewController = [[TWTRShareEmailViewController alloc] initWithCompletion:^(NSString* email, NSError* error) {
NSLog(#"Email %#, Error: %#", email, error);
}];
[self presentViewController:shareEmailViewController animated:YES completion:nil];
} else {
// TODO: Handle user not signed in (e.g. attempt to log in or show an alert)
}
// Swift
if Twitter.sharedInstance().session {
let shareEmailViewController = TWTRShareEmailViewController() { email, error in
println("Email \(email), Error: \(error)")
}
self.presentViewController(shareEmailViewController, animated: true, completion: nil)
} else {
// TODO: Handle user not signed in (e.g. attempt to log in or show an alert)
}
NOTES:
Even if the user grants access to her email address, it is not guaranteed you will get an email address. For example, if someone signed up for Twitter with a phone number instead of an email address, the email field may be empty. When this happens, the completion block will pass an error because there is no email address available.
Twitter Dev Ref
PAST
There is NO way you can get email address of a twitter user.
The Twitter API does not provide the user's email address as part of the OAuth token negotiation process nor does it offer other means to obtain it.
Twitter Doc.
You will have to use Twitter framework. Twitter has provided a beautiful framework for that, you just have to integrate it in your app.
To get user email address, your application should be whitelisted. Here is the link. Go to use this form. You can either send mail to sdk-feedback#twitter.com with some details about your App like Consumer key, App Store link of an App, Link to privacy policy, Metadata, Instructions on how to log into our App etc..They will respond within 2-3 working days.
Here is the story how I got whitelisted by conversation with Twitter support team:
Send mail to sdk-feedback#twitter.com with some details about your App like Consumer key, App Store link of an App, Link to privacy policy, Metadata, Instructions on how to log into our App. Mention in mail that you want to access user email adress inside your App.
They will review your App and reply to you withing 2-3 business days.
Once they say that your App is whitelisted, update your App's settings in Twitter Developer portal. Sign in to apps.twitter.com and:
On the 'Settings' tab, add a terms of service and privacy policy URL
On the 'Permissions' tab, change your token's scope to request email. This option will only been seen, once your App gets whitelisted.
Put your hands on code
Use of Twitter framework:
Get user email address
-(void)requestUserEmail
{
if ([[Twitter sharedInstance] session]) {
TWTRShareEmailViewController *shareEmailViewController =
[[TWTRShareEmailViewController alloc]
initWithCompletion:^(NSString *email, NSError *error) {
NSLog(#"Email %# | Error: %#", email, error);
}];
[self presentViewController:shareEmailViewController
animated:YES
completion:nil];
} else {
// Handle user not signed in (e.g. attempt to log in or show an alert)
}
}
Get user profile
-(void)usersShow:(NSString *)userID
{
NSString *statusesShowEndpoint = #"https://api.twitter.com/1.1/users/show.json";
NSDictionary *params = #{#"user_id": userID};
NSError *clientError;
NSURLRequest *request = [[[Twitter sharedInstance] APIClient]
URLRequestWithMethod:#"GET"
URL:statusesShowEndpoint
parameters:params
error:&clientError];
if (request) {
[[[Twitter sharedInstance] APIClient]
sendTwitterRequest:request
completion:^(NSURLResponse *response,
NSData *data,
NSError *connectionError) {
if (data) {
// handle the response data e.g.
NSError *jsonError;
NSDictionary *json = [NSJSONSerialization
JSONObjectWithData:data
options:0
error:&jsonError];
NSLog(#"%#",[json description]);
}
else {
NSLog(#"Error code: %ld | Error description: %#", (long)[connectionError code], [connectionError localizedDescription]);
}
}];
}
else {
NSLog(#"Error: %#", clientError);
}
}
Hope it helps !!!
If you'd like a user's email address, you'll need to ask a user for it within the confines of your own application and service. The Twitter API does not provide the user's email address as part of the OAuth token negotiation process nor does it offer other means to obtain it.
In Swift 4.2 and Xcode 10.1
It's getting email also.
import TwitterKit
#IBAction func onClickTwitterSignin(_ sender: UIButton) {
TWTRTwitter.sharedInstance().logIn { (session, error) in
if (session != nil) {
let name = session?.userName ?? ""
print(name)
print(session?.userID ?? "")
print(session?.authToken ?? "")
print(session?.authTokenSecret ?? "")
let client = TWTRAPIClient.withCurrentUser()
client.requestEmail { email, error in
if (email != nil) {
let recivedEmailID = email ?? ""
print(recivedEmailID)
}else {
print("error--: \(String(describing: error?.localizedDescription))");
}
}
let storyboard = self.storyboard?.instantiateViewController(withIdentifier: "SVC") as! SecondViewController
self.navigationController?.pushViewController(storyboard, animated: true)
}else {
print("error: \(String(describing: error?.localizedDescription))");
}
}
}
Swift 3-4
#IBAction func btnTwitterAction(_ sender: Any) {
TWTRTwitter.sharedInstance().logIn(completion: { (session, error) in
if (session != nil) {
print("signed in as \(String(describing: session?.userName))");
if let mySession = session{
let client = TWTRAPIClient.withCurrentUser()
//To get User name and email
client.requestEmail { email, error in
if (email != nil) {
print("signed in as \(String(describing: session?.userName))");
let firstName = session?.userName ?? "" // received first name
let lastName = session?.userName ?? "" // received last name
let recivedEmailID = email ?? "" // received email
}else {
print("error: \(String(describing: error?.localizedDescription))");
}
}
//To get user profile picture
client.loadUser(withID: session?.userID, completion: { (userData, error) in
if (userData != nil) {
let fullName = userData.name //Full Name
let userProfileImage = userData.profileImageLargeURL //User Profile Image
let userTwitterProfileUrl = userData?.profileURL // User TwitterProfileUrl
}
})
}
} else {
print("error: \(error?.localizedDescription)");
}
})
}

Resources