Introduction
Bit6 is a real-time, cloud-based communications-as-a-service platform that allows mobile and web application developers to quickly and easily add voice/video calling, texting, and multimedia messaging capabilities into their apps.
Bit6 is a real-time, cloud-based communications-as-a-service platform that allows mobile and web application developers to quickly and easily add voice/video calling, texting, and multimedia messaging capabilities into their apps.
Go to Dashboard and get the API Key for your app.
Step 1. Download the Bit6 SDK.
Step 2. Add Bit6.framework
and Bit6Resources.bundle
to your project.
Step 3. Link the following frameworks and libraries: libicucore.tdb
, libstdc++.tdb
, GLKit
and VideoToolbox
.
Step 4. Set the Architectures value to be $(ARCHS_STANDARD_32_BIT)
for iPhone Simulators and $(ARCHS_STANDARD)
for devices.
Step 5. Set your Deployment Target to be iOS 7.0 or later.
Step 6. In your Build Settings add -ObjC
to the 'Other Linker Flags'
Bit6 starting from version v0.8.5 is available through CocoaPods. To install it, simply add the following lines to your Podfile:
# Uncomment this line if you're using Swift
# use_frameworks!
pod "Bit6"
Whitelist Bit6 for Network Requests
If you compile your app with iOS SDK 9.0, you will be affected by App Transport Security. You will need to add the following to your info.plist file:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>amazonaws.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSTemporaryExceptionMinimumTLSVersion</key>
<string>TLSv1.1</string>
</dict>
</dict>
</dict>
Bitcode Bit6 SDK doesn't include support for bitcode yet. You can disable bitcode in your target configuration:
Step 1. Import Bit6: @import Bit6;
for ObjectiveC and import Bit6
for Swift.
Step 2. Launch Bit6 with your API Key.
//ObjectiveC
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// start of your application:didFinishLaunchingWithOptions: method
// ...
[Bit6 startWithApiKey:@"your_api_key"];
NSDictionary *remoteNotificationPayload = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
if (remoteNotificationPayload) {
[[Bit6 pushNotification] didReceiveRemoteNotification:remoteNotificationPayload];
}
// The rest of your application:didFinishLaunchingWithOptions: method
// ...
}
//Swift
func application(application: UIApplication,
didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
// start of your application:didFinishLaunchingWithOptions: method
// ...
Bit6.startWithApiKey("your_api_key")
if let remoteNotificationPayload = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] as? NSDictionary {
Bit6.pushNotification().didReceiveRemoteNotification(remoteNotificationPayload as [NSObject : AnyObject])
}
// The rest of your application:didFinishLaunchingWithOptions: method
// ...
}
Step 3. Support for Push Notifications
NOTE. Remember to upload your development and production APNS Certificates to Dashboard.
Implement the following methods in your AppDelegate
//ObjectiveC
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
[application registerForRemoteNotifications];
}
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void(^)())completionHandler
{
[[Bit6 pushNotification] handleActionWithIdentifier:identifier forRemoteNotification:userInfo completionHandler:completionHandler];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
[[Bit6 pushNotification] didReceiveRemoteNotification:userInfo];
}
- (void) application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
[[Bit6 pushNotification] didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
- (void) application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
[[Bit6 pushNotification] didFailToRegisterForRemoteNotificationsWithError:error];
}
//Swift
func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
application.registerForRemoteNotifications()
}
func application(application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [NSObject : AnyObject], completionHandler: () -> Void) {
Bit6.pushNotification().handleActionWithIdentifier(identifier, forRemoteNotification: userInfo, completionHandler: completionHandler)
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
Bit6.pushNotification().didReceiveRemoteNotification(userInfo)
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
Bit6.pushNotification().didReceiveRemoteNotification(userInfo, fetchCompletionHandler: completionHandler)
}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
Bit6.pushNotification().didRegisterForRemoteNotificationsWithDeviceToken(deviceToken)
}
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
Bit6.pushNotification().didFailToRegisterForRemoteNotificationsWithError(error)
}
Each user in the system has one or more identities - user id, username, email, facebook id, google account, phone number etc. Identities are required for user authentication, managing contacts, identifying user's network. An identity is represented by a URI.
An username is case-insensitive and must consist of alphanumeric characters, e.g. usr:john
or usr:test123
.
Bit6 supports various authentication mechanisms described in the following sections.
Create a new user account with a username identity and a password.
//ObjectiveC
Bit6Address *address = [Bit6Address addressWithUsername:@"john"];
[[Bit6 session] signUpWithUserIdentity:address
password:@"secret"
completionHandler:^(NSDictionary *response, NSError *error) {
if (!error) {
//Sign Up Completed
}
else {
//Sign Up Failed
}
}];
//Swift
let address = Bit6Address(username:"john")
Bit6.session().signUpWithUserIdentity(address,
password:"secret") { (response,error) in
if error == nil {
//Sign Up Completed
}
else {
//Sign Up Failed
}
}
Login into an existing account using an Identity and a password.
//ObjectiveC
Bit6Address *address = [Bit6Address addressWithUsername:@"john"];
[[Bit6 session] loginWithUserIdentity:address
password:@"secret"
completionHandler:^(NSDictionary *response, NSError *error) {
if (!error) {
//Login Completed
}
else {
//Login Failed
}
}];
//Swift
let address = Bit6Address(username:"john")
Bit6.session().loginWithUserIdentity(address,
password:@"secret"){ (response,error) in
if error == nil {
//Login Completed
}
else {
//Login Failed
}
}
//ObjectiveC
if ([Bit6 session].authenticated) {
NSLog(@"Active Session");
}
else {
NSLog(@"Not Active Session");
}
//Swift
if Bit6.session().authenticated {
NSLog("Active Session")
}
else {
NSLog("Not Active Session")
}
//ObjectiveC
[[Bit6 session] logoutWithCompletionHandler:^(NSDictionary *response, NSError *error) {
if (! error) {
NSLog(@"Logout Completed");
}
}];
//Swift
Bit6.session().logoutWithCompletionHandler({(response,error) in
if error == nil {
NSLog("Logout Completed")
}
})
//ObjectiveC
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(loginCompletedNotification:)
name:Bit6LoginCompletedNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(logoutCompletedNotification:)
Bit6LogoutCompletedNotification
object:nil];
- (void) loginCompletedNotification:(NSNotification*)notification
{
}
- (void) logoutCompletedNotification:(NSNotification*)notification
{
}
//Swift
NSNotificationCenter.defaultCenter().addObserver(self,
selector: "loginCompletedNotification:",
name: Bit6LoginCompletedNotification,
object: nil)
NSNotificationCenter.defaultCenter().addObserver(self,
selector: "logoutStartedNotification:",
name: Bit6LogoutStartedNotification,
object: nil)
func loginCompletedNotification(notification:NSNotification)
{
}
func logoutStartedNotification(notification:NSNotification)
{
}
Bit6 integrates with various OAuth1 and OAuth2 providers for simplified user authentication.
See the FBVideoCallsDemo sample project included with the sdk.
Create a new Bit6 account or login into an existing one. In this example we use Facebook Login.
//ObjectiveC
[[Bit6 session] getAuthInfoCompletionHandler:^(NSDictionary *response, NSError *error) {
if (response[@"facebook"][@"client_id"]){
FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
[login logInWithReadPermissions:@[@"public_profile",@"email",@"user_friends"] fromViewController:self.view.window.rootViewController handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
if (error || result.isCancelled) {
//An Error Ocurred
}
else {
[Bit6.session oauthForProvider:@"facebook" params:@{@"client_id":response[@"facebook"][@"client_id"], @"access_token":[FBSDKAccessToken currentAccessToken].tokenString} completion:^(NSDictionary *response, NSError *error) {
if (!error) {
//Login Completed
}
else {
//Login Failed
}
}];
}
}];
}
}];
//Swift
Bit6.session().getAuthInfoCompletionHandler(){ (response, error) in
if let facebook = response["facebook"] as? NSDictionary {
let client_id = facebook["client_id"] as! NSString
let login = FBSDKLoginManager()
login.logInWithReadPermissions(["public_profile","email","user_friends"], fromViewController:self.view.window!.rootViewController) { (result, error) in
if error != nil || result.isCancelled {
//An Error Ocurred
}
else {
Bit6.session().oauthForProvider("facebook", params:["client_id":client_id, "access_token":FBSDKAccessToken.currentAccessToken().tokenString]){ (response, error) in
if error == nil {
//Login Completed
}
else {
//Login Failed
}
}
}
}
}
}
To get a Bit6Address object you can do the following:
//ObjectiveC
Bit6Address *address = [Bit6Address addressWithFacebookId:friendId];
//Swift
let address = Bit6Address(facebookId:friendId)
See the Bit6CallDemo and Bit6CallDemo-Swift sample projects included with the sdk.
//ObjectiveC
Bit6Address *address = ...
Bit6CallController *callController = [Bit6 createCallTo:address streams:Bit6CallStreams_Audio];
if (callController){
//trying to reuse the previous viewController, or create a default one
Bit6CallViewController *callViewController = [Bit6 callViewController] ?: [Bit6CallViewController createDefaultCallViewController];
//set the call to the viewController
[callViewController addCallController:callController];
//start the call
[callController start];
//present the viewController
[Bit6 presentCallViewController:callViewController];
}
else {
//cannot make call to specified address
}
//Swift
let address : Bit6Address = ...
let callController = Bit6.createCallTo(address, streams:.Audio)
if callController != nil {
//trying to reuse the previous viewController, or create a default one
let callViewController = Bit6.callViewController() ?? Bit6CallViewController.createDefaultCallViewController()
//set the call to the viewController
callViewController.addCallController(callController)
//start the call
callController.start()
//present the viewController
Bit6.presentCallViewController(callViewController)
}
else {
NSLog("Call Failed")
}
Listen to call state changes
//ObjectiveC
//register the observer
[callController addObserver:self forKeyPath:@"callState" options:NSKeyValueObservingOptionOld context:NULL];
//deregister observer
//[callController removeObserver:self forKeyPath:@"callState"];
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([object isKindOfClass:[Bit6CallController class]]) {
if ([keyPath isEqualToString:@"callState"]) {
[self callStateChangedNotification:object];
}
}
}
- (void) callStateChangedNotification:(Bit6CallController*)callController
{
switch (callController.callState) {
}
}
//Swift
//register the observer
callController.addObserver(self, forKeyPath:"callState", options: .Old, context:nil)
//deregister observer
//callController.removeObserver(self, forKeyPath:"callState")
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
guard let object = object,
let callController = object as? Bit6CallController else { return }
if keyPath == "callState" {
self.callStateChangedNotification(callController)
}
}
func callStateChangedNotification(callController:Bit6CallController) {
switch (callController.callState) {
}
}
By default the incoming calls are handled by a singleton instance of Bit6IncomingCallHandler
.
You can customize the incoming in-call screen, the incoming call window prompt or even handle the entire flow by implementing the Bit6IncomingCallHandlerDelegate
protocol:
//ObjectiveC
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[Bit6 incomingCallHandler].delegate = self;
//the rest of your code
}
- (Bit6CallViewController*)inCallViewController
{
//return your custom view controller
}
- (UIView*)incomingCallPromptContentViewForCallController:(Bit6CallController*)callController
{
//return your custom in-call prompt view
}
//implement this method if you want to override the default incoming call flow.
- (void)receivedIncomingCall:(Bit6CallController*)callController
{
}
//Swift
func application(application: UIApplication,
didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
Bit6.incomingCallHandler().delegate = self
//the rest of your code
}
func inCallViewController() -> Bit6CallViewController
{
//return your custom view controller
}
func incomingCallPromptContentViewForCallController(callController: Bit6CallController) -> UIView? {
{
//return your custom in-call prompt view
}
//implement this method if you want to override the default incoming call flow.
func receivedIncomingCall(callController: Bit6CallController) {
}
Add the ringtone file to your Xcode project. Apple Docs
Open the Bit6 Dashboard and sign in. Then select your app and open the Settings tab. Here you need to set the ringtone file name for both the APNS development and the APNS production configurations.
You can continue the calls in the background by enable "Audio and Airplay" and "Voice over IP" Background Modes in your target configuration.
Bit6 interconnects with the phone networks (PSTN) and allows making outgoing phone calls.
Phone numbers must be in E164 format, prefixed with +
. So a US (country code 1
) number (555) 123-1234
must be presented as +15551231234
.
For the test purposes we allow short 1 minute calls to destinations in US and Canada. In later releases we will also add billing, which will in turn allow to connect calls of any length to any destination number.
//ObjectiveC
NSString *destination = @"+14445556666";
Bit6CallController *callController = [Bit6 createCallToPhoneNumber:destination];
if (callController){
//trying to reuse the previous viewController, or create a default one
Bit6CallViewController *callViewController = [Bit6 callViewController] ?: [Bit6CallViewController createDefaultCallViewController];
//set the call to the viewController
[callViewController addCallController:callController];
//start the call
[callController start];
//present the viewController
[Bit6 presentCallViewController:callViewController];
}
else {
//cannot make call to specified address
}
//Swift
let destination = "+14445556666";
let callController = Bit6.createCallToPhoneNumber(destination)
if callController != nil {
//trying to reuse the previous viewController, or create a default one
let callViewController = Bit6.callViewController() ?? Bit6CallViewController.createDefaultCallViewController()
//set the call to the viewController
callViewController.addCallController(callController)
//start the call
callController.start()
//present the viewController
Bit6.presentCallViewController(callViewController)
}
else {
//call failed
}
See the Bit6CallDemo and Bit6CallDemo-Swift sample projects included with the sdk.
Step 1. Create an UIViewController class that extends from Bit6CallViewController. All the implemented methods are optional.
//ObjectiveC
@interface MyCallViewController : Bit6CallViewController
@end
@implementation MyCallViewController
- (Bit6CallController*) callController
{
return [[Bit6 callControllers] firstObject];
}
//called in the Main Thread when the UI controls should be updated. For example when the speaker is activated or the audio is muted.
- (void) refreshControlsView
{
NSLog(@"Audio Muted: %@",[Bit6CallController audioMuted]?@"YES":@"NO");
NSLog(@"Speaker Enabled: %@",[Bit6CallController speakerEnabled]?@"YES":@"NO");
}
//called in the Main Thread when the status of the call changes.
- (void) callStateChangedNotificationForCallController:(Bit6CallController*)callController
{
switch (self.callController.callState) {
case Bit6CallState_NEW: NSLog(@"Call created"); break;
case Bit6CallState_ACCEPTING_CALL:
case Bit6CallState_GATHERING_CANDIDATES:
case Bit6CallState_WAITING_SDP:
case Bit6CallState_SENDING_SDP:
case Bit6CallState_CONNECTING: NSLog(@"Call is connecting"); break;
case Bit6CallState_CONNECTED: NSLog(@"Call connected"); break;
case Bit6CallState_END: NSLog(@"Call ends"); break;
case Bit6CallState_ERROR: NSLog(@"Call ends with an error"); break;
case Bit6CallState_DISCONNECTED: NSLog(@"Call is disconnected for the moment. Will try to reconnect again."); break;
case Bit6CallState_MISSED: NSLog(@"Missed Call"); break;
}
//refresh the timer label
[self secondsChangedNotificationForCallController:callController];
}
//called in the Main Thread each second to allow the refresh of a timer UILabel.
- (void) secondsChangedNotificationForCallController:(Bit6CallController*)callController
{
switch (self.callController.callState) {
case Bit6CallState_NEW: case Bit6CallState_ACCEPTING_CALL:
case Bit6CallState_GATHERING_CANDIDATES:
case Bit6CallState_WAITING_SDP: case Bit6CallState_SENDING_SDP:
case Bit6CallState_CONNECTING:
NSLog(@"Connecting..."); break;
case Bit6CallState_CONNECTED:
NSLog(@"Seconds: %@",[Bit6Utils clockFormatForSeconds:self.callController.seconds]); break;
case Bit6CallState_END: case Bit6CallState_MISSED:
case Bit6CallState_ERROR: case Bit6CallState_DISCONNECTED:
NSLog(@"Disconnected"); break;
}
}
//called in the Main Thread to customize the frames for the video feeds. You can call [self setNeedsUpdateVideoViewLayout] at any time to force a refresh of the frames.
- (void)updateLayoutForVideoFeedViews:(NSArray<Bit6VideoFeedView*>*)videoFeedViews
{
//Here you can do your own calculations to set the frames or just call super. You can easily keep the aspect ratio by using AVMakeRectWithAspectRatioInsideRect() (import AVFoundation/AVFoundation.h) to do an scaleToFit or Bit6MakeRectWithAspectRatioToFillRect to do an scaleToFill.
[super updateLayoutForVideoFeedViews:videoFeedViews];
}
@end
//Swift
class MyCallViewController: Bit6CallViewController
var callController : Bit6CallController? {
get {
return Bit6.callControllers().first
}
}
//called in the Main Thread when the UI controls should be updated. For example when the speaker is activated or the audio is muted.
override func refreshControlsView()
{
if Bit6CallController.audioMuted() {
NSLog("Audio Muted: true")
}
else {
NSLog("Audio Muted: false")
}
if Bit6CallController.speakerEnabled() {
NSLog("Speaker Enabled: true")
}
else {
NSLog("Speaker Enabled: false")
}
}
//called in the Main Thread when the status of the call changes.
override func override func callStateChangedNotificationForCallController(callController: Bit6CallController)
{
switch (self.callController.callState) {
case .NEW: NSLog("Call created")
case .ACCEPTING_CALL: fallthrough
case .GATHERING_CANDIDATES: fallthrough
case .WAITING_SDP: fallthrough
case .SENDING_SDP: fallthrough
case .CONNECTING: NSLog("Call is connecting")
case .CONNECTED: NSLog("Call connected")
case .END: NSLog("Call ends")
case .ERROR: NSLog("Call ends with an error")
case .DISCONNECTED: NSLog("Call is disconnected for the moment. Will try to reconnect again.")
case .MISSED: NSLog("Missed Call")
}
//refresh the timer label
self.secondsChangedNotificationForCallController(callController)
}
//called in the Main Thread each second to allow the refresh of a timer UILabel.
override func secondsChangedNotificationForCallController(callController: Bit6CallController!)
{
switch (self.callController.callState) {
case .NEW: fallthrough case .ACCEPTING_CALL: fallthrough
case .GATHERING_CANDIDATES: fallthrough
case .WAITING_SDP: fallthrough
case .SENDING_SDP: fallthrough
case .CONNECTING: NSLog("Call is starting")
case .CONNECTED:
self.timerLabel.text = Bit6Utils.clockFormatForSeconds(Double(self.callController.seconds))
case .DISCONNECTED: fallthrough case .END: fallthrough
case .MISSED: fallthrough case .ERROR:
self.timerLabel.text = "Disconnected"
}
}
//called to customize the frames for the video feeds. You can call [self setNeedsUpdateVideoViewLayout] at any time to force a refresh of the frames.
override func updateLayoutForVideoFeedViews(videoFeedViews: [Bit6VideoFeedView])
{
//Here you can do your own calculations to set the frames or just call super. You can easily keep the aspect ratio by using AVMakeRectWithAspectRatioInsideRect() (import AVFoundation/AVFoundation.h) to do an scaleToFit or Bit6MakeRectWithAspectRatioToFillRect to do an scaleToFill.
super.updateLayoutForVideoFeedViews(videoFeedViews)
}
@end
Step 2. Implement the actions and do the connections in your nib/storyboard file.
//ObjectiveC
- (IBAction) switchCamera:(id)sender {
[Bit6CallController switchCamera];
}
- (IBAction) muteCall:(id)sender {
[Bit6CallController switchMuteAudio];
}
- (IBAction) hangup:(id)sender {
[Bit6CallController hangupAll];
}
- (IBAction) speaker:(id)sender {
[Bit6CallController switchSpeaker];
}
//Swift
@IBAction func switchCamera(sender : UIButton) {
Bit6CallController.switchCamera()
}
@IBAction func muteCall(sender : UIButton) {
Bit6CallController.switchMuteAudio()
}
@IBAction func hangup(sender : UIButton) {
Bit6CallController.hangupAll()
}
@IBAction func speaker(sender : UIButton) {
Bit6CallController.switchSpeaker()
}
Step 3. Do some additional configurations on your viewDidLoad method
//ObjectiveC
- (void)viewDidLoad {
[super viewDidLoad];
self.usernameLabel.text = self.callController.otherDisplayName;
if (! self.callController.hasVideo) {
//hide switch camera option from the UI
}
//On iPad and iPod the audio always goes through the speaker.
NSString *deviceType = [UIDevice currentDevice].model;
if(![deviceType isEqualToString:@"iPhone"]){
//hide speaker option from the UI
}
}
//Swift
override func viewDidLoad() {
super.viewDidLoad()
self.usernameLabel.text = self.callController!.otherDisplayName
if !self.callController.hasVideo {
//hide switch camera option from the UI
}
//On iPad and iPod the audio always goes through the speaker.
var deviceType = UIDevice.currentDevice().model
if deviceType != "iPhone" {
//hide speaker option from the UI
}
}
See the DataChannelDemo sample project included with the sdk.
The process works similar to a regular audio/video call.
Step1. Start the call.
//ObjectiveC
Bit6Address * address = ...
Bit6CallController *callController = [Bit6 createCallTo:address streams:Bit6CallStreams_Data];
if (callController){
self.callController = callController;
//we listen to call state changes
[callController addObserver:self forKeyPath:@"callState" options:NSKeyValueObservingOptionOld context:NULL];
//start the call
[callController start];
}
else {
//cannot make call to specified address
}
//Swift
let address : Bit6Address = ...
let callController = Bit6.createCallTo(address, streams:.Data)
if callController != nil {
self.callController = callController
//we listen to call state changes
callController.addObserver(self, forKeyPath:"callState", options: .Old, context:nil)
//start the call
callController.start()
}
else {
//cannot make call to specified address
}
Note. See it's not required to create a Bit6CallViewController
to make calls.
Step2. Start a transfer.
//ObjectiveC
UIImage *image = ...
NSData *imageData = UIImagePNGRepresentation(image);
Bit6OutgoingTransfer *transfer = [[Bit6OutgoingTransfer alloc] initWithData:imageData name:@"my_image.png" mimeType:@"image/png"];
[self.callController startTransfer:transfer];
//Swift
let image = ...
let data = UIImagePNGRepresentation(image)
let transfer = Bit6OutgoingTransfer(data:imageData, name:"my_image.png", mimeType:"image/png")
self.callController.startTransfer(transfer)
Step3. Listen for incoming transfers and updates in outgoing transfers.
You need to handle the Bit6TransferUpdateNotification
.
//ObjectiveC
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(transferUpdateNotification:)
name:Bit6TransferUpdateNotification
object:self.callController];
- (void) transferUpdateNotification:(NSNotification*)notification
{
Bit6Transfer *transfer = notification.userInfo[Bit6ObjectKey];
NSString *change = notification.userInfo[Bit6ChangeKey];
Bit6CallController *callController = notification.object;
Bit6TransferType transferType = transfer.type;
if (callController == self.callController) {
//the transfer started
if ([change isEqualToString:Bit6TransferStartedKey]) {
}
//transfer updated
else if ([change isEqualToString:Bit6TransferProgressKey]) {
CGFloat progressAtTheMoment = [notification.userInfo[Bit6ProgressKey] floatValue];
}
//the transfer ended
else if ([change isEqualToString:Bit6TransferEndedKey]) {
//transfer received
if (transferType == Bit6TransferType_INCOMING) {
if ([transfer.mimeType hasPrefix:@"image/"]) {
UIImage *image = [UIImage imageWithData:transfer.data];
}
}
else {
//transfer sent
}
}
//the transfer failed
else if ([change isEqualToString:Bit6TransferEndedWithErrorKey]) {
NSLog(@"%@",transfer.error.localizedDescription);
}
}
}
//Swift
NSNotificationCenter.defaultCenter().addObserver(self
selector:"transferUpdateNotification:",
name:Bit6TransferUpdateNotification,
object:self.callController)
func transferUpdateNotification(notification:NSNotification)
{
let transfer = notification.userInfo![Bit6ObjectKey] as! Bit6Transfer
let change = notification.userInfo![Bit6ChangeKey] as! String
let callController = notification.object as! Bit6CallController
let transferType = transfer.type
if callController == self.callController {
switch change {
//the transfer started
case Bit6TransferStartedKey : break
//transfer updated
case Bit6TransferProgressKey :
let progressAtTheMoment = (notification.userInfo![Bit6ProgressKey] as! NSNumber).floatValue
//the transfer ended
case Bit6TransferEndedKey :
//transfer received
if transferType == .INCOMING {
if transfer.mimeType.hasPrefix("image/") {
let image = UIImage(data:transfer.data)!
}
}
else {
//transfer sent
}
//the transfer failed
case Bit6TransferEndedWithErrorKey :
NSLog("\(transfer.error.localizedDescription)")
default: break
}
}
}
//ObjectiveC
Bit6OutgoingMessage *message = [Bit6OutgoingMessage new];
message.content = @"This is a text message";
message.destination = [Bit6Address addressWithUsername:@"user2"];
[message sendWithCompletionHandler:^(NSDictionary *response, NSError *error) {
if (!error) {
//Message Sent
}
else {
//Message Failed
}
}];
//Swift
var message = Bit6OutgoingMessage()
message.content = "This is a text message"
message.destination = Bit6Address(username:"user2")
message.sendWithCompletionHandler { (response, error) in
if error == nil {
//Message Sent
}
else {
//Message Failed
}
}
//ObjectiveC
NSArray *messages = [Bit6 messagesWithOffset:0 length:NSIntegerMax asc:YES];
//Swift
var messages = Bit6.messagesWithOffset(0, length: NSIntegerMax, asc: true)
To know when a message has been added or a message status has been updated, register as an observer for updates on message level:
//ObjectiveC
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(messagesChangedNotification:)
name:Bit6MessagesChangedNotification
object:nil];
//Swift
var conversation : Bit6Conversation = ...
NSNotificationCenter.defaultCenter().addObserver(self,
selector:"messagesChangedNotification:",
name:Bit6MessagesChangedNotification,
object:nil)
Upon receiving a message change notification, update the messages array:
//ObjectiveC
- (void) messagesChangedNotification:(NSNotification*)notification
{
Bit6Message *msg = notification.userInfo[Bit6ObjectKey];
NSString *change = notification.userInfo[Bit6ChangeKey];
if ([change isEqualToString:Bit6AddedKey]) {
//add message to self.messages and refresh changes in UI
}
else if ([change isEqualToString:Bit6UpdatedKey]) {
//find message in self.messages and refresh changes in UI
}
else if ([change isEqualToString:Bit6DeletedKey]) {
//find message in self.messages, delete it and refresh changes in UI
}
}
//Swift
func messagesChangedNotification(notification:NSNotification)
{
let msg = notification.userInfo[Bit6ObjectKey]
let change = notification.userInfo[Bit6ChangeKey]
if change == Bit6AddedKey {
//add message to self.messages and refresh changes in UI
}
else if change == Bit6UpdatedKey {
//find message in self.messages and refresh changes in UI
}
else if change == Bit6DeletedKey {
//find message in self.messages, delete it and refresh changes in UI
}
}
//ObjectiveC
Bit6Message *messageToDelete = ...
[Bit6 deleteMessage:messageToDelete
completion:^(NSDictionary *response, NSError *error) {
if (!error) {
//message deleted
}
}];
//Swift
var messageToDelete : Bit6Message = ...
Bit6.deleteMessage(messageToDelete) { (response, error) in
if error == nil {
//message deleted
}
}
You can enable background remote notifications by checking the property in your target configuration.
Then in your UIApplicationDelegate implementation you need to add the following:
//ObjectiveC
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler
{
[[Bit6 pushNotification] didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
//Swift
func application(application: UIApplication,
didReceiveRemoteNotification userInfo: [NSObject : AnyObject],
fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void)
{
Bit6.pushNotification().didReceiveRemoteNotification(userInfo, fetchCompletionHandler:completionHandler)
}
application:didReceiveRemoteNotification:fetchCompletionHandler:
remember to remove this from your application:didFinishLaunchingWithOptions:
//ObjectiveC
//Remove
/*
NSDictionary *remoteNotificationPayload = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
if (remoteNotificationPayload) {
//[[Bit6 pushNotification] didReceiveRemoteNotification:remoteNotificationPayload];
}
*/
//Swift
//Remove
/*
if let remoteNotificationPayload = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] as? NSDictionary {
Bit6.pushNotification().didReceiveRemoteNotification(remoteNotificationPayload as [NSObject : AnyObject])
}
*/
A conversation is interaction between two users, that includes all text messages and multimedia messages they exchange. There is one conversation per destination address (phone number, e-mail, Facebook account etc).
NOTE: arranging messages in conversations is optional, but can be very convenient depending on what you need.
//ObjectiveC
NSArray *conversations = [Bit6 conversations];
//Swift
var conversations = Bit6.conversations()
//ObjectiveC
Bit6Address *address = [Bit6Address addressWithUsername:@"user2"];
Bit6Conversation *conversation = [Bit6Conversation conversationWithAddress:address];
[Bit6 addConversation:conversation];
//Swift
var address = Bit6Address(username:"user2")
var conversation = Bit6Conversation(address: address)
Bit6.addConversation(conversation)
//ObjectiveC
Bit6Conversation *conversationToDelete = ...
[Bit6 deleteConversation:conversationToDelete
completion:^(NSDictionary *response, NSError *error) {
if (!error) {
//conversation deleted
}
}];
//Swift
var conversationToDelete : Bit6Conversation = ...
Bit6.deleteConversation(conversationToDelete) { (response, error) in
if error == nil {
//conversation deleted
}
}
You will need to know when a conversation has been created, deleted or changed (for example, a new message was received within a certain conversation, the conversation title has changed, etc).
Register as an observer for updates on conversation level:
//ObjectiveC
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(conversationsChangedNotification:)
name:Bit6ConversationsChangedNotification
object:nil];
//Swift
NSNotificationCenter.defaultCenter().addObserver(self,
selector:"conversationsChangedNotification:",
name:Bit6ConversationsChangedNotification,
object:nil)
Upon receiving a conversation change notification, update the conversations array:
//ObjectiveC
- (void) conversationsChangedNotification:(NSNotification*)notification
{
Bit6Conversation *conversation = notification.userInfo[Bit6ObjectKey];
NSString *change = notification.userInfo[Bit6ChangeKey];
if ([change isEqualToString:Bit6AddedKey]) {
//add conversation to self.conversations and refresh changes in UI
}
if ([change isEqualToString:Bit6UpdatedKey]) {
//find conversation in self.conversations and refresh changes in UI
}
if ([change isEqualToString:Bit6DeletedKey]) {
//find conversation in self.conversations, remove it and refresh changes in UI
}
}
//Swift
func conversationsChangedNotification(notification:NSNotification){
let conversation = notification.userInfo[Bit6ObjectKey]
let change = notification.userInfo[Bit6ChangeKey]
if change == Bit6AddedKey {
//add conversation to self.conversations and refresh changes in UI
}
else if change == Bit6UpdatedKey {
//find conversation in self.conversations and refresh changes in UI
}
else if change == Bit6DeletedKey {
//find conversation in self.conversations, remove it and refresh changes in UI
}
}
Get the number of unread messages for a particular conversation:
//ObjectiveC
Bit6Conversation *conversation = ...
NSNumber *badge = conversation.badge;
//Swift
var conversation : Bit6Conversation = ...
var badge = conversation.badge
When showing a messages UIViewController the following code must be called to mark the messages as read and stop increasing the badge value while in the UIViewController:
//ObjectiveC
Bit6Conversation *conversation = ...
[Bit6 setCurrentConversation:conversation];
//Swift
var conversation : Bit6Conversation = ...
Bit6.setCurrentConversation(conversation)
When you are leaving the messages UIViewController the following code must be called:
//ObjectiveC
- (void)dealloc {
[Bit6 setCurrentConversation:nil];
}
//Swift
deinit {
Bit6.setCurrentConversation(nil)
}
To get the unread messages total:
//ObjectiveC
[Bit6 totalBadge]
//Swift
Bit6.totalBadge()
Although messages do not have to be arranged in conversations, it is frequently convenient to have the messages sorted by destination. More docs on handling conversation here.
//ObjectiveC
Bit6Conversation *conversation = ...
self.messages = conversation.messages;
//Swift
var conversation : Bit6Conversation = ...
self.messages = conversation.messages
To know when a message has been added to a particular conversation, or a message status has been updated, register as an observer for updates on message level:
//ObjectiveC
Bit6Conversation *conversation = ...
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(messagesChangedNotification:)
name:Bit6MessagesChangedNotification
object:conversation];
//Swift
var conversation : Bit6Conversation = ...
NSNotificationCenter.defaultCenter().addObserver(self,
selector:"messagesChangedNotification:",
name:Bit6MessagesChangedNotification,
object:conversation)
Upon receiving a message change notification, update the conversations array:
//ObjectiveC
- (void) messagesChangedNotification:(NSNotification*)notification
{
Bit6Message *message = notification.userInfo[Bit6ObjectKey];
NSString *change = notification.userInfo[Bit6ChangeKey];
if ([change isEqualToString:Bit6AddedKey]) {
//add message to self.messages and refresh changes in UI
}
else if ([change isEqualToString:Bit6UpdatedKey]) {
//find message in self.messages and refresh changes in UI
}
else if ([change isEqualToString:Bit6DeletedKey]) {
//find message in self.messages, remove it and refresh changes in UI
}
}
//Swift
func messagesChangedNotification(notification:NSNotification)
{
let message = notification.userInfo[Bit6ObjectKey]
let change = notification.userInfo[Bit6ChangeKey]
if change == Bit6AddedKey {
//add message to self.messages and refresh changes in UI
}
else if change == Bit6UpdatedKey {
//find message in self.messages and refresh changes in UI
}
else if change == Bit6DeletedKey {
//find message in self.messages, remove it and refresh changes in UI
}
}
//ObjectiveC
Bit6Conversation *conv = ...
Bit6Group *group = [Bit6Group groupWithConversation:conv];
//Swift
var conv = ...
var group = Bit6Group(conversation:conv)
//ObjectiveC
[Bit6Group createGroupWithMetadata:@{@"key1":@"value1", ...}
completion:^(Bit6Group *group, NSError *error) {
if (!error) {
//group created
}
}];
//Swift
Bit6Group.createGroupWithMetadata(["key1":"value1", ...]){ (group, error) in
if error == nil {
//group created
}
}
A Bit6ConversationsChangedNotification
and a Bit6GroupsChangedNotification
with change key Bit6AddedKey
will be sent. See "Listen to Changes" in Conversations.
Note. If you want to set the title for a group the use of Bit6GroupMetadataTitleKey is recomended, as in:
//ObjectiveC
[Bit6Group createGroupWithMetadata:@{Bit6GroupMetadataTitleKey:@"some title", ...}
completion:^(Bit6Group *group, NSError *error) {
if (!error) {
//group created
}
}];
//ObjectiveC
[group leaveGroupWithCompletion:^(NSError *error) {
if (!error) {
//you have left the group
}
}];
//Swift
group.leaveGroupWithCompletion(){ (error) in
if error == nil {
//you have left the group
}
}
A Bit6GroupsChangedNotification
with change key Bit6ObjectDeleted
will be sent. See "Listen to Changes" in Conversations.
//ObjectiveC
[group setMetadata:@{@"key1":@"value1", ...}
completion:^(NSError *error) {
if (!error) {
//metadata has changed
}
}];
//Swift
group.setMetadata(["key1":"value1", ...]){ (error) in
if error == nil {
//metadata has changed
}
}
A Bit6GroupsChangedNotification
with change key Bit6UpdatedKey
will be sent. See "Listen to Changes" in Conversations.
Note. If you want to set the title for a group the use of Bit6GroupMetadataTitleKey is recomended
//ObjectiveC
Bit6Address *friendToInvite = ...
NSString *role = Bit6GroupMemberRole_User;
[group inviteGroupMemberWithAddress:friendToInvite role:role
completion:^(NSArray *members, NSError *error) {
if (!error) {
//friend have been invited
//members is the updated list of group members
}
}];
//Swift
let friendToInvite : Bit6Address = ...
let role = Bit6GroupMemberRole_User
group.inviteGroupMemberWithAddress(friendToInvite, role:role){ (members, error) in
if error == nil {
//friend have been invited
//members is the updated list of group members
}
}
A Bit6GroupsChangedNotification
with change key Bit6UpdatedKey
will be sent. See "Listen to Changes" in Conversations.
//ObjectiveC
Bit6GroupMember *friendToRemove = group.members[0];
[group kickGroupMember:friendToRemove
completion:^(NSArray *members, NSError *error) {
if (!error) {
//friend has been removed
//members is the updated list of group members
}
}];
//Swift
let friendToRemove = group.members[0]
group.kickGroupMember(friendToRemove){ (members, error) in
if error == nil {
//friend has been removed
//members is the updated list of group members
}
}
A Bit6GroupsChangedNotification
with change key Bit6UpdatedKey
will be sent. See "Listen to Changes" in Conversations.
Call +[Bit6 typingBeginToAddress:]
.
If using UITextField to type, utilize its UITextFieldDelegate to send the notification:
//ObjectiveC
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string
{
Bit6Address *otherUserAddress = ...
[Bit6 typingBeginToAddress:otherUserAddress];
return YES;
}
//Swift
func textField(textField: UITextField,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String) -> Bool
{
var otherUserAddress : Bit6Address = ...
Bit6.typingBeginToAddress(otherUserAddress)
return true
}
You can check who is typing for a specific conversation:
//ObjectiveC
Bit6Conversation *conv = ...
Bit6Address *whoIsTyping = conv.typingAddress;
//Swift
let conv = ...
let whoIsTyping = conv.typingAddress
To detect when someone is typing register as an observer:
//ObjectiveC
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(typingBeginNotification:)
name:Bit6TypingDidBeginRtNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(typingEndNotification:)
name:Bit6TypingDidEndRtNotification
object:nil];
- (void)typingBeginNotification:(NSNotification*)notification
{
Bit6Address* fromAddress = notification.userInfo[@"from"];
Bit6Address* convervationAddress = notification.object;
}
- (void)typingEndNotification:(NSNotification*)notification
{
Bit6Address* fromAddress = notification.userInfo[@"from"];
Bit6Address* convervationAddress = notification.object;
}
//Swift
NSNotificationCenter.defaultCenter().addObserver(self,
selector:"typingBeginNotification:",
name:Bit6TypingDidBeginRtNotification,
object:nil)
NSNotificationCenter.defaultCenter().addObserver(self,
selector:"typingEndNotification:",
name:Bit6TypingDidEndRtNotification,
object:nil)
func typingBeginNotification(notification:NSNotification){
let fromAddress = notification.userInfo!["from"] as! Bit6Address
let convervationAddress = notification.object as! Bit6Address
}
func typingEndNotification(notification:NSNotification){
let fromAddress = notification.userInfo!["from"] as! Bit6Address
let convervationAddress = notification.object as! Bit6Address
}
//ObjectiveC
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(receivedRTNotification:)
name:Bit6CustomRtNotification
object:nil];
- (void) receivedRTNotification:(NSNotification*)notification
{
Bit6Address *from = notification.userInfo[@"from"];
Bit6Address *to = notification.userInfo[@"to"];
NSString *type = notification.userInfo[@"type"];
}
//Swift
NSNotificationCenter.defaultCenter().addObserver(self,
selector: "receivedRTNotification:",
name: Bit6CustomRtNotification,
object: nil)
func receivedRTNotification(notification:NSNotification)
{
let from : Bit6Address = notification.userInfo["from"]
let to : Bit6Address = notification.userInfo["to"]
let type : String = notification.userInfo["type"]
}
//ObjectiveC
Bit6Address *address = ... ;
[Bit6 sendNotificationToAddress:address type:@"custom_type" data:nil];
//Swift
var address : Bit6Address = ...
Bit6.sendNotificationToAddress(address, type:"custom_type", data:nil)
Let your users exchange photos and pictures.
//ObjectiveC
UIImage *image = ...
Bit6OutgoingMessage *message = [Bit6OutgoingMessage new];
message.image = image;
message.destination = [Bit6Address addressWithUsername:@"user2"];
[message sendWithCompletionHandler:^(NSDictionary *response, NSError *error) {
if (!error) {
//Message Sent
}
else {
//Message Failed
}
}];
//Swift
var image : UIImage = ...
var message = Bit6OutgoingMessage()
message.image = image
message.destination = Bit6Address(username:"user2")
message.sendWithCompletionHandler{ (response, error) in
if error == nil {
//Message Sent
}
else {
//Message Failed
}
}
If you are using UIImagePickerController
to take/select a video you can do the following in the UIImagePickerControllerDelegate
method:
//ObjectiveC
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
// start of your imagePickerController:didFinishPickingMediaWithInfo: method
Bit6OutgoingMessage *message = [Bit6OutgoingMessage new];
message.destination = [Bit6Address addressWithUsername:@"user2"];
NSString *mediaType = info[UIImagePickerControllerMediaType];
if ([mediaType isEqualToString:(NSString *) kUTTypeMovie]) {
//here we add the video to the message
message.videoURL = info[UIImagePickerControllerMediaURL];
//if we used the camera to take the video we have to set the cropping times
message.videoCropStart =
[info objectForKey:@"_UIImagePickerControllerVideoEditingStart"];
message.videoCropEnd =
[info objectForKey:@"_UIImagePickerControllerVideoEditingEnd"];
}
[message sendWithCompletionHandler:^(NSDictionary *response, NSError *error) {
if (!error) {
//Message Sent
}
else {
//Message Failed
}
}];
// The rest of your imagePickerController:didFinishPickingMediaWithInfo: method
}
//Swift
func imagePickerController(picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [NSObject : AnyObject])
{
// start of your imagePickerController:didFinishPickingMediaWithInfo: method
var message = Bit6OutgoingMessage()
message.destination = Bit6Address(username:"user2")
let mediaType = info[UIImagePickerControllerMediaType] as! String
if mediaType == (kUTTypeMovie as String) {
//here we add the video to the message
message.videoURL = info[UIImagePickerControllerMediaURL] as! NSURL
//if we used the camera to take the video we have to set the cropping times
message.videoCropStart =
info["_UIImagePickerControllerVideoEditingStart"] as! NSNumber
message.videoCropEnd =
info["_UIImagePickerControllerVideoEditingEnd"] as! NSNumber
}
message.sendWithCompletionHandler { (response, error) in
if error == nil {
//Message Sent
}
else {
//Message Failed
}
}
// The rest of your imagePickerController:didFinishPickingMediaWithInfo: method
}
In iOS 8 you must add the key NSLocationWhenInUseUsageDescription to your info.plist file. This string is used to specify the reason for accessing the user’s location information.
Step 1. Prepare the message:
//ObjectiveC
Bit6OutgoingMessage *message = [Bit6OutgoingMessage new];
message.destination = [Bit6Address addressWithUsername:@"user2"];
//Swift
var message = Bit6OutgoingMessage()
message.destination = Bit6Address(username:"user2")
Step 2. Start the location service:
//ObjectiveC
[[Bit6 locationController] startListeningToLocationForMessage:message delegate:self];
//Swift
Bit6.locationController().startListeningToLocationForMessage(message, delegate: self)
Step 3. Implement the Bit6CurrentLocationControllerDelegate
and send the message when the location has been obtained
//ObjectiveC
@interface ChatsViewController <Bit6CurrentLocationControllerDelegate>
- (void) currentLocationController:(Bit6CurrentLocationController*)b6clc
didGetLocationForMessage:(Bit6OutgoingMessage*)message
{
[message sendWithCompletionHandler:^(NSDictionary *response, NSError *error) {
if (!error) {
//Message Sent
}
else {
//Message Failed
}
}];
}
- (void) currentLocationController:(Bit6CurrentLocationController*)b6clc
didFailWithError:(NSError*)error
message:(Bit6OutgoingMessage*)message
{
//an error occurred
}
//Swift
class ChatsViewController : Bit6CurrentLocationControllerDelegate
func currentLocationController(b6clc: Bit6CurrentLocationController!,
didGetLocationForMessage message: Bit6OutgoingMessage!)
{
message.sendWithCompletionHandler{ (response, error) in
if error == nil {
//Message Sent
}
else {
//Message Failed
}
}
}
func currentLocationController(b6clc: Bit6CurrentLocationController!,
didFailWithError error: NSError!,
message: Bit6OutgoingMessage!)
{
//an error occurred
}
Note. Remember to import the CoreLocation framework.
//ObjectiveC
Bit6OutgoingMessage *message = [Bit6OutgoingMessage new];
message.location = CLLocationCoordinate2DMake(latitude, longitude);
message.destination = [Bit6Address addressWithUsername:@"user2"];
[message sendWithCompletionHandler:^(NSDictionary *response, NSError *error) {
if (!error) {
//Message Sent
}
else {
//Message Failed
}
}];
//Swift
var message = Bit6OutgoingMessage()
message.location = CLLocationCoordinate2DMake(latitude, longitude)
message.destination = Bit6Address(username:"user2")
message.sendWithCompletionHandler{ (response, error) in
if error == nil {
//Message Sent
}
else {
//Message Failed
}
}
//ObjectiveC
Bit6Message *message = ...
//Open in AppleMaps
[Bit6 openLocationOnMapsFromMessage:msg];
/*
//Open in GoogleMaps app, if available
CLLocationCoordinate2D location = msg.location;
NSString *urlString = [NSString stringWithFormat:@"comgooglemaps://?center=%f,%f&zoom=14",
location.latitude, location.longitude];
NSURL *url = [NSURL URLWithString:urlString];
if ([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url];
}
*/
/*
//Open in Waze app, if available
CLLocationCoordinate2D location = msg.location;
NSString *urlString = [NSString stringWithFormat:@"waze://?ll=%f,%f&navigate=yes",
location.latitude, location.longitude];
NSURL *url = [NSURL URLWithString:urlString];
if ([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url];
}
*/
//Swift
var message : Bit6Message = ...
//Open in AppleMaps
Bit6.openLocationOnMapsFromMessage(message)
/*
//Open in GoogleMaps app, if available
let location = msg.location
let urlString = "comgooglemaps://?center=\(location.latitude),\(location.longitude)&zoom=14"
let url = NSURL(string: urlString)
if UIApplication.sharedApplication().canOpenURL(url!) {
UIApplication.sharedApplication().openURL(url!)
}
*/
/*
//Open in Waze app, if available
let location = msg.location
let urlString = "waze://?ll=\(location.latitude),\(location.longitude)&navigate=yes"
let url = NSURL(string: urlString)
if UIApplication.sharedApplication().canOpenURL(url!) {
UIApplication.sharedApplication().openURL(url!)
}
*/
Step 1. Start the audio recording:
//ObjectiveC
[[Bit6 audioRecorder] startRecordingAudioWithMaxDuration: 60
delegate: self
defaultPrompt: YES
errorHandler:^(NSError *error) {
//an error occurred
}];
//Swift
Bit6.audioRecorder().startRecordingAudioWithMaxDuration(60,
delegate: self,
defaultPrompt: true,
errorHandler: { (error) in
//an error occurred
});
Note If defaultPrompt
param is YES then a default UIAlertView will be shown to handle the recording. If NO then you need to provide a custom UI to handle the recording.
To get the length of the current recording: [Bit6 audioRecorder].duration
. Then you can use [Bit6Utils clockFormatForSeconds:duration]
to get a string like 01:05.
To cancel the recording: [[Bit6 audioRecorder] cancelRecording]
To finish the recording: [[Bit6 audioRecorder] stopRecording]
To know if there's a recording in process: [Bit6 audioRecorder].isRecording
Step 2. Implement the Bit6AudioRecorderControllerDelegate
. Its methods are called in the main thread.
//ObjectiveC
@interface ChatsViewController <Bit6AudioRecorderControllerDelegate>
- (void) doneRecorderController:(Bit6AudioRecorderController*)b6rc
filePath:(NSString*)filePath
{
if ([Bit6Utils audioDurationForFileAtPath:filePath] > 1.0) {
Bit6OutgoingMessage *message = [Bit6OutgoingMessage new];
message.destination = [Bit6Address addressWithUsername:@"user2"];
message.audioFilePath = filePath;
[message sendWithCompletionHandler:^(NSDictionary *response, NSError *error) {
if (!error) {
//Message Sent
}
else {
//Message Failed
}
}];
}
}
- (void)isRecordingWithController:(Bit6AudioRecorderController*)b6rc
filePath:(NSString*)filePath
{
//called each 0.5 seconds while recording
double durationValue = [Bit6 audioRecorder].duration;
NSString *duration = [Bit6Utils clockFormatForSeconds:durationValue];
}
- (void)cancelRecorderController:(Bit6AudioRecorderController*)b6rc
{
//the recording was cancelled
}
//Swift
class ChatsViewController : Bit6AudioRecorderControllerDelegate
func doneRecorderController(b6rc: Bit6AudioRecorderController,
filePath: NSString) {
if Bit6Utils.audioDurationForFileAtPath(filePath) > 1.0 {
var message = Bit6OutgoingMessage()
message.destination = Bit6Address(username:"user2")
message.audioFilePath = filePath
message.sendWithCompletionHandler{ (response, error) in
if error == nil {
//Message Sent
}
else {
//Message Failed
}
}
}
}
func isRecordingWithController(b6rc: Bit6AudioRecorderController,
filePath: NSString) {
//called each 0.5 seconds while recording
if let durationValue = Bit6.audioRecorder().duration {
let duration = Bit6Utils.clockFormatForSeconds(durationValue)
}
}
func cancelRecorderController(b6rc: Bit6AudioRecorderController) {
//the recording was cancelled
}
//ObjectiveC
NSString *filePath = message.pathForFullAttachment;
[[Bit6 audioPlayer] startPlayingAudioFileAtPath:filePath errorHandler:^(NSError *error)
{
//an error occurred
}];
//Swift
var message : Bit6OutgoingMessage() = ...
let filePath = message.pathForFullAttachment
Bit6.audioPlayer().startPlayingAudioFileAtPath(filePath,
errorHandler: { (error) in
//an error occurred
})
To get the length of an audio file in a Bit6Message: message.audioDuration
To get the current audio file playing length: [Bit6 audioPlayer].duration
To get the audio file playing current time: [Bit6 audioPlayer].currentTime
To get the path to the last audio file played: [Bit6 audioPlayer].filePathPlaying
To know if there's an audio file playing at the moment: [Bit6 audioPlayer].isPlayingAudioFile
Note. You can listen to the Bit6AudioPlayingNotification
to update your UI while playing an audio file.
Let the user view the attachment preview, play audio files, view location on the map, view the picture fullscreen etc.
Check the attachment path for a given message.
//ObjectiveC
Bit6Message *msg = ...;
NSString *thumbnailAttachmentPath = msg.pathForThumbnailAttachment;
NSString *fullAttachmentPath = msg.pathForFullAttachment;
//Swift
var msg : Bit6Message = ...
var thumbnailAttachmentPath = msg.pathForThumbnailAttachment
var fullAttachmentPath = msg.pathForFullAttachment
Check the attachment state for a given message.
Properties:
message.statusForThumbnailAttachment
- for thumbnails
message.statusForFullAttachment
- for full size
//ObjectiveC
Bit6Message *msg = ...;
switch (msg.statusForFullAttachment) {
case Bit6MessageAttachmentStatus_INVALID:
//the message can't have an attachment, for example a text message
break;
case Bit6MessageAttachmentStatus_FAILED:
//the attachment can't be downloaded, for example because of an
//error when uploading the attachment
break;
case Bit6MessageAttachmentStatus_NOT_FOUND:
//the file is not in cache
break;
case Bit6MessageAttachmentStatus_DOWNLOADING:
//the file is not in cache, but is being downloaded
break;
case Bit6MessageAttachmentStatus_FOUND:
//the file is in cache, ready to be used
break;
}
//Swift
var msg : Bit6Message = ...
switch msg.statusForFullAttachment {
case .INVALID:
//the message can't have an attachment, for example a text message
break;
case .FAILED:
//the attachment can't be downloaded, for example because of an
//error when uploading the attachment
break;
case .NOT_FOUND:
//the file is not in cache
break;
case .DOWNLOADING:
//the file is not in cache, but is being downloaded
break;
case .FOUND:
//the file is in cache, ready to be used
break;
}
Step 1. Check if the message has a preview to show:
//ObjectiveC
Bit6Message *message = ...
if (message.type != Bit6MessageType_Text) {
//can show preview
}
//Swift
var msg : Bit6Message = ...
if message.type != .Text {
//can show preview
}
Step 2. To show the preview, use a Bit6ThumbnailImageView
object. To add Bit6ThumbnailImageView
to your nib/storyboard just add an UIImageView
object and change its class to Bit6ThumbnailImageView
:
Step 3. Set the message to be used with the Bit6ThumbnailImageView
object:
//ObjectiveC
Bit6Message *message = ...
Bit6ThumbnailImageView *imageView = ...
imageView.message = message;
//Swift
var msg : Bit6Message = ...
var imageView : Bit6ThumbnailImageView = ...
imageView.message = message
Step 1. Implement the Bit6ThumbnailImageViewDelegate
//ObjectiveC
@interface ChatsViewController <Bit6ThumbnailImageViewDelegate>
//Swift
class ChatsViewController : Bit6ThumbnailImageViewDelegate
Step 2. Depending on the attachment type, show the location on the map, play the audio or video, open the picture full screen:
//ObjectiveC
- (void) touchedThumbnailImageView:(Bit6ThumbnailImageView*)thumbnailImageView
{
Bit6Message *msg = thumbnailImageView.message;
Bit6MessageAttachmentStatus fullAttachStatus = msg.statusForFullAttachment;
if (msg.type == Bit6MessageType_Location) {
//Open in AppleMaps
[Bit6 openLocationOnMapsFromMessage:msg];
}
else if (msg.type == Bit6MessageType_Attachments) {
//play an audio file
if (msg.attachFileType == Bit6MessageFileType_Audio) {
[[Bit6 audioPlayer] startPlayingAudioFileInMessage:msg errorHandler:^(NSError *error) {
//an error occurred
}];
}
else if (msg.attachFileType == Bit6MessageFileType_Video) {
if ([Bit6 shouldDownloadVideoBeforePlaying]) {
if (fullAttachStatus==Bit6MessageAttachmentStatus_FOUND) {
[Bit6 playVideoFromMessage:msg viewController:self.navigationController];
}
}
else {
[Bit6 playVideoFromMessage:msg viewController:self.navigationController];
}
}
else if (msg.attachFileType == Bit6MessageFileType_Image) {
//code to navigate to another screen to show the image in full size
}
}
}
//Swift
func touchedThumbnailImageView(thumbnailImageView:Bit6ThumbnailImageView) {
let msg = thumbnailImageView.message
let fullAttachStatus = msg.statusForFullAttachment
if msg.type == .Location{
//Open in AppleMaps
Bit6.openLocationOnMapsFromMessage(msg)
}
else if msg.type == .Attachments {
//play an audio file
if msg.attachFileType == .Audio {
Bit6.audioPlayer().startPlayingAudioFileInMessage(msg,errorHandler: { (error) in
//an error occurred
})
}
else if msg.attachFileType == .Video {
if Bit6.shouldDownloadVideoBeforePlaying() {
if fullAttachStatus == .FOUND {
Bit6.playVideoFromMessage(msg, viewController:self.navigationController)
}
}
else {
Bit6.playVideoFromMessage(msg, viewController:self.navigationController)
}
}
else if msg.attachFileType == .Image {
//code to navigate to another screen to show the image in full size
}
}
}
Step 3. Set the Bit6ThumbnailImageView
delegate
//ObjectiveC
Bit6ThumbnailImageView *imageView = ...
imageView.thumbnailImageViewDelegate = self;
//Swift
var imageView : Bit6ThumbnailImageView = ...
imageView.thumbnailImageViewDelegate = self
Step 1. Check if a message has an image to show:
//ObjectiveC
Bit6Message *msg = ...
if (msg.type == Bit6MessageType_Attachments && msg.attachFileType == Bit6MessageFileType_Image) {
//can show full image
}
//Swift
var msg : Bit6Message = ...
if msg.type == .Attachments && msg.attachFileType == .Image {
//can show full image
}
Step 2. Use a Bit6ImageView
object to show the image.
Add Bit6ImageView
to nib/storyboard: add an UIImageView
object and change its class to Bit6ImageView
.
Step 3. Set the message to be used with the Bit6ImageView
object:
//ObjectiveC
Bit6Message *message = ...
Bit6ImageView *imageView = ...
imageView.message = message;
//Swift
var msg : Bit6Message = ...
var imageView : Bit6ImageView = ...
imageView.message = message
By default when playing a video attachment ([Bit6 playVideoFromMessage:msg viewController:viewController]
) the file will be streamed if not found in the device. If you want to change this behaviour by forcing the video file to be downloaded you can add the following to your target info.plist
Implement a menu popup to enable message forwarding, resending and text copying.
This menu controller is available in Bit6ThumbnailImageView for multimedia messages and in Bit6MessageLabel for text messages.
Step 1. Implement Bit6MenuControllerDelegate
//ObjectiveC
@interface ChatsTableViewController <Bit6MenuControllerDelegate>
@end
Bit6MessageLabel *textLabel = ...
textLabel.message = message;
textLabel.menuControllerDelegate = self;
Bit6ThumbnailImageView *imageView = ...
imageView.message = message;
imageView.menuControllerDelegate = self;
//Swift
class ChatsTableViewController :Bit6MenuControllerDelegate {
}
var textLabel : Bit6MessageLabel = ...
textLabel.message = message
textLabel.menuControllerDelegate = self
var imageView : Bit6ThumbnailImageView = ...
imageView.message = message;
imageView.menuControllerDelegate = self;
Step 2. Enable resending of failed messages
To show this action, implement resendFailedMessage:
//ObjectiveC
- (void) resendFailedMessage:(Bit6OutgoingMessage*)msg
{
//try to send the message again
[msg sendWithCompletionHandler:^(NSDictionary *response, NSError *error) {
...
}];
}
//Swift
func resendFailedMessage(msg:Bit6OutgoingMessage){
//try to send the message again
msg.sendWithCompletionHandler{ (response, error) in
...
}
}
Step 3. Enable message forwarding
To show this action, implement forwardMessage:
//ObjectiveC
- (void) forwardMessage:(Bit6Message*)msg
{
//we create a copy of the message
Bit6OutgoingMessage *messageToForward = [Bit6OutgoingMessage outgoingCopyOfMessage:msg];
//set the destination and the channel
Bit6Address *address = ...
messageToForward.destination = address;
//send a copy of the message to the new destination
[messageToForward sendWithCompletionHandler:^(NSDictionary *response, NSError *error) {
...
}];
}
//Swift
func forwardMessage(msg:Bit6Message)
{
//we create a copy of the message
var messageToForward = Bit6OutgoingMessage.outgoingCopyOfMessage(msg)
//set the destination and the channel
var address : Bit6Address = ...
messageToForward.destination = address
//send a copy of the message to the new destination
messageToForward.sendWithCompletionHandler{ (response, error) in
...
}
}
Bit6ThumbnailImageView
and Bit6ImageView
classes can downloads the attachments automatically.
You can also download the files manually using the Bit6FileDownloader
class. For example to download the thumbnail for a message:
//ObjectiveC
NSURL *url = message.remoteURLForThumbnailAttachment;
NSString *filePath = message.pathForThumbnailAttachment;
NSData* (^preparationBlock)(NSData *data) = ^(NSData *imageData) {
UIImage *image = [UIImage imageWithData:imageData];
UIImage *finalImage = ...
return UIImagePNGRepresentation(finalImage);
};
[Bit6FileDownloader downloadFileAtURL:url
toFilePath:filePath
preparationBlock:preparationBlock
priority:NSOperationQueuePriorityNormal
completionHandler:^(NSURL* location, NSURLResponse* response, NSError* error) {
//operation completed
}
];
//Swift
let url = message.remoteURLForThumbnailAttachment!
let filePath = message.pathForThumbnailAttachment!
let preparationBlock = ({ (data:NSData) -> NSData in
let image = UIImage(data:data)
let finalImage = ...
return UIImagePNGRepresentation(finalImage)!
})
Bit6FileDownloader.downloadFileAtURL(url,
toFilePath:filePath,
preparationBlock:preparationBlock,
priority:.Normal) { (location, response, error) in
//operation completed
}
preparationBlock
can be used to make changes to the downloaded data before saving it locally. For example you could download the thumbnail and apply a filter using this block.