Logo

Application collectors

AVPlayer Media Player v3

For the IOS / tvOS Collector

The AVPlayer Media Player v3 Extension is a configuration option for the IOS / tvOS Collector by Datazoom that makes the following additional data points automatically collectable in real time.

Integration Instructions

DzAVPlayerAdapter

The DzAVPlayerAdapter is designed to provide integration support for the AVPlayer implementations.

Description

The DzAVPlayerAdapter project offers a set of utilities and functionality to facilitate seamless integration with AVPlayer implementations. It provides common code and abstraction layers to streamline the integration of Datazoom's tracking capabilities into AVPlayer-powered applications.

Installation

To use the DzAVPlayerAdapter project in your application, include the following dependency in your project's Gradle file:

The preferred way of installing Collector is via the Swift Package Manager.

Swift package manager


Add Swift Package Dependency - navigate to File → Add Packages
Paste the repository URL: https://gitlab.com/datazoom/apple/libraries-release/apple_dz_avplayer_adapter
For Rules, select Branch (with branch set to main) and click Add Package.
On the confirmation dialog click Add Package, again.

Note: Instead of downloading the full git history of DzAVPlayerAdapter and building it from source, this repo just contains a pointer to the precompiled XCFramework included in the latest DzAVPlayerAdapter release.

Cocoapods

Add the 'DzAVPlayerAdapter' pod in the podfile

source 'https://gitlab.com/datazoom/pod-specs.git'
pod 'DzAVPlayerAdapter'

Usage

To integrate the DzAVPlayerAdapter project into your project:

Initialize DzAVPlayerAdapter by providing your Datazoom configuration id and configuring necessary settings.

Initialization

 let configBuilder = Config.Builder(configurationId: configId)
 configBuilder.logLevel(logLevel: .verbose)
 configBuilder.isProduction(isProduction: isProduction)
 
 Datazoom.shared.doInit(config: configBuilder.build())

Create Context

If you want to track events from your AVPlayer, you need to create a context and assign your player to it so that the SDK can track events and metadata from your specified player.

/**
  *  Creates a player context. A player context is used to embed the player with Datazoom and is responsible for player-specific events.
  *  - Parameters:
  *      - player: The AVPlayer instance.
  *      - eventSpace: The event space to be used to create the player context. This parameter is optional. If not provided, a default event space will be created.
  *  - Returns: The DzAdapter.
 * @see <a href="https://help.datazoom.io/">Datazoom Help</a>
 */
func createContext(
    player: AVPlayer,
    eventSpace: BaseContext = Datazoom.shared.createBaseContext()
) -> DzAdapter
Example
Datazoom.shared.createContext(player: yourAVPlayer)

Custom Metadata - Context

Context custom metadata is used to attach metadata, which is a Dictionary of your choice, to each event sent from the given context you created using the Datazoom object. For more information, please refer to the Datazoom documentation on custom metadata.

API looks like

/**
 * Sends metadata to Context.
 *
 * - Parameters:
        - metadata: a Dictionary that will be attached to each event being sent from SDK, for more information on this topic, please visit our
 *   documentation.
 */
func setMetadata(metadata: <String, Any>)
Example
yourAVPlayerContext.setMetadata(
    [
        "property": "custom property",
        "property2": "custom property2",
    ]
)

You can also retrieve your current metadata by calling the following API:

/**
 * Returns the metadata that is being sent with each event in given context.
 *
 * @return The metadata that is being sent with each event in given context.
 */
func getMetadata() -> <String, Any>
Example
yourAVPlayerContext.getMetadata()

Casting

We have the ability to send casting events, we don't track any communication you have with Chromecast receiver. We expect you to invoke context/adapter following function with a bool sending casting state.

func sendCastEvent(isCasting: Bool)
Example
yourAVPlayerContext.sendCastEvent(isCasting = {castingState})

Player width and height

We have the ability to gather player width and height and track if there any change in the dimensions. We expect you to invoke context/adapter following function with a UIView sending the superview of the AVPlayer.

func setPlayerView(playerView: UIView)
Example
yourAVPlayerContext.setPlayerView(playerView: yourVideoPlayerView)

Fullscreen

We have the ability to send Fullscreen/Exit Fullscreen events. We expect you to invoke context/adapter following function with a boolean sending Fullscreen state.

func sendFullScreenEvent(isFullScreen: Bool)
Example
yourAVPlayerContext.sendFullScreenEvent(isFullScreen = {isFullScreen})

Custom Events - Context

To send events in the context scope, you can use the following method, with each parameter explained below.

/**
 * Sends an event to given context.
 *
 *   - Parameters:
 *      - name: The name of the event.
 *      - payload: a Dictionary that will be attached to each event being sent from given context, for more information on this topic, please visit our   documentation.
 */
func generateEvent(name: String, payload: <String, Any>)
Example
yourAVPlayerContext.generateEvent(
    name = "login",
    eventMap =
        [
            "property": "custom property",
            "property2": "custom property2",
        ]
)

Custom Events - Global

To send events in the global scope, you can use the following method, with each parameter explained below.

/**
 * Sends an event to Datazoom.
 *
 *  - Parameters:
 *      - name: The name of the event.
 *      - payload: a Dictionary that will be attached to each event being sent from SDK, for more information on this topic, please visit our documentation.
 */
fun generateEvent(name: String, payload: <String, Any>)
Example
Datazoom.shared.generateEvent(
    name = "login",
    eventMap =
        [
            "property": "custom property",
            "property2": "custom property2",
        ]
)

Custom Metadata - Global

Global metadata is used to attach metadata, which is a HashMap of your choice, to each event sent from any context you created using the Datazoom object. For more information, please refer to the Datazoom documentation on custom metadata.

API looks like

/**
 * Sends metadata to Datazoom.
 * 
 *  - Parameters:
 *      - metadata: a Dictionary that will be attached to each event being sent from SDK, for more information on this topic, please visit our
 *   documentation.
 */
fun setMetadata(metadata: <String, Any>)
Example
Datazoom.shared.setMetadata(
    [
        "property": "custom property",
        "property2": "custom property2",
    ]
)

Get all player context

Datazoom supports having multiple contexts. You can create multiple contexts and Datazoom will keep track of all created contexts and give ability to get them if you need them anytime of your app lifecycle.

API looks like

/**
 * Returns a list of all player contexts.
 *
 * @return A list of all player contexts. You can retrieve list of all player contexts at anytime in player lifecycle.
 */
fun playerContexts() -> [DzAdapter]
Example
Datazoom.shared.playerContexts()

Destroy Context

If you want to destroy a context, there are multiple ways to do it. Ideally, you should ask the Datazoom object to destroy the context by either providing your context ID or the context/adapter itself.

/**
 * Removes a player context.
 *
 * @param adapter The adapter to be used to remove the context.
 */
fun removeContext(adapter: DzAdapter)

or

/**
 * Removes a player context.
 *
 * @param id The id of the player context to be removed. its the same id returned by
 * @sample createContext(adapter: DzAdapter): String
 */
fun removeContext(id: String)

Please make sure that once the context is removed from Datazoom, you do not use it for communication because it will no longer be able to communicate with the server, and you might miss critical events.

Example
  Datazoom.shared.removeContext("contextId")

or

  Datazoom.shared.removeContext(context)

The appropriate place to destroy the context could be the deinit lifecycle method of the ViewController.

deinit {
    Datazoom.shared.removeContext(adapter: context)
  }

Listening SDK events

 Datazoom.shared.sdkEvents.watch { event in
    guard let eventDescription = event?.description as? String else { return }
    if eventDescription.contains("SdkInit") {
        // SDK initialized
    } else if eventDescription.contains("SdkError") {
        // SDK error 
    } else if eventDescription.contains("AdapterCreated") {
        // Adapter created 
     }
}

Supported Client Events

SdkInit

The "SdkInit" event signals the successful initialization of the SDK and the receipt of the API key used during initialization. Upon receiving this event, users can confidently proceed with utilizing the SDK's functionalities, knowing that the SDK is ready for use with the provided API key. Ensure to handle this event appropriately in your application code to synchronize operations requiring the SDK's readiness.

SdkError

The "SdkError" event is triggered when an error occurs during the operation of the SDK. This event provides crucial information about the nature of the error, such as error codes or descriptive messages, allowing developers to identify and handle errors effectively within their applications.

AdapterCreated

The "AdapterCreated" event signifies the successful creation of an adapter within the SDK. This event provides notification to developers that the adapter, responsible for interfacing with external systems or components, is ready for use. Utilize this event to synchronize operations dependent on the availability of the adapter within your application code.

Objective-C Integration

Our XCFramework is written in Swift, which means it cannot be consumed directly in Objective-C projects. To overcome this, you need to create a Swift wrapper that exposes the required APIs in an Objective-C-compatible way.

We’ve already provided a Sample Project demonstrating this approach. Below is a step-by-step guide.

Why a Wrapper is Needed

Objective-C cannot directly recognize Swift-only features or certain language constructs. The solution is to write a thin Swift wrapper layer, where you mark the methods with @objc and make them available to Objective-C using the auto-generated Swift bridging header (-Swift.h).

Creating a Wrapper

Create a Swift file, for example DatazoomWrapper.swift, inside your project:

import Foundation
import AVKit
import DzAVPlayerAdapter
import DzBase

@objc public class DatazoomWrapper: NSObject {

  @objc
  @MainActor static let sharedInstance = DatazoomWrapper()
  private override init() {}
  
  private var dzAdapter: DzAdapter? = nil

  @objc
  func initializeDZ(configId: String, isProduction: Bool) {
    let configBuilder = Config.Builder(configurationId: configId)
    configBuilder.logLevel(logLevel: .verbose)
    configBuilder.isProduction(isProduction: isProduction)
    Datazoom.shared.doInit(config: configBuilder.build())
  }
    
  @objc
  func setupDataZoomAdapter(player: AVPlayer, videoUrl: String, videoPlayerView: UIView) {
    dzAdapter = Datazoom.shared.createContext(player: player)
  }
  
  @objc
  func destroyDzAdapter() {
    guard let dzAdapter else { return}
    Datazoom.shared.removeContext(adapter: dzAdapter)
  }
}

Key points:

The wrapper class must inherit from NSObject.

Methods and properties you want visible in Objective-C must be marked @objc.

Make sure the wrapper itself is marked public if used from another module.

Consuming from Objective-C

Once the wrapper is in place, Xcode generates a bridging header named:

<ProjectName>-Swift.h

You can import this in your Objective-C files:

#import "<YourProjectName>-Swift.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [[DatazoomWrapper sharedInstance] initializeDZWithConfigId:@"Enter your config id" isProduction: NO];
    ////
    //// some code
}

@end

Step-by-Step Summary

Add the Datazoom dependencies to your project.

Create a Swift wrapper file.

Ensure Defines Module = YES in Build Settings.

Import -Swift.h in your Objective-C files.

Call into the wrapper just like any Objective-C class.

Sample Project

For a working example, check out our Objective-C Integration Sample. It demonstrates:

How the wrapper is written.

How it’s imported into Objective-C.

How methods are called end-to-end.

Issues

If you encounter any issues or have suggestions for improvements, please open an issue on the GitHub repository.