iOS
Basic
Integration

This is a developers' guide for setting up a LiveLike SDK configuration for native iOS apps. We will take you through the basic technical steps for configuration and show you how to send your first widgets and chat messages. We will also provide detailed samples and instructions for a complete LiveLike integration.

Prerequisites

  • An admin login and registered application on the Producer Site (provided by LiveLike).
  • Client ID - Used to initialize the SDK. For instructions on how to get your Client ID see Initialization
  • A Bitbucket account with SSH enabled. The Engagement SDK is hosted on a private repository. You need to provide LiveLike with your Bitbucket username and we'll grant you access.
  • OS: iOS 10+

Installation

The SDK can be installed through packaging managers like Carthage or Cocoapods.

Add the following to a Cartfile

git "git@bitbucket.org:livelike/livelike-ios-sdk.git" == 1.1

and then run in the Terminal

carthage update --platform iOS

Follow the instructions in the “If you’re building for iOS” section of the Carthage README adding the following text to the xcfilelists mentioned therein:

input.xcfilelist
$(SRCROOT)/Carthage/Build/iOS/EngagementSDK.framework
$(SRCROOT)/Carthage/Build/iOS/PubNub.framework
$(SRCROOT)/Carthage/Build/iOS/SendBirdSDK.framework
$(SRCROOT)/Carthage/Build/iOS/Lottie.framework
$(SRCROOT)/Carthage/Build/iOS/Mixpanel.framework
output.xcfilelist
$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/EngagementSDK.framework
$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/PubNub.framework
$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/SendBirdSDK.framework
$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Lottie.framework
$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Mixpanel.framework

Add the following to a Podfile

target '<application-target-name>' do
    use_frameworks!
    pod 'EngagementSDK', :git => 'git@bitbucket.org:livelike/livelike-ios-sdk.git', :tag=> '1.1'
end

Initialization

For this step, you will need your Client ID to initialize the Engagement SDK. To get your ClientID:

  1. Login to the Producer Site.
  2. Under your profile picture select 'My Organizations' from the dropdown menu.
  3. You will see your Client ID under any app available in the 'Apps' section.
  4. You can also pass user access-token (optional parameter) during sdk initialisation or sdk will create new user.
  5. SDK exposes LiveLikeUser stream to get user and access-token persist at client app for future SDK init calls.

Import the EngagementSDK

import EngagementSDK
#import < EngagementSDK-Swift.h >

Next, create an instance of the EngagementSDK. We recommend that you create the EngagementSDK instance as early as possible for your application (for example, in the application:didFinishLaunchingWithOptions: method of the AppDelegate ).

The initializer for the EngagementSDK expects an access token to be passed in, which uniquely identifies a user. Access tokens are generated with the EngagementSDK.generateAccessToken(clientID:, completion:) method. You should store the generated access token as you see fit, and create the EngagementSDK object with it. An example of generate and storing when needed, and using the retrieved token when available, is given below.

var engagementSDK: EngagementSDK?

func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    if let localAccessToken = localStorage.accessToken {
        engagementSDK = EngagementSDK(clientID: "< your-client-id>", 
                                     livelikeAccessToken: localAccessToken)
    } else {
        EngagementSDK.generateAccessToken(clientID: "< your-client-id>") { generatedAccessToken in
            localStorage.accessToken = generatedAccessToken
            engagementSDK = EngagementSDK(clientID: "< your-client-id>",
                                         livelikeAccessToken: generatedAccessToken)
        }
    }
    
    return true
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    if (localStorage.accessToken != nil) {
        LLEngagementSDK *engagementSDK = [[LLEngagementSDK alloc] initWithClientID:@"< your-client-id>" livelikeAccessToken: localAccessToken];
    } else {
        [LLEngagementSDK generateAccessTokenWithClientID:@"< your-client-id>" completion:^(NSString * _Nonnull generatedAccessToken) {
            localStorage.accessToken = generatedAccessToken
            LLEngagementSDK *engagementSDK = [[LLEngagementSDK alloc] initWithClientID:@"< your-client-id>"
                                                                   livelikeAccessToken:generatedAccessToken];
        }];
    }

    return YES;
}

Configure your Layout

Now you have to configure your media page layout and declare containers for the Engagement SDK Widget and Chat views to live inside.

Even though this guide makes use of both the chat and widget components, it is possible to use only one of the components, if desired.

The first step is to add two UIViews to your view layout to serve as containers for the Engagement SDK's Widget and Chat view. Regardless of whether you use Storyboards or Programmatic UI, please follow the steps that work best for your application.

Storyboards - If you are using storyboards then you need to add two UIViews to your view hierarchy for Widget and Chat. Add any constraints for managing the size and position of the subviews. Then, create and hook up an @IBOutlet in your ViewController for each view called widgetView and chatView.

Programmatic - If you are using programmatic UI, you need to instantiate two UIViews called widgetView and chatView. Add them as subviews to your ViewController's view. Add any constraints for managing the size and position of the subviews.

Then, in your ViewController, create an instance of WidgetViewController and ChatViewController

class ViewController: UIViewController {
    @IBOutlet var widgetView: UIView!
    @IBOutlet var chatView: UIView!

    let widgetViewController = WidgetViewController()
    let chatViewController = ChatViewController()
}
@interface ViewController : UIViewController

@property (weak, nonatomic) IBOutlet UIView *widgetView;
@property (weak, nonatomic) IBOutlet UIView *chatView;
@property id< LLContentSession > session;

@end

@interface ViewController () {
    LLChatViewController *chatViewController;
    LLWidgetViewController *widgetViewController;
}
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // Creating the Widget and Chat ViewControllers
    widgetViewController = [[LLWidgetViewController alloc] init];
    chatViewController = [[LLChatViewController alloc] init];
}
@end

Finally, you need to add the WidgetViewController and ChatViewController as a child view controller to your ViewController. For more information on child view controllers see Apple documentation on Implementing a ContainerViewController.

In the viewDidLoad method add the following:

override func viewDidLoad() {
    super.viewDidLoad()
    
    // Add widgetViewController as child view controller  
    addChild(widgetViewController)
    widgetView.addSubview(widgetViewController.view)
    widgetViewController.didMove(toParent: self)

    // Apply constraints to fill the container
    widgetViewController.view.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        widgetViewController.view.bottomAnchor.constraint(equalTo: widgetView.bottomAnchor),
        widgetViewController.view.topAnchor.constraint(equalTo: widgetView.topAnchor),
        widgetViewController.view.trailingAnchor.constraint(equalTo: widgetView.trailingAnchor),
        widgetViewController.view.leadingAnchor.constraint(equalTo: widgetView.leadingAnchor)
    ])

    // Add chatViewController as a child view controller
    addChild(chatViewController)
    chatView.addSubview(chatViewController.view)
    chatViewController.didMove(toParent: self)

    // Apply constraints to fill the container
    chatViewController.view.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        chatViewController.view.bottomAnchor.constraint(equalTo: chatView.bottomAnchor),
        chatViewController.view.topAnchor.constraint(equalTo: chatView.topAnchor),
        chatViewController.view.trailingAnchor.constraint(equalTo: chatView.trailingAnchor),
        chatViewController.view.leadingAnchor.constraint(equalTo: chatView.leadingAnchor)
    ])
}
- (void)viewDidLoad {
    [super viewDidLoad];
    
    widgetViewController = [[LLWidgetViewController alloc] init];
    chatViewController = [[LLChatViewController alloc] init];
    
    // Creating the Widget and Chat ViewControllers
    widgetViewController = [[LLWidgetViewController alloc] init];
    chatViewController = [[LLChatViewController alloc] init];

    // Adding widgetViewController as child view controller
    [self addChildViewController:widgetViewController];
    [_widgetView addSubview:widgetViewController.view];
    [widgetViewController didMoveToParentViewController:self];

    // Applying constraints to fill the widgetView container
    widgetViewController.view.translatesAutoresizingMaskIntoConstraints = false;
    [widgetViewController.view.bottomAnchor constraintEqualToAnchor:_widgetView.bottomAnchor].active = true;
    [widgetViewController.view.topAnchor constraintEqualToAnchor:_widgetView.topAnchor].active = true;
    [widgetViewController.view.trailingAnchor constraintEqualToAnchor:_widgetView.trailingAnchor].active = true;
    [widgetViewController.view.leadingAnchor constraintEqualToAnchor:_widgetView.leadingAnchor].active = true;

    // Adding chatViewController as a child view controller
    [self addChildViewController:chatViewController];
    [_chatView addSubview:chatViewController.view];
    [chatViewController didMoveToParentViewController:self];

    // Applying constraints to fill the chatView container
    chatViewController.view.translatesAutoresizingMaskIntoConstraints = false;
    [chatViewController.view.bottomAnchor constraintEqualToAnchor:_chatView.bottomAnchor].active = true;
    [chatViewController.view.topAnchor constraintEqualToAnchor:_chatView.topAnchor].active = true;
    [chatViewController.view.trailingAnchor constraintEqualToAnchor:_chatView.trailingAnchor].active = true;
    [chatViewController.view.leadingAnchor constraintEqualToAnchor:_chatView.leadingAnchor].active = true;
}

Click here to go to the Use Cases section for visual examples of different possible layouts.

Starting A Content Session

A Content Session represents a user's subscription to a particular program (typically a live, linear TV show, game or episode). To start a Content Session you will need a Program ID. Integrating teams are expected to create programs within the LiveLike system, either through the API or through the CMS. The team should then copy the Program IDs into the relevant media metadata in their own systems, so that content sessions can be started along with media playback.

How to create Program ID

How to publish a widget

In your ViewController, declare a variable to maintain an instance of the ContentSession we will be creating.

class ViewController: UIViewController {
    @IBOutlet var widgetView: UIView!
    @IBOutlet var chatView: UIView!

    let widgetViewController = WidgetViewController()
    let chatViewController = ChatViewController()

    var session: ContentSession?
}
@interface ViewController () {
    LLChatViewController *chatViewController;
    LLWidgetViewController *widgetViewController;
}
@property (weak, nonatomic) IBOutlet UIView *widgetView;
@property (weak, nonatomic) IBOutlet UIView *chatView;
@property id< LLContentSession > session;
@end

Now call the contentSession method of your instance of the EngagementSDK - passing in your Program ID. This will return a ContentSession object. You must store this in the ContentSession variable declared earlier.

Assign the ContentSession to the WidgetViewController so the user can begin receiving widgets published from the CMS.

Assign the ContentSession to the ChatViewController so the user will also be able to participate in the chat room.

func startContentSession(engagementSDK: EngagementSDK) {
    let config = SessionConfiguration(programID: "< program-id >")
    session = engagementSDK.contentSession(config: config)
    widgetViewController.session = session
    chatViewController.session = session
}
- (void)startContentSession: (LLEngagementSDK*)engagementSDK {
    LLSessionConfiguration *config = [[LLSessionConfiguration alloc] initWithProgramID:@"< program-id >"];                  
    id< LLContentSession > session = [engagementSDK contentSessionWithConfig:config];
    self.session = session;
    chatViewController.session = session;
    widgetViewController.session = session;
}

Closing Content Session

When the user returns to your application's home page and away from your live video screen you will want to close the ContentSession. Closing a session will disconnect from Widgets and Chat and release all the related resources .

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    self.session?.close()
    //or
    self.session = nil
}
- (void) viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];

    //Close
    [[self session] close];
    //or
    self.session = nil;
}

Sample Integration

import UIKit
import EngagementSDK

class ViewController: UIViewController {

    @IBOutlet weak var widgetView: UIView!
    @IBOutlet weak var chatView: UIView!

    var engagementSDK: EngagementSDK!

    //Creating the Widget and Chat ViewControllers
    let widgetViewController = WidgetViewController()
    let chatViewController = ChatViewController()

    //Holding a reference to the session to keep it valid
    var session: ContentSession?
    
    override func viewDidLoad() {
        super.viewDidLoad()

        engagementSDK = EngagementSDK(clientID: "< client-id >")
        
        // Adding widgetViewController as child view controller
        addChild(widgetViewController)
        widgetView.addSubview(widgetViewController.view)
        widgetViewController.didMove(toParent: self)
        
        // Applying constraints to fill the container
        widgetViewController.view.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            widgetViewController.view.bottomAnchor.constraint(equalTo: widgetView.bottomAnchor),
            widgetViewController.view.topAnchor.constraint(equalTo: widgetView.topAnchor),
            widgetViewController.view.trailingAnchor.constraint(equalTo: widgetView.trailingAnchor),
            widgetViewController.view.leadingAnchor.constraint(equalTo: widgetView.leadingAnchor)
            ])
        
        // Adding chatViewController as a child view controller
        addChild(chatViewController)
        chatView.addSubview(chatViewController.view)
        chatViewController.didMove(toParent: self)
        
        // Applying constraints to fill the container
        chatViewController.view.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            chatViewController.view.bottomAnchor.constraint(equalTo: chatView.bottomAnchor),
            chatViewController.view.topAnchor.constraint(equalTo: chatView.topAnchor),
            chatViewController.view.trailingAnchor.constraint(equalTo: chatView.trailingAnchor),
            chatViewController.view.leadingAnchor.constraint(equalTo: chatView.leadingAnchor)
            ])

        //Creating a Content Session
        let config = SessionConfiguration(programID: "< program-id >")
        session = engagementSDK.contentSession(config: config)

        // Applying the Content Session to the Widget and Chat ViewControllers
        widgetViewController.session = session
        chatViewController.session = session
    }

    //Ending a session
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        self.session?.close()
    }
}
#import < UIKit/UIKit.h >
#import < EngagementSDK-Swift.h >
#import "ViewController.h"

@interface ViewController : UIViewController

@property (weak, nonatomic) IBOutlet UIView *widgetView;
@property (weak, nonatomic) IBOutlet UIView *chatView;
@property id< LLContentSession > session;

@end

@interface ViewController () {
    LLChatViewController *chatViewController;
    LLWidgetViewController *widgetViewController;
    LLEngagementSDK *engagementSDK;
}
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    engagementSDK = [[LLEngagementSDK alloc] initWithClientID:@"< client-id >"];

    // Creating the Widget and Chat ViewControllers
    widgetViewController = [[LLWidgetViewController alloc] init];
    chatViewController = [[LLChatViewController alloc] init];

    // Adding widgetViewController as child view controller
    [self addChildViewController:widgetViewController];
    [_widgetView addSubview:widgetViewController.view];
    [widgetViewController didMoveToParentViewController:self];

    // Applying constraints to fill the container
    widgetViewController.view.translatesAutoresizingMaskIntoConstraints = false;
    [widgetViewController.view.bottomAnchor constraintEqualToAnchor:_widgetView.bottomAnchor].active = true;
    [widgetViewController.view.topAnchor constraintEqualToAnchor:_widgetView.topAnchor].active = true;
    [widgetViewController.view.trailingAnchor constraintEqualToAnchor:_widgetView.trailingAnchor].active = true;
    [widgetViewController.view.leadingAnchor constraintEqualToAnchor:_widgetView.leadingAnchor].active = true;

    // Adding chatViewController as a child view controller
    [self addChildViewController:chatViewController];
    [_chatView addSubview:chatViewController.view];
    [chatViewController didMoveToParentViewController:self];

    // Applying constraints to fill the container
    chatViewController.view.translatesAutoresizingMaskIntoConstraints = false;
    [chatViewController.view.bottomAnchor constraintEqualToAnchor:_chatView.bottomAnchor].active = true;
    [chatViewController.view.topAnchor constraintEqualToAnchor:_chatView.topAnchor].active = true;
    [chatViewController.view.trailingAnchor constraintEqualToAnchor:_chatView.trailingAnchor].active = true;
    [chatViewController.view.leadingAnchor constraintEqualToAnchor:_chatView.leadingAnchor].active = true;

    //Creating a Content Session
    LLSessionConfiguration *config = [[LLSessionConfiguration alloc] initWithProgramID:@"< program-id >"];
    id< LLContentSession > session = [engagementSDK contentSessionWithConfig:config];
    self.session = session;

    // Applying the Content Session to the Widget and Chat ViewControllers
    chatViewController.session = session;
    widgetViewController.session = session;
}

//Ending a session
- (void) viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    [self.session close];
}

@end

Contacting LiveLike Support

If you've been unable to solve your problem with the help of the sections above, please feel free to contact the LiveLike support team at support@livelike.com.