Swift
iOS Development
Video Playback
Programming Tutorial
AVKit

How to play a local video with Swift?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Playing a local video in iOS is straightforward with AVPlayer and AVPlayerViewController. The key is locating the file URL correctly, configuring the player on the main thread, and handling playback lifecycle events. This approach works for bundled files and app sandbox files.

Play a Bundled Video with AVPlayerViewController

If the video is included in the app bundle, resolve it with Bundle.main.url and pass it to AVPlayer.

swift
1import UIKit
2import AVKit
3import AVFoundation
4
5final class VideoViewController: UIViewController {
6    override func viewDidAppear(_ animated: Bool) {
7        super.viewDidAppear(animated)
8        playBundledVideo()
9    }
10
11    private func playBundledVideo() {
12        guard let url = Bundle.main.url(forResource: "demo", withExtension: "mp4") else {
13            print("video not found")
14            return
15        }
16
17        let player = AVPlayer(url: url)
18        let controller = AVPlayerViewController()
19        controller.player = player
20
21        present(controller, animated: true) {
22            player.play()
23        }
24    }
25}

Add demo.mp4 to target membership in Xcode so the file is copied into the app bundle.

Play a File from Documents Directory

For downloaded or generated videos, build a URL in the app documents folder.

swift
1import Foundation
2import AVFoundation
3
4func localDocumentsVideoURL(fileName: String) -> URL {
5    let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
6    return documents.appendingPathComponent(fileName)
7}
8
9let url = localDocumentsVideoURL(fileName: "recording.mp4")
10print(url.path)

Then use the same AVPlayer initialization pattern as bundled assets.

Embed Player in View Hierarchy

If you want inline playback instead of a modal controller, attach AVPlayerLayer to a custom view.

swift
1import UIKit
2import AVFoundation
3
4final class InlineVideoView: UIView {
5    private var player: AVPlayer?
6    private var layerPlayer: AVPlayerLayer?
7
8    func configure(with url: URL) {
9        player = AVPlayer(url: url)
10        let playerLayer = AVPlayerLayer(player: player)
11        playerLayer.videoGravity = .resizeAspect
12        playerLayer.frame = bounds
13        layer.addSublayer(playerLayer)
14        layerPlayer = playerLayer
15    }
16
17    override func layoutSubviews() {
18        super.layoutSubviews()
19        layerPlayer?.frame = bounds
20    }
21
22    func play() {
23        player?.play()
24    }
25}

Inline playback gives more UI flexibility but requires manual lifecycle management.

Handle Playback Completion and Errors

Observe end notifications and item status so users get clear feedback.

swift
1NotificationCenter.default.addObserver(
2    forName: .AVPlayerItemDidPlayToEndTime,
3    object: nil,
4    queue: .main
5) { _ in
6    print("playback finished")
7}

For production code, remove observers on deinit and surface recoverable errors in UI.

Add Basic Playback Controls

For custom interfaces, connect play and pause actions to the same player instance and expose simple state updates for UI controls.

swift
1import AVFoundation
2
3final class VideoController {
4    let player: AVPlayer
5
6    init(url: URL) {
7        self.player = AVPlayer(url: url)
8    }
9
10    func play() {
11        player.play()
12    }
13
14    func pause() {
15        player.pause()
16    }
17
18    func seekToStart() {
19        player.seek(to: .zero)
20    }
21}

You can bind these methods to button taps in UIKit or SwiftUI wrappers. Keeping playback commands in one small controller class improves testability and reduces repeated state logic across views. For long videos, also consider observing timeControlStatus to update loading indicators and playback buttons in sync with real player state during buffering events and intermittent network delays on device hardware in production apps.

Common Pitfalls

A common mistake is using wrong file path assumptions for bundled assets. Always resolve URLs through Bundle.main or FileManager.

Another issue is presenting player controller before setting player. Configure controller first, then present and play.

A third issue is forgetting target membership for local resource files, which makes bundled videos unavailable at runtime.

Summary

  • Use AVPlayerViewController for quick and reliable local video playback.
  • Resolve bundled and documents file URLs with the correct API.
  • Use AVPlayerLayer for inline playback layouts.
  • Observe completion events for better playback UX.
  • Validate resource inclusion and path correctness before debugging playback.

Course illustration
Course illustration

All Rights Reserved.