Skip to content

SvenTiigi/YouTubePlayerKit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation


logo

YouTubePlayerKit

A Swift Package to easily play YouTube videos.

Swift Version Platforms
Build and Test Status Documentation Twitter Mastodon

Example application

import SwiftUI
import YouTubePlayerKit

struct ContentView: View {

    var body: some View {
        // ๏ฃฟ WWDC 2019 Keynote
        YouTubePlayerView(
            "https://youtube.com/watch?v=psL_5RIBqnY"
        )
    }

}

Features

  • Play YouTube videos with just one line of code ๐Ÿ“บ
  • YouTube Terms of Service compliant implementation โœ…
  • Access to all native YouTube iFrame APIs ๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป
  • Support for SwiftUI, UIKit and AppKit ๐Ÿง‘โ€๐ŸŽจ
  • Runs on iOS, macOS and visionOS ๐Ÿ“ฑ ๐Ÿ–ฅ ๐Ÿ‘“

Example

Check out the example application to see YouTubePlayerKit in action. Simply open the Example/Example.xcodeproj and run the "Example" scheme.

Installation

To integrate using Apple's Swift Package Manager, add the following as a dependency to your Package.swift:

dependencies: [
    .package(url: "https://github.com/SvenTiigi/YouTubePlayerKit.git", from: "2.0.0")
]

Or navigate to your Xcode project then select Swift Packages, click the โ€œ+โ€ icon and search for YouTubePlayerKit.

Note

When integrating YouTubePlayerKit to a macOS or Mac Catalyst target please ensure to enable "Outgoing Connections (Client)" in the "Signing & Capabilities" sections.

App Store Review

When submitting an app to the App Store which includes the YouTubePlayerKit, please ensure to add a link to the YouTube API Terms of Services in the review notes.

https://developers.google.com/youtube/terms/api-services-terms-of-service

Limitations

  • Audio background playback is not supported as it violates the YouTube Terms of Service.
  • Simultaneous playback of multiple YouTube players is not supported.
  • Controlling playback of 360ยฐ videos is not supported.

Usage

Tip

Please refer to the YouTubePlayerKit documentation for a complete overview.

A YouTubePlayer can be easily displayed when using SwiftUI by declaring a YouTubePlayerView.

import SwiftUI
import YouTubePlayerKit

struct ContentView: View {

    let youTubePlayer: YouTubePlayer = "https://youtube.com/watch?v=psL_5RIBqnY"

    var body: some View {
        YouTubePlayerView(self.youTubePlayer) { state in
            // An optional overlay view for the current state of the player
            switch state {
            case .idle:
                ProgressView()
            case .ready:
                EmptyView()
            case .error(let error):
                ContentUnavailableView(
                    "Error",
                    systemImage: "exclamationmark.triangle.fill",
                    description: Text("YouTube player couldn't be loaded: \(error)")
                )
            }
        }
        // Optionally react to specific updates such as the fullscreen state
        .onReceive(
            self.youTubePlayer.fullscreenStatePublisher
        ) { fullscreenState in
            if fullscreenState.isFullscreen {
                // ...
            }
        }
    }

}

Tip

You can optionally mark the YouTubePlayer with @StateObject or @ObservedObject to automatically update your view whenever the source, parameters, or isLoggingEnabled change.

When using UIKit or AppKit you can make use of the YouTubePlayerViewController or YouTubePlayerHostingView.

import UIKit
import YouTubePlayerKit

let youTubePlayerViewController = YouTubePlayerViewController(
    player: "https://youtube.com/watch?v=psL_5RIBqnY"
)

let youTubePlayerHostingView = YouTubePlayerHostingView(
    player: "https://youtube.com/watch?v=psL_5RIBqnY"
)

// Access the player on both instances via the `.player` property
// Example: youTubePlayerViewController.player

YouTubePlayer

A YouTubePlayer is the central object in order to play a certain YouTube video and interact with the underlying YouTube iFrame API.

As seen in the previous examples you can initialize a player by using a string literal:

let youTubePlayer: YouTubePlayer = "https://youtube.com/watch?v=psL_5RIBqnY"

To take full control you can initialize a YouTubePlayer with a YouTubePlayer.Source, YouTubePlayer.Parameters and a YouTubePlayer.Configuration

let youTubePlayer = YouTubePlayer(
    // Possible values: .video, .videos, .playlist, .channel
    source: .video(id: "psL_5RIBqnY"),
    // The parameters of the player
    parameters: .init(
        autoPlay: true,
        showControls: true,
        loopEnabled: true,
        startTime: .init(value: 5, unit: .minutes),
        // ...
    ),
    // The configuration of the underlying web view
    configuration: .init(
        fullscreenMode: .system,
        allowsInlineMediaPlayback: true,
        customUserAgent: "MyCustomUserAgent",
        // ...
    )
)

To differentiate between parameters and configuration, understand that parameters control the behavior and style of the YouTube player, while the configuration is linked to the underlying web view. You cannot modify the configuration after instantiation; however, it is possible to update the parameters, as shown below:

youTubePlayer.parameters.showControls = false

Warning

Updating the YouTubePlayer.Parameters during runtime will cause the player to reload.

Source

The YouTubePlayer.Source is an enum which allows you to specify which YouTube source should be loaded/cued.

// A single video
let video: YouTubePlayer.Source = .video(id: "psL_5RIBqnY")

// Series of videos
let videos: YouTubePlayer.Source = .videos(ids: ["w87fOAG8fjk", "RXeOiIDNNek", "psL_5RIBqnY"])

// Playlist
let playlist: YouTubePlayer.Source = .playlist(id: "PLHFlHpPjgk72Si7r1kLGt1_aD3aJDu092")

// Channel
let channel: YouTubePlayer.Source = .channel(name: "GoogleDevelopers")

You can also use a URL to initialize a source.

let source: YouTubePlayer.Source? = .init(urlString: "https://youtube.com/watch?v=psL_5RIBqnY")

Note

The URL parsing logic is designed to handle most known YouTube URL formats, but there may be some variations that it doesn't cover.

API

A YouTubePlayer lets you access the underlying YouTube Player iFrame API to play, pause, seek, retrieve video information and much more.

The majority of the APIs are async and throwable functions.

// Pauses the currently playing video
try await youTubePlayer.pause()

Tip

Please see the documentation for a full overview of the available APIs.

In case of an error, most functions throw a YouTubePlayer.APIError. This allows you to easily examine the reason for the error, any underlying error, and the executed JavaScript along with its response.

do {
    try await youTubePlayer.setCaptions(fontSize: .small)
} catch {
    print(
        "Failed to set captions font size",
        error.reason,
        error.underlyingError,
        error.javaScript,
        error.javaScriptResponse
    )
}

Additionally, several Publishers are available to react to changes of the player:

// Observe playback metadata
let cancellable = youTubePlayer
    .playbackMetadataPublisher
    .sink { playbackMetadata in
        // ...
    }

Video Thumbnail

You can load a YouTube video thumbnail via the YouTubeVideoThumbnail object.

// Initialize an instance of YouTubeVideoThumbnail
let videoThumbnail = YouTubeVideoThumbnail(
    videoID: "psL_5RIBqnY",
    // Choose between default, medium, high, standard, maximum
    resolution: .high
)

// Retrieve the URL, if available
let url: URL? = videoThumbnail.url

// Retrieve the image, if available.
let image: YouTubeVideoThumbnail.Image? = try await videoThumbnail.image()

Additionally, the player allows you to easily retrieve the thumbnail url and image for the currently loaded video.

// Returns the video thumbnail URL of the currently loaded video
try await youTubePlayer.getVideoThumbnailURL()

/// Returns the video thumbnail of the currently loaded video
try await youTubePlayer.getVideoThumbnailImage(resolution: .maximum)

Logging

If you wish to gain more insights into the underlying communication of the YouTube Player iFrame JavaScript API, you can enable the logging of a player via the isLogginEnabled parameter.

The YouTubePlayer utilizes the unified logging system (OSLog) to log information about the player options, JavaScript events and evaluations.

// Enable or disable logging during initialization
let youTubePlayer = YouTubePlayer(
    source: [
        "w87fOAG8fjk",
        "RXeOiIDNNek",
        "psL_5RIBqnY"
    ],
    isLoggingEnabled: true
)

// To update during runtime update the isLoggingEnabled property
youTubePlayer.isLoggingEnabled = false

// Additionally, you can retrieve an instance of the logger if logging is enabled.
let logger: Logger? = youTubePlayer.logger()

Advanced

You can observe the incoming stream of YouTubePlayer.Event from the underlying YouTube Player iFrame API through the following publisher.

let cancellable = youTubePlayer
    .eventPublisher
    .sink { event in
        switch event.name {
        case .playbackQualityChange:
            break
        case .autoplayBlocked:
            break
        default:
            break
        }
    }

Important

The YouTubePlayerKit supports both official as well as unofficial/undocumented events. Please see the YouTubePlayer.Event.Name enumeration for more details.

To run custom JavaScript on the YouTube player JavaScript instance:

try await youTubePlayer.evaluate(
    javaScript: "\(.youTubePlayer).play()"
)

Note

The custom string interpolation of \(.youTubePlayer) is a placeholder for the YouTube player JavaScript variable.

Alternatively, you can use the following convenience function to directly invoke a function on the YouTube player JavaScript object:

try await youTubePlayer.evaluate(
    javaScript: .youTubePlayer(
        functionName: "setLoop",
        parameters: [
            true
        ]
    )
)

If you wish to further customize the underlying HTML you can configure the YouTubePlayer.HTMLBuilder when initializing an instance of the player:

let youTubePlayer = YouTubePlayer(
    source: .video(id: "psL_5RIBqnY"),
    configuration: .init(
        htmlBuilder: .init(
            youTubePlayerJavaScriptVariableName: "youtubePlayer",
            youTubePlayerEventCallbackURLScheme: "youtubeplayer",
            youTubePlayerEventCallbackDataParameterName: "data",
            youTubePlayerIframeAPISourceURL: .init(string: "https://www.youtube.com/iframe_api")!,
            htmlProvider: { htmlBuilder, jsonEncodedYouTubePlayerOptions in
                // TODO: Return custom HTML string
            }
        )
    )
)

Credits