The Quick Start Swift example application demonstrates how you can authenticate with BlackBerry Spark Communications Services with user authentication disabled while using the BlackBerry Key Management Service. This example is implemented in Swift; for the Objective-C implementation, see the Quick Start example.
This example requires the Spark Communications SDK, which you can find along with related resources at the location below.
- Instructions to Download and Configure the SDK.
- iOS Getting Started instructions in the Developer Guide.
- API Reference
Getting started video
By default, this example application is configured to work in a domain with user authentication disabled and the BlackBerry Key Management Service enabled. See the Download & Configure section of the Developer Guide to get started configuring a domain in the sandbox.
Once you have a domain in the sandbox, edit Quick Start Swift's ConfigSettings.plist
file
to configure the example with your domain ID.
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>authProvider</key>
<string>testAuth</string>
<key>useBlackBerryKMS</key>
<true/>
<key>testAuth</key>
<dict>
<key>clientId</key>
<string>not_used</string>
<key>domain</key>
<string>UPDATE_WITH_YOUR_DOMAIN</string>
<key>environment</key>
<string>sandbox</string>
</dict>
</dict>
</plist>
When you run Quick Start Swift, it will prompt you for a user ID and a password. Since you've configured your domain to have user authentication disabled, you can enter any string you like for the user ID and an SDK identity will be created for it. Other applications that you run in the same domain will be able to find this identity by this user ID. The password is used to protected the keys stored in the BlackBerry Key Management Service.
Common code for authenticating users, synchronizing keys and performing common tasks can be found in examples/Support/Source
. Much of the heavy lifting can be found in these classes and they should be referenced before implementing a custom key management or authentication scheme.
BBMAuthController
and BBMKeyManager
can be instantiated with classes/instances that allow you to substitute your own user authentication or key management scheme while handling all of the Spark Communications related functionality.
BBMAccess
provides wrappers around common outgoing messages and the SDK data model.
You may use, extend, or modify this code as needed for your own application.
AccountViewController
is the primary entry point for the application.
To observe connectivity changes to the service we first add our instance as a connectivity listener to the service:
BBMEnterpriseService.shared().add(self)
And implement the delegate method to update the UI when connectivity changes:
public func connectivityStateChange(_ connected: Bool, strict connectedStrict: Bool) {
serviceConnectivityLabel.text = connected ? "Connected" : "Disconnected";
}
We then create an instance of a BBMAuthController
, set ourselves as the rootController (which provides a UI context for pushing auth related modal UI) and add ourselves as a delegate:
//In QuckStartApp.swift
private let _authController : BBMAuthController = BBMAuthController(tokenManager:BBMGoogleTokenManager.self,
domain:SDK_SERVICE_DOMAIN,
environment:kBBMConfig_Sandbox)
//In App Delegate.swift
QuickStartApp.app().authController().rootController = self
//Auth controller using observable monitors
serviceMonitor = ObservableMonitor(name:"ServiceStateMonitor") {
[weak self]
() -> Void in
if let weakSelf = self {
//serviceStarted and authState are observable properties. This means this block
//of code will be called each time the monitor activates, and each time either
//of these property values change
weakSelf.serviceStateChanged(QuickStartApp.app().authController().serviceStarted)
weakSelf.authStateChanged(QuickStartApp.app().authController().authState)
}
}
Once we have an authController
instance we can use it to start the service and and sign in if we have cached credentials:
//Start the service.
QuickStartApp.app().authController().startBBMEnterpriseService()
//Resume our previous session
QuickStartApp.app().authController().signInSilently()
BBMAuthControllerDelegate
has the properties serviceStarted and authState which can be monitored using an ObservableMonitor
. See the Simple Chat for a more detailed example on how to use ObservableMonitor
s to monitor credential changes.
//The AuthController will invoke this callback any time the user credentials/auth state changes
//via the serviceMonitor created above
func authStateChanged(_ authState: BBMAuthState) {
//Update our UI
}
func serviceStateChanged(_ serviceStarted : Bool) {
//Update our UI
}
BBMAuthController
handles the details of starting the service and retrieving and setting user tokens.
The actual fetching of the tokens is handled by an id<BBMTokenManager>
class. This example uses BBMGoogleTokenManager
which implements the BBMTokenManager
protocol and updates tokens via the GoogleSignIn API. You may substitue any class that implements the BBMTokenManager
protocol in initWithTokenManager:
if you wish to use a different oAuth provider.
To start the service call:
[[BBMEnterpriseService service] start:SDK_SERVICE_DOMAIN environment:SDK_ENVIROMENT completionBlock:^(BOOL success) {
self.serviceStarted = success;
//Inform our delegates (AccountViewController) that the service state has changed
}];
The primary purpose of BBMAuthController
is to respond to delegate callbacks from the BBMTokenManager
instance. These happen via two delegate callbacks defined in BBMAuthenticationDelegate.h
and in turn update the UI via the BBMAuthControllerDelegate
methods and/or via monitored changes to the observable authState and serviceState properties:
- (void)localAuthDataChanged:(BBMAuthenticatedAccount *)accountData error:(NSError *)error
{
//Handle changes in the auth state. This method will be called whenever the authenticated
//account information changes.
if(error) {
NSLog(@"Sign in error %@", error.localizedDescription);
}
//No account data means we have logged out.
if(nil == accountData) {
BBMAuthState *authState = [BBMAuthState emptyAuthState];
self.authState = authState;
return;
}
SetupStateBlock callback = ^(NSString *authTokenState, NSString *setupState, NSNumber *regId) {
BBMAuthState *authState = [[BBMAuthState alloc] init];
authState.account = accountData;
authState.authTokenState = authTokenState;
authState.setupState = setupState;
authState.regId = regId;
self.authState = authState;
[self registerLocalUser];
};
//Send the access token and userId to the BBM service. The supplied callback will be invoked
//with the resulting tokenState, setupState and registrationId for the local user. The callback
//may be invoked several times as the setup progresses through the various states. States such
//as "deviceSwitch" may require user interaction.
[[BBMEnterpriseService service] sendAuthToken:accountData.accessToken
userIdentifier:accountData.accountId
setupStateBlock:callback];
}
- (UIViewController *)authController
{
//Provide the id<BBMTokenManager> with a root controller for presenting a modal sign in window.
return self.rootController;
}
The critical part here is updating the tokens for the service. This is done via:
[[BBMEnterpriseService service] sendAuthToken:accountData.accessToken
forUserName:accountData.accountId
setupStateBlock:callback];
The callback will be invoked multiple times as the setupState
and authState of the user changes with some states such as DeviceSwitchRequired
requiring user interaction.
To make a property on a class observable via an ObservableMonitor
you must call ObservableTracker
getterCalledForObject:propertyName:
from the property getter. Usage of ObservableMonitors
is entirely optional, but is implemented for all properties on SDK model objects and can be leveraged in your code in lieu of directly using KVO. See Rich Chat or Simple Chat for examples. ObservableMonitors and observable properties are not thread safe. See the ObservableMonitor documentation for additional details.
- (BBMAuthState *)authState
{
//propertyName must match the actual property name.
[ObservableTracker getterCalledForObject:self propertyName:@"authState"];
return _authState;
}
localAuthDataChanged:account:error
is called from a BBMTokenManager
instance when the credentials change. This is implemented in BBMGoogleTokenManager
for the GoogleSignIn API as follows:
- (void)googleSignInStateChanged:(NSError *)error
{
GIDGoogleUser *user = [GIDSignIn sharedInstance].currentUser;
if(nil == user) {
[self.delegate localAuthDataChanged:nil error:error];
return;
}
//name, email and avatarURL are optional. userID is implementation dependent.
BBMAuthenticatedAccount *accountData = [BBMAuthenticatedAccount accountDataWithId:user.userID
idToken:user.authentication.idToken
accessToken:user.authentication.accessToken
name:user.profile.name
email:user.profile.email
avatarURL:[user.profile imageURLWithDimension:120]];
[self.delegate localAuthDataChanged:accountData error:error];
}
These examples are released as Open Source and licensed under the Apache 2.0 License.
These examples were created using SDKs from Apple Inc. and may contain code licensed for use only with Apple products. Please review your Apple SDK Agreement for additional details.
This page includes icons from: https://material.io/icons/ used under the Apache 2.0 License.
If you find a issue in one of the Samples or have a Feature Request, simply file an issue.