The Chat Poll example application demonstrates how a user can post polls within a chat as custom messages. The example also demonstrates how the Chat Message References feature of the BlackBerry Spark Communications Services SDK can be used to cast and tally votes for a particular poll. We demonstrate how a user can post polls within a chat as custom messages. We also demonstrate how the Chat Message References feature of the SDK can be used to cast and tally votes for a particular poll.
This example can be used with the other examples but will only send and render messages with
the Text
tag and other tags related to polls: Poll
, PollVoteUp
and PollVoteDown
. It will allow you to do the following:
- Create a poll as custom messages in a chat
- Vote on a poll created by other users
- View a poll's results
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 Chat Poll'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 Chat Poll, 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.
The ChatPollApp
class is the primary entry point for the application and owns an instance of a BBMAuthController. These classes provide authentication via GoogleSignIn and key management via Firebase.
For BBMAuthController
, you can substitute an implementation of id<BBMTokenManager> that interacts with your oAuth provider of choice. Configuration is handled via the ConfigSettings.plist file:
private let _authController : BBMAuthController! = {
let controller = BBMAuthController.fromConfigFile()
return controller
}()
Example Token Managers are provided for both Azure AD and Google SignIn. To use Google SignIn with the BlackBerry Key Management Service (for example), the ConfigSettings.plist
should be configured as follows:
<?xml version="1.0" encoding="UTF-8"?>
<!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>googleSignIn</string>
<key>useBlackBerryKMS</key>
<true/>
<!-- This key must match the authProvider value above -->
<key>googleSignIn</key>
<dict>
<key>domain</key>
<string>blackberry_service_domain (ie :abde-ed6d-11e6-a754-f8cab45257283)</string>
<key>clientId</key>
<string>google_client_id (ie: 485684fd-643d-4612-83c6-71a075f4357f)</string>
<key>environment</key>
<string>sandbox</string>
</dict>
</dict>
</plist>
ChatViewController
will render a list of the messages in a specific chat. An ObservableMonitor
is used to monitor the message count and last message identifier on the chat and request messages and update the tableView
when either change. Only messages with a tag known by the app will be displayed, the rest will be ignored.
ChatViewController
will monitor the chat state and load new messages and update the tableView
via:
//This monitor will lazy-load all of the messages in a given chat and add them to an array
//that we can use to drive our tableView. chat.lastMessage and chat.numMessages are both
//observable properties so this block will run whenever these change - which will happen whenever
//a message is added or removed.
chatMonitor = ObservableMonitor.init(activatedWithName: "messageMonitor") {
[weak self]() -> Void in
guard let weakSelf = self else {
return;
}
var messageIsPending : Bool = false
let lastMsg : UInt64 = weakSelf.chat!.lastMessage
let firstMsg : UInt64 = weakSelf.chat!.numMessages > 0 ? lastMsg - weakSelf.chat!.numMessages + 1 : 0
var messages = Array<BBMChatMessage>()
let msgMap : BBMLiveMap = BBMEnterpriseService.shared().model().chatMessage
for msgId in firstMsg...lastMsg {
let key : BBMChatMessageKey = BBMChatMessageKey.init(chatId: weakSelf.chat!.chatId, messageId: msgId)
let chatMessage : BBMChatMessage = msgMap[key] as! BBMChatMessage
//Only display messages with known tags
guard chatMessage.tag != nil && weakSelf.isKnownTag(tag : chatMessage.tag) else {
continue
}
messageIsPending = messageIsPending || chatMessage.bbmState == kBBMStatePending
messages.insert(chatMessage, at: 0)
}
if(messageIsPending == false) {
self?.messages = messages
self?.tableView.reloadData()
}
}
When sending a message the tag Text
is used for text messages and Poll
is
used to create a poll. We render the Poll
message differently, allowing the
participants in a chat to vote YES
or NO
on it.
let tag : String = self.pollSwitch.isOn ? "Poll" : "Text"
let chatMessageSend : BBMChatMessageSendMessage = BBMChatMessageSendMessage(chatId: self.chat.chatId, tag:tag)
chatMessageSend.content = self.messageField.text
BBMEnterpriseService.shared().sendMessage(toService: chatMessageSend)
When a user votes, a message with the tag PollVoteUp
or PollVoteDown
is sent. We use Chat Message References to associate the vote with the Poll
message. We pass the Poll
message's id as a reference in BBMChatMessageSendMessage_Ref
and use the PollVoteUp
or PollVoteDown
tags to help us tally the votes later on.
To avoid more than one vote per user, we record locally that the user has cast a vote in the localData
field of the chat.
func sendVote(vote : Bool) {
let tag : String = vote ? "PollVoteUp" : "PollVoteDown"
let chatMessageSend : BBMChatMessageSendMessage = BBMChatMessageSendMessage(chatId: self.message.chatId, tag: tag)
let ref : BBMChatMessageSendMessage_Ref = BBMChatMessageSendMessage_Ref.init(messageId: self.message.messageId, tag: tag)
chatMessageSend.ref = [ref]
BBMEnterpriseService.shared().sendMessage(toService: chatMessageSend)
//Record vote locally in locaData to avoid a second vote
let localData : NSMutableDictionary
if(self.message.localData == nil) {
localData = NSMutableDictionary()
}
else {
localData = self.message.localData as! NSMutableDictionary
}
localData["pollVote"] = vote
let messageId : String = String(self.message.messageId)
let elements : [String : Any] = ["messageId":messageId,"chatId":self.message.chatId , "localData": localData]
let requestListChange : BBMRequestListChangeMessage = BBMRequestListChangeMessage.init(elements: [elements], type: "chatMessage")
BBMEnterpriseService.shared().sendMessage(toService: requestListChange)
}
To view the results of the poll, the original poll message is needed.
//If the tag is "PollVoteUp" or "PollVoteDown" we need to get the referenced message
//which is the poll.
var messageId = message.messageId
if(message.tag == "PollVoteUp" || message.tag == "PollVoteDown") {
//Get messageId for poll, which is the referenced message
let reference : BBMChatMessage_Ref = message.ref.first as! BBMChatMessage_Ref
messageId = reference.messageId
}
After the messageId
of the poll message is obtained, a BBMChatMessageCriteria
request is sent to get the list of messages with the tag PollVoteUp
that reference the poll. The result is a list of BBMChatMessage
objects.
//Get list for upvotes by matching a criteria
let forChatMessageCriteria : BBMChatMessageCriteria = BBMChatMessageCriteria()
forChatMessageCriteria.chatId = message.chatId
let forRef : BBMChatMessageCriteria_Ref = BBMChatMessageCriteria_Ref()
forRef.tag = "PollVoteUp"
forRef.messageId = messageId
forChatMessageCriteria.ref = forRef
let forRefs : BBMLiveList = BBMEnterpriseService.shared().model().chatMessage(with: forChatMessageCriteria)
Now the same is done but using the tag PollVoteDown
and the same messasgeId
. This will return a list of messages with the tag PollVoteDown
that reference the poll.
//Get list of downvotes by matching a criteria
let againstChatMessageCriteria : BBMChatMessageCriteria = BBMChatMessageCriteria()
againstChatMessageCriteria.chatId = message.chatId
let againstRef : BBMChatMessageCriteria_Ref = BBMChatMessageCriteria_Ref()
againstRef.tag = "PollVoteDown"
againstRef.messageId = messageId
againstChatMessageCriteria.ref = againstRef
let againstRefs : BBMLiveList = BBMEnterpriseService.shared().model().chatMessage(with: againstChatMessageCriteria)
After we get both lists, we use a monitor to make sure both lists are kept up to date, we then tally the votes and show the results.
pollResultsMonitor = ObservableMonitor.init(activatedWithName: "voteMonitor", selfTerminatingBlock: {
[weak self]() -> Bool in
guard let weakSelf = self else {
return true;
}
if(forRefs.bbmState == kBBMStatePending || againstRefs.bbmState == kBBMStatePending) {
return false
}
for ref in forRefs.array as! Array<BBMChatMessage> {
//Access the chat message and extract any useful data
}
for ref in againstRefs.array as! Array<BBMChatMessage> {
//Access the chat message and extract any useful data
}
//Present data extracted from messages
return true
})
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.