Video Streaming Tutorial for iOS: Getting Started

Learn how to build a video streaming app using AVKit and AVFoundation frameworks. By Saeed Taheri.

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

Adding a Looping Video Preview

You may have noticed that black box at the top of the list. Your next task is turning that black box into a custom video player. Its purpose is to play a revolving set of clips to get users excited about all these videos.

Looping video player at the top of the list

Then, you need to add a few custom gestures, like tapping to turn on sound and double-tapping to change it to 2x speed. When you want to have very specific control over how things work, it’s better to write your own video view.

It’s your job to get things going.

Understanding AVFoundation

While AVFoundation can feel a bit intimidating, most of the objects you deal with are still pretty high-level.

The main classes you’ll need to get familiar with are:

  1. AVPlayerLayer: This special CALayer subclass can display the playback of a given AVPlayer object.
  2. AVAsset: These are static representations of a media asset. An asset object contains information such as duration and creation date.
  3. AVPlayerItem: The dynamic counterpart to an AVAsset. This object represents the current state of a playable video. This is what you need to provide to AVPlayer to get things going.

AVFoundation is a huge framework that goes well beyond these few classes. Fortunately, this is all you’ll need to create your looping video player.

You’ll come back to each of these in turn, so don’t worry about memorizing them.

Writing a Custom Video View With AVPlayerLayer

The first class you need to cozy up to is AVPlayerLayer. This CALayer subclass is like any other layer: It displays whatever is in its contents property.

This layer just happens to fill its contents with frames from a video you’ve given it via its player property.

The problem is that you can’t use this layer directly in SwiftUI. After all, SwiftUI doesn’t have the concept of CALayers. For that, you need to go back to the old and gold UIKit.

Go to LoopingPlayerView.swift, where you’ll find an empty view you’ll use to show videos. It needs an array of video URLs to play.

The first thing you need to do is add the proper import statement, this time for AVFoundation:

import AVFoundation

Good start! Now you can get AVPlayerLayer into the mix.

A UIView is simply a wrapper around a CALayer. It provides touch handling and accessibility features but isn’t a subclass. Instead, it owns and manages an underlying layer property. One nifty trick is that you can actually specify what type of layer you would like your view subclass to own.

Add the following property override to tell LoopingPlayerView.swift that it should use an AVPlayerLayer instead of a plain CALayer:

override class var layerClass: AnyClass {
  return AVPlayerLayer.self
}

Since you’re wrapping the player layer in a view, you need to expose a player property.

To do so, add the following computed property so you don’t need to cast your layer subclass all the time:

var playerLayer: AVPlayerLayer {
  return layer as! AVPlayerLayer
}

To be able to use this view in SwiftUI, you need to create a wrapper using UIViewRepresentable.

Add these lines of code in the same file, outside the LoopingPlayerUIView definition:

struct LoopingPlayerView: UIViewRepresentable {
  let videoURLs: [URL]
}

UIViewRepresentable is a protocol. You need to implement its methods to complete the bridge between UIKit and SwiftUI.

Add these inside LoopingPlayerView:

// 1
func makeUIView(context: Context) -> LoopingPlayerUIView {  
  // 2
  let view = LoopingPlayerUIView(urls: videoURLs)
  return view
}

// 3
func updateUIView(_ uiView: LoopingPlayerUIView, context: Context) { }
  1. SwiftUI calls makeUIView(context:) when it needs a new instance of your UIView.
  2. You create a new instance of LoopingPlayerUIView using the initializer and returning the new instance.
  3. SwiftUI calls this method when it needs to update the underlying UIView. For now, leave it empty.

Now, get back to VideoFeedView.swift and add the following property to get URLs for video clips:

private let videoClips = VideoClip.urls

Inside makeEmbeddedVideoPlayer(), replace Rectangle() with the following code, but keep the view modifiers:

LoopingPlayerView(videoURLs: videoClips)

Build and run to see… nothing new! You just passed the video clip URLs to the view but you didn’t do anything with them yet.

Writing the Looping Video View

Next, go over to LoopingPlayerView.swift and get ready to add a player. After all, you now know you need a player to make video playing work.

To get started, add the following player property to LoopingPlayerUIView:

private var player: AVQueuePlayer?

The discerning eye will see that this is no plain AVPlayer instance. That’s right, this is a special subclass called AVQueuePlayer. As you can probably guess by the name, this class allows you to provide a queue of items to play.

Replace init(urls:) with the following to initialize the player:

init(urls: [URL]) {
  allURLs = urls
  
  player = AVQueuePlayer()

  super.init(frame: .zero)

  playerLayer.player = player
}

Here, you first create the player object and then connect it to the underlying AVPlayerLayer.

Now, it’s time to add your list of video clips to the player so it can start playing them.

Add the following method to do so:

private func addAllVideosToPlayer() {
  for url in allURLs {
    // 1
    let asset = AVURLAsset(url: url)

    // 2
    let item = AVPlayerItem(asset: asset)

    // 3
    player?.insert(item, after: player?.items().last)
  }
}

Here, you’re looping through all the clips. For each one, you:

  1. Create an AVURLAsset from the URL of each video clip object.
  2. Then, you create an AVPlayerItem with the asset that the player can use to control playback.
  3. Finally, you use insert(_:after:) to add each item to the queue.

Now, go back to init(urls:) and call the method after super.init(frame:) and before setting player to playerLayer:

addAllVideosToPlayer()

Now that you have your player set, it’s time to do some configuration.

To do this, add the following two lines in init(urls:) after addAllVideosToPlayer():

player?.volume = 0.0
player?.play()

This sets your looping clip show to auto-play and audio off by default.

Build and run to see your fully working clip show!

Black loop clip without repeat

Unfortunately, when the last clip has finished playing, the video player fades to black.