mitrenegade mitrenegade - 6 months ago 122
Swift Question

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

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.

Answer

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")
            }
        }
    }