Sceenic - WatchTogether
  • Watch Together and Synchronization SDKs
  • Watch together SDK
    • Watch Together SDK overview
    • Authentication overview
    • Tutorials
      • Android
        • Android - Java/Kotlin
      • iOS
        • iOS Swift/Objective-c adapter
      • Web
        • Authentication
        • Create a New Project
        • Adding WT SDK library to the project
        • Installing the NPM package
        • Sample application
          • The conference skeleton
          • Connecting to a Session
          • How to turn on and off video and audio
          • How to change video quality
          • Errors handling
          • Leave the call
        • Support
    • API references
      • Android reference
        • Session
        • SessionListener
        • SessionReconnectListener
        • SessionConnectionListener
        • Participant
          • ParticipantType
        • SessionError
      • iOS Swift reference
        • Session
        • SessionDelegate
        • Participant
        • ParticipantActiveSpeakerDelegate
        • ParticipantDelegate
        • LocalParticipant
        • WTError
        • DataTypes
      • iOS Objective-c adapter reference
        • SessionAdapter
        • SessionAdapterDelegate
        • ParticipantAdapter
        • LocalParticipantAdapter
        • ParticipantAdpaterDelegate
        • ParticipantAdapterActiveSpeakerDelegate
        • NSError
        • DataTypes
      • Web reference
        • WT Session
          • WTSession.connect(sToken, pName, uC, m)
          • WTSession.connectAsAViewer(sToken, pName)
          • WTSession.connectWithSharingScreen(sToken, pName)
          • WTSession.disconnect()
          • WTSession.enableStats()
          • WTSession.sendMessage(msg)
          • WTSession.sendPlayerData(time)
          • async WTSession.getSessionState()
        • SessionListeners
          • WTSessionListeners.onConnected(fn)
          • WTSessionListeners.onDisconnected(fn)
          • WTSessionListeners.onStreamCreated(fn)
          • WTSessionListeners.onLocalStreamCreated(fn)
          • WTSessionListeners.onMosReport(fn)
          • WTSessionListeners.offMosReport(fn)
          • WTSessionListeners.onMessageReceived(fn)
          • WTSessionListeners.onSyncData(fn)
          • WTSessionListeners.onIceDisconnected(fn)
        • Participant
          • setMediaQuality
        • ParticipantListeners
        • ErrorsListeners
        • ReconnectListeners
        • MediaDevices
      • Cluster authentication service reference (CAS)
  • Synchronization SDK
    • Synchronization SDK overview
    • Tutorials
      • Android
        • Android - Java/Kotlin
      • iOS
        • iOS - Swift/Objective-c
      • Web
        • Installing the NPM package
        • Web - TypeScript/React
        • v2.0 Migration Guide
    • API references
      • Android reference
        • SynchSDK
        • SynchListener
      • iOS reference
        • SynchSDK
        • SynchListener
      • Web reference
  • Celebrity SDK
    • Celebrity SDK overview
    • Tutorials
      • Web
        • Installing the NPM package
        • Web - TypeScript/React
    • API References
      • Web reference
  • Chat SDK
    • Chat SDK overview
    • Tutorials
      • Web
        • Installing the NPM package
        • Web - TypeScript/React
    • API Refences
      • Web reference
  • Public Chat SDK
    • Public Chat SDK overview
    • Tutorials
      • Web
        • Installing the NPM package
        • Web - TypeScript/React
    • API Refences
      • Web reference
  • Celebrity Watch Party
    • Web application
    • Android
    • iOS
    • Celebrity View & Fan View
Powered by GitBook
On this page
  • Overview
  • Requirements
  • Authentication
  • Acquiring an Access Token
  • Going to production
  • Creating a new project
  • Adding Watch Together iOS SDK library to the project
  • Granting access to camera and microphone
  • Initialize a Session
  • Using the SessionDelegate
  • Using the Participant object
  • Using the ParticipantDelegate object
  • Working with the LocalParticipant object
  • Running the application
  • Next steps
  • Support

Was this helpful?

  1. Watch together SDK
  2. Tutorials
  3. iOS

iOS Swift/Objective-c adapter

PreviousiOSNextWeb

Last updated 2 years ago

Was this helpful?

Overview

Follow this step-by-step tutorial to implement the Watch Together video chat sample application.

While the client-side application will take care of most of the functionality, in order to make this sample application work, you will need to get an access token from the (CAS).

  • To better understand the Watch Together architecture have a look at this guide -

  • Have a look at the

  • The full code samples can be found here

Requirements

To complete this guide successfully the following prerequisites are required:

  • A Sceenic account

Authentication

An Access Token is needed in order to allow a client to connect to a Session.

Note: It is important that the client application does not request an Access Token directly from the backend. By doing that you risk exposing the API_TOKEN and API_SECRET.

  • To simplify the tutorial, in the section below you can see an example of getting an Access Token

Acquiring an Access Token

curl -iL --request GET --url https://YOUR_CAS_URL/stream/token/v2/ --header 'auth-api-key: API_KEY' --header 'auth-api-secret: API_SECRET'

A successful response will look like that:

{
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...."
}

Note: Every Streaming Token corresponds to one specific Session only. To allow two different clients to connect to the same Session, the clients need to use the same Access Token.

Going to production

‌To go to production you will need to implement your own authentication server. Using the server the Access Token will be shared to your various clients (Web, Android, and iOS). With this valid Access Token you will be able to use the service.

For that you will need:

Creating a new project

  • Open Xcode and create a new project

  • Choose a Single View application

  • Configure your product organization, bundle, and team names

  • Set the application location on your computer and press "create"

Adding Watch Together iOS SDK library to the project

In case you require to use an Objective-c project, please add the WatchTogetherAdapter to your project

  • Create a folder with the name "WatchTogether" in the root of the project

  • Copy all files ("WatchTogether.framework","WatchTogether.podspec") to created folder

  • [When using the Objective-c adapater] - do the following extra steps

    • Create a folder named "WatchTogetherAdapter" in the root of the project

    • Copy all the files unpacked from the SDK ("WatchTogetherAdapter.framework","WatchTogetherAdapter.podspec") to "WatchTogetherAdapter" folder

  • Open folder with your project in Terminal

    • Print the command "pod init"

    • Print the command "open Podfile"

    • In the opened file paste this string pod 'WatchTogether', :path => './WatchTogether' after "target 'Your project name' do" and save file

    • [When using the Objective-c adapater] - do the following extra steps

      • In the opened file paste the following strings pod 'WatchTogetherAdapter', :path => './WatchTogetherAdapter' pod 'WatchTogether', :path => './WatchTogether'

    • In the terminal write the command "pod install"

  • Disable application Bitcode.

    • Build Settings → Build Options → Enable Bitcodes → Set to "NO"

  • To mock a video for the simulator you will need to add an mp4 video file to your main bundle and rename it to simulator_mock.mp4

Granting access to camera and microphone

  • Go to the info.plist file, add the following properties

    • Add the property "Privacy - Camera Usage Description" and the value "$(PRODUCT_NAME) uses the camera"

    • Add the property "Privacy - Microphone Usage Description" and the value "$(PRODUCT_NAME) uses the microphone"

Initialize a Session

  • Import the library to the view controller

import WatchTogether

@import WatchTogetherAdapter;

  • Set the minimal log level

Session.setMinLogLevel(.debug)

[SessionAdapter setMinLogLevel:WTALogLevelDebug];

  • Create a session instance

    • username - a display name

    • delegate - an object conforming to SessionDelegate protocol

let session = SessionBuilder()
            .withUsername(username)
            .withVideoCodec(.H264)
            .withVideoRenderer(.Metal)
            .withDelegate(self)
            .build()

SessionAdapter* session =  [[SessionAdapter alloc] initWith:username];
[session buildSession];
session.delegate = self;

  • Get the local participant instance

let localParticipant = session.localParticipant

ParticipantAdapter* localParticipant = session.localParticipant;

  • Connect to the session

    • Access token - an authentication token received from the Authentication Server

do {
    try session.connect(with: token)// method can throw
} catch {
    print(error)//error is WTError
    //fallback logic
}    
NSError* err = nil;
[_session connectWith:_token error:&err];
if (err != nil) {
        //fallback logic
}
  • Disconnect from the session

session.disconnect()

[session disconnect];

Using the SessionDelegate

extension YourClass: SessionDelegate {
    func onSessionConnected(sessionId: String, participants: [Participant]) {
        print("Connected to \(sessionId)")
    }
    
    func onSessionDisconnect() {
        print("Disconnect from session")
    }
    
    func onRemoteParticipantJoined(participant: Participant) {
        print("New participant with id: \(participant.getId())")
    }
     
    func onRemoteParticipantStartMedia(participant: Participant) {
        print("New media from id: \(participant.getId())")
    }
    
    func onRemoteParticipantStopMedia(participant: Participant) {
        print("Stoped media from id: \(participant.getId())")
    }
    
    func onRemoteParticipantLeft(participant: Participant) {
        print("Participant left with id: \(participant.getId())")
    }
    
    func onSessionError(error: Error) {
        print(error.localizedDescription)
    } 
    
    func onRemoteParticipantNotification(message: String, participantId: String) {
        print("message: \(message) from participant \(participantId)"
    }
}

- (void)onSessionConnectedWithSessionId:(NSString *)sessionId participants:(NSArray<ParticipantAdapter *> *)participants{
    NSLog(@"Connected to session");
}

- (void)onSessionDisconnect{
    NSLog(@"Disconnect from session");
}

- (void)onRemoteParticipantJoinedWithParticipant:(ParticipantAdapter *)participant{
    NSLog(@"New participant");
}

- (void)onRemoteParticipantStartMediaWithParticipant:(ParticipantAdapter *)participant{
    NSLog(@"New media");
}

- (void)onRemoteParticipantStopMediaWithParticipant:(ParticipantAdapter *)participant{
    NSLog(@"Stoped media");
}

- (void)onRemoteParticipantLeftWithParticipant:(ParticipantAdapter *)participant{
    NSLog(@"Participant left");
}

- (void)onSessionErrorWithError:(NSError *)error{
    NSLog(@"Session failed with error");
}

-(void)onRemoteParticipantNotificationWithMessage:(NSString *)message participantId:(NSString *)participantId { 
    NSLog(@"message: %@ from participant %@", message, participantId);
}

Using the Participant object

  • To get Participant video use the method getVideo()

  • To get Participant display name use the method getDisplayName()

  • To get Participant id use the method getId()

class ParticipantView: UIView {
    var participant: Participant?
    
     func setParticipant(_ participant: Participant) {
        self.participant = participant
        let participantVideo = participant.getVideo() // View with video from participant
        let displayName = participant.getDisplayName() // Participant  display name
        participant?.delegate = self
        let participantId:String = participant.getId()// Participant id

        self.addSubview(participantVideo)
     }
     //MARK: Actions to change participant properties
     @IBAction func onVolumeChanged(_ sender: UISlider) {
        participant.volume = sender.value// values from 0.0 to 1.0
     }
     @IBAction func muteAudioAction(_ sender: UIButton) {
        participant.isAudioEnabled = !participant.isAudioEnabled //true/false enable disable audio
     }
    
     @IBAction func muteVideoAction(_ sender: UIButton) {
        participant.isVideoEnabled = !participant.isVideoEnabled //true/false
     }
}

@property (weak, nonatomic) ParticipantAdapter* participant;

- (void) setParticipant: (ParticipantAdapter *)participant {
    UIView* view = [participant getVideo];
    NSString *participantId = [_participant getId]
    _participantNameLabel.text = [_participant getDisplayName];
    _participant.delegate = self
    [self addSubview: view];
 } 
 //MARK: Actions in participant view
 - (IBAction)changeVolume:(UISlider *)sender {
    _participant.volume = sender.value; //from 0.0 to 1.10
}
 - (IBAction)enableAudio:(UIButton *)sender {
    _participant.isAudioEnabled = !_participant.isAudioEnabled//YES/NO turn on/off
 }
 - (IBAction)enableAudio:(UIButton *)sender {
    !_participant.isVideoEnabled = !_participant.isVideoEnabled
 }

Using the ParticipantDelegate object

extension ParticipantView: ParticipantDelegate{
    func onReconnecting() {
        print("Participant reconnecting")
    }
    
    func onReconnected() {
        print("Participant reconnected")
    }
    
    func onChangeMediaSettings() {
        print("Participant has changed media settings")
    }
    
    func onStreamUpdate(qualityInfo: StreamQualityInfo) {
        print("Participant has new quality info \(qualityInfo.quality)")
    }
}

- (void)onChangeMediaSettings{
    NSLog(@"On participant cahnge media settings");
}

- (void)onStreamUpdateWithQualityInfo:(StreamQualityInfoAdapter *)qualityInfo{
    NSLog(@"On participant update quality");
}

- (void)onReconnecting{
    NSLog(@"On participant reconnecting");
}

- (void)onReconnected{
    NSLog(@"On participant reconnected");
}

Working with the LocalParticipant object

LocalParticipant class implements the Participant protocol

  • Camera preview

localParticipant.startCameraPreview()

//MARK: switch camera action
@IBAction func onSwitchCamera(_ sender: UIButton) {
    localParticipant.cameraPosition == .front ? AVCaptureDevice.Position.back
            : AVCaptureDevice.Position.front
}
func onResolutionChanged(to: VideoQuality, fps: Int32) {
    localParticipant.videoQuality = VideoQuality.Low// VideoQuality.Hight, VideoQuality.Default
    localParticipant.fps = fps // normal values in range from 15 to 45
}

[_localParticipant startCameraPreview];

//MARK: Actions
//@property (nonatomic) BOOL isFront;
- (IBAction)switchCamera:(id)sender {
    _isFront = !_isFront;
    if (_isFront){
        [_localParticipant setCameraPosition: AVCaptureDevicePositionFront];
    } else {
        [_localParticipant setCameraPosition: AVCaptureDevicePositionBack];
    }
}
- (IBAction)changeVideoQuality:(UISegmentedControl *)sender {
        if(sender.selectedSegmentIndex == 0){
            _localParticipant.videoQuality = VideoQualityAdapterLow;
        } else if (sender.selectedSegmentIndex == 1){
            _localParticipant.videoQuality = VideoQualityAdapterDefault;
        } else {
            _localParticipant.videoQuality = VideoQualityAdapterHigh;
        }
    }
}

- (IBAction)changeFrameRate:(UISlider *)sender {
        _localParticipant.frameRate = sender.value; //normal values in range 15 to 45
    }
}

localParticipant.set(orientation: .landscapeRight) 
// localParticipant.set(orientation: nil) //use to disable orientation lock

[_localParticipant setWithOrientation: AVCaptureVideoOrientationLandscapeLeft];
// [_localParticipant setWithOrientation: 0]; //use to disable orientation lock

Running the application

Once coding is finished, you should be able to run the application.

Next steps

Support

To learn how to acquire an Access Token please look at the (CAS) reference

TheAccess Tokenis a JWT token - more about JWT you can read - .

You can get your API_KEY and API_SECRET in your private area, .

API_KEY, and API_SECRET - can be retrieved in your private area once you

Your own working authentication server -

Download the library () package and unpack it

To get more information about Cocoapod go to this site

Video orientation set method using the parameter

You can view the complete Watch Together sample application here - .

Learn more about Watch Together architecture -

To understand better how to set up the Authentication please have a look at the .

Need technical support? contact us at .

Cluster Authentication Server
Watch Together overview
Authentication overview
Swift
Obj-c adapter
Xcode
Cluster Authentication Server
here
here
login
Authentication overview
Private area
https://cocoapods.org
AVCaptureVideoOrientation
Swift
Watch Together overview
Authentication overview
Support@sceenic.co