Vonage Video API: Real-Time Video in iOS

Get started with the Vonage Video API and add real-time video streaming to your iOS apps. By Yusuf Tör.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

Connecting to the Session

Before you can start livestreaming, you need to connect to the session.

To do this, head to StreamingViewController.swift. Below the token property, add the following:

private var session: OTSession?

OpenTok uses an OTSession to represent your session. Here, you’re keeping a strong reference to your session object.

Now, to connect to a session add the following code below leave():

private func connectToSession() {
  // 1
  session = OTSession(
    apiKey: apiKey,
    sessionId: sessionId,
    delegate: self
  )

  // 2
  var error: OTError?
  session?.connect(withToken: token, error: &error)

  // 3
  if let error = error {
    print("An error occurred connecting to the session", error)
  }
}

Here’s what’s happening:

  1. Initialize OTSession with your apiKey and sessionId. Set the delegate to StreamingViewController. Setting the delegate will cause a compiler error as you have not yet made StreamingViewController conform to OTSessionDelegate.
  2. Connect to the session using your token and pass in a reference to an OTError to catch any synchronous errors. OTError defines errors specific to the OpenTok iOS SDK.
  3. If a synchronous error did occur, print it to the console.

Since you set the delegate of OTSession to self, you’ll need to implement the OTSessionDelegate protocol. Add the following extension at the bottom of StreamingViewController.swift:

// MARK: - OTSessionDelegate
extension StreamingViewController: OTSessionDelegate {
  // 1
  func sessionDidConnect(_ session: OTSession) {
    print("The client connected to the session.")
  }

  // 2
  func sessionDidDisconnect(_ session: OTSession) {
    print("The client disconnected from the session.")
  }

  // 3
  func session(_ session: OTSession, didFailWithError error: OTError) {
    print("The client failed to connect to the session: \(error).")
  }

  // 4
  func session(_ session: OTSession, streamCreated stream: OTStream) {
    print("A stream was created in the session.")
  }

  // 5
  func session(_ session: OTSession, streamDestroyed stream: OTStream) {
    print("A stream was destroyed in the session.")
  }
}

This code handles events relating to the session. Specifically, when:

  1. You successfully connect to the session.
  2. You successfully disconnect from the session.
  3. An asynchronous error occurs while trying to connect to the session.
  4. Someone else publishes their video stream to the session. This provides an OTStream, which represents their stream.
  5. Someone else stops publishing their video stream to the session.

You’re almost ready to try out your app, but you still need to actually connect to the session.

To do this, add the following at the bottom of viewDidLoad():

connectToSession()

Build and run, then enter the lounge. The screen will still be black, but you’ll notice in the console that you’ve successfully connected to the session:

Console output showing your client connected to the video session

Publishing the Camera

Now that you can connect to the session, it’s time to publish your camera and microphone.

Add the following below connectToSession():

private func publishCamera() {
  // 1
  guard let publisher = OTPublisher(delegate: nil) else {
    return
  }

  // 2
  var error: OTError?
  session?.publish(publisher, error: &error)

  // 3
  if let error = error {
    print("An error occurred when trying to publish", error)
    return
  }

  // 4
  guard let publisherView = publisher.view else {
    return
  }

  // 5
  let screenBounds = UIScreen.main.bounds
  let viewWidth: CGFloat = 150
  let viewHeight: CGFloat = 267
  let margin: CGFloat = 20

  publisherView.frame = CGRect(
    x: screenBounds.width - viewWidth - margin,
    y: screenBounds.height - viewHeight - margin,
    width: viewWidth,
    height: viewHeight
  )
  view.addSubview(publisherView)
}

Here’s what this code does:

  1. Initialize an OTPublisher object. This object can capture an audio-video stream from the device’s microphone and camera.
  2. Publish an audio-video stream to the session and pass in a reference to OTError to catch any synchronous errors.
  3. If a synchronous error did occur, print it to the console.
  4. Get the view from the publisher, which contains the video captured from the device’s camera. Since the simulator doesn’t have a camera, it substitutes in a default video.
  5. Add the view as a subview in the corner of StreamingViewController.
Note: You set OTPublisher‘s delegate to nil here, but you could implement OTPublisherDelegate to handle events related to the publisher, such as asynchronous errors. You could also use it to pass settings to OTPublisher to adjust the camera resolution, audio fallback settings, video scaling behavior and more.

Next, to publish the camera when a connection has been established, inside sessionDidConnect(_:), add the following after print:

publishCamera()

Build and run and you’ll see the simulator’s default video of a spinning teapot in the bottom-right. How apt! You’re now livestreaming in the session. If you were to run this code on a device, you’d see your camera’s video instead. Congratulations, you now have half a tea party! :]

The lounge in the app with a single video stream

Subscribing to an Incoming Stream

Before you can see a video stream from another client who’s publishing to the session, you need to subscribe to it.

Below the token property add the following:

private var subscriberView: UIView?

This will keep a strong reference to the view that holds the video stream of the other client.

Now, add the following code below publishCamera():

private func subscribe(to stream: OTStream) {
  // 1
  guard let subscriber = OTSubscriber(stream: stream, delegate: nil) else {
    return
  }

  // 2
  var error: OTError?
  session?.subscribe(subscriber, error: &error)

  // 3
  if let error = error {
    print("An error occurred when subscribing to the stream", error)
    return
  }

  // 4
  guard let subscriberView = subscriber.view else {
    return
  }

  // 5
  self.subscriberView = subscriberView
  subscriberView.frame = UIScreen.main.bounds
  view.insertSubview(subscriberView, at: 0)
}

Here’s what’s going on in this code:

  1. Create OTSubscriber using OTStream, which represents the video stream of another device.
  2. Subscribe to this stream and pass in a reference to OTError to catch any synchronous errors.
  3. If a synchronous error did occur, print it to the console.
  4. Get the view from the subscriber. This view contains the stream’s video.
  5. Keep a reference to the view and insert that view below the subviews of StreamingViewController. This view is the same size as the screen’s bounds and will fill the screen.
Note: You also set OTSubscriber‘s delegate to nil here. Implementing the OTSubscriberDelegate protocol enables you to handle events related to the subscriber.

Finally, at the bottom of session(_:streamCreated:), add:

subscribe(to: stream)

This makes you subscribe to a stream whenever it’s created.

To have a two-way livestream in your app, you either need a friend with another device or another iOS simulator. In this case, build and run using the iPhone 12 Pro simulator. Then, stop running the app, select the iPhone 12 simulator and build and run again.

Now, on the iPhone 12 Pro simulator, tap the TeaParty app to open it again and you’ll have two simulators running the app at the same time. Enter the lounge on both simulators and you’ll see two spinning teapots on each device:

The lounge in your app with two concurrent video streams

The two simulators are having a virtual tea party — they’re both publishing their video to the session and subscribing to each other’s video stream!