Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/1.0.3 #15

Merged
merged 11 commits into from
Jun 14, 2024
4 changes: 3 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ let package = Package(
name: "PlaybackSDK",
dependencies: [
.product(name: "BitmovinPlayer", package: "BitmovinPlayer"),
]
],
resources: [
.process("PrivacyInfo.xcprivacy")]
),
.testTarget(
name: "PlaybackSDKTests",
Expand Down
26 changes: 18 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,23 +84,33 @@ PlayBackSDKManager.shared.loadPlayer(entryID: entryId, authorizationToken: autho
```

# Playing Access-Controlled Content
To play premium or freemium on-demand and live videos, an `authorizationToken` has to be passed into the player.
Before loading the player, a call to CloudPay to start session must be made with the same token.
To play on-demand and live videos that require authorization, at some point before loading the player your app must call CloudPay to start session, passing the authorization token:
```swift
"\(baseURL)/sso/start?token=\(authorizationToken)"
```
In case a custom `user-agent` header is set for the request when creating a token, it should be passed to the player as well.
Then the same token should be passed into the `loadPlayer(entryID:, authorizationToken:)` method of `PlayBackSDkManager`.
For the free videos that user should be able to watch without logging in, starting the session is not required and `authorizationToken` can be set to an empty string.

> [!NOTE]
> If the user is authenticated, has enough access level to watch a video, the session was started and the same token was passed to the player but the videos still throw a 401 error, it might be related to these requests having different user-agent headers.

## Configure user-agent
Sometimes a custom `user-agent` header is automatically set for the requests on iOS when creating a token and starting a session. `Alamofire` and other 3rd party networking frameworks can modify this header to include information about themselves. In such cases they should either be configured to not modify the header, or the custom header should be passed to the player as well.

Example:

```swift
PlayBackSDKManager.shared.initialize(apiKey: apiKey, baseURL: baseURL, userAgent: customUserAgent) { result in 
// Handle player UI error 
PlayBackSDKManager.shared.initialize(
apiKey: apiKey,
baseURL: baseURL,
userAgent: customUserAgent
) { result in
// Handle player UI error
}
```
By default the SDK uses system user agent, so if your app uses native URL Session, the `userAgent` parameter most likely can be omitted.


**Resources:**
# Resources

- **Tutorial:** [Tutorial](https://streamamg.github.io/playback-sdk-ios/tutorials/table-of-contents/#resources)
- **Demo app:** [GitHub Repository](https://github.com/StreamAMG/playback-demo-ios)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
//
// InitializeSdkExample.swift
//
//
// Created by Franco Driansetti on 27/02/2024.
//

import PlaybackSDK

PlaybackSDKManager.shared.initialize(apiKey: "YOUR_API_KEY") { result in
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
//
// InstallPlayerPluginTutorial.swift
//
//
// Created by Franco Driansetti on 27/02/2024.
//

import Foundation

PlayBackSDKManager.shared.initialize(apiKey: settingsManager.apiKey, baseURL: settingsManager.baseURL) { result in
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
//
// LoadHlsStreamTutorial.swift
//
//
// Created by Franco Driansetti on 27/02/2024.
//

Swift

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
//
// LoadPlayerViewTutorial.swift
//
//
// Created by Franco Driansetti on 27/02/2024.
//

import Foundation

PlayBackSDKManager.shared.loadPlayer(entryID: settingsManager.entryId, authorizationToken: settingsManager.authorizationToken, onError: { error in
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Foundation

public enum PlayBackAPIError: Error {

case invalidResponsePlaybackData

case invalidPlaybackDataURL

case invalidPlayerInformationURL

case initializationError

case loadHLSStreamError

case networkError(Error)

case apiError(statusCode: Int, message: String)
}
Original file line number Diff line number Diff line change
@@ -1,41 +1,35 @@
//
// InitializeSdkExample.swift
//
//
// Created by Franco Driansetti on 27/02/2024.
//

import SwiftUI
import PlaybackSDK

@main
struct PlayBackDemoApp: App {

let sdkManager = PlayBackSDKManager()
let apiKey = "API_KEY"
var body: some Scene {
WindowGroup {
HomeView()
}
}

init() {
// Initialize the Playback SDK with the provided API key and base URL
PlayBackSDKManager.shared.initialize(apiKey: apiKey) { result in
switch result {
case .success(let license):
// Obtained license upon successful initialization
print("SDK initialized with license: \(license)")

// Register the video player plugin
let bitmovinPlugin = BitmovinPlayerPlugin()
VideoPlayerPluginManager.shared.registerPlugin(bitmovinPlugin)

case .failure(let error):
// Print an error message and set initializationError flag upon initialization failure
print("SDK initialization failed with error: \(error)")

}
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import SwiftUI
import PlaybackSDK
import Alamofire

@main
struct PlayBackDemoApp: App {

let sdkManager = PlayBackSDKManager()
let apiKey = "API_KEY"
var body: some Scene {
WindowGroup {
HomeView()
}
}

init() {
// Get the user-agent set by Alamofire
let userAgent = AF.session.configuration.httpAdditionalHeaders?["User-Agent"]

// Initialize the Playback SDK with the provided API key and custom user-agent
PlayBackSDKManager.shared.initialize(apiKey: apiKey, userAgent: userAgent) { result in
switch result {
case .success(let license):
// Obtained license upon successful initialization
print("SDK initialized with license: \(license)")

// Register the video player plugin
let bitmovinPlugin = BitmovinPlayerPlugin()
VideoPlayerPluginManager.shared.registerPlugin(bitmovinPlugin)

case .failure(let error):
// Print an error message and set initializationError flag upon initialization failure
print("SDK initialization failed with error: \(error)")

}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
//
// LoadPlayerViewTutorial.swift
//
//
// Created by Franco Driansetti on 27/02/2024.
//

import SwiftUI
import PlaybackSDK

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,20 @@
@Steps {

@Step {
**Step 1:** Initialize the Playback SDK by providing your API key and register the default player plugin.
Initialize the Playback SDK by providing your API key and register the default player plugin.
**Make sure this step is done when the app starts.**


@Code(name: "PlayBackDemoApp.swift", file: PlayBackDemoApp.swift)
}
@Step {
**Step 2:**
Add custom `user-agent` header.

This step is only required for content that needs a token, when using Alamofire or other 3rd party frameworks that overwrite the standard `user-agent` header with their own.
If the content requires starting a CloudPay session, it's important that the request to start the session has the same `user-agent` header as the video loading requests from the player. This can be achieved either by disabling the overwriting behaviour in the 3rd party networking framework you're using, or by passing a `userAgent` parameter to the `initialize` method, like in this example with Alamofire.
@Code(name: "PlayBackDemoAppWithUserAgent.swift", file: PlayBackDemoAppWithUserAgent.swift, previousFile: PlayBackDemoApp.swift)
}
@Step {
Load the player using the Playback SDK and handle any playback errors.

In this step, the code utilizes the **loadPlayer** function provided by the Playback SDK to initialize and load the video player. The function takes the entry ID and authorization token as parameters. Additionally, it includes a closure to handle any potential playback errors that may occur during the loading process.
Expand All @@ -34,6 +40,12 @@

@Code(name: "PlayerTestView.swift", file: PlayerTestView.swift)
}
@Step {
Handle the playback errors from Playback SDK.

This step describes enum for error handling. Above is the error enum returned by the SDK, where the apiError also has the reason code and message for the API error. The playback API is returning the reason code in the response. For the list of the error codes and reasons, please refer to [Get Video Playback Data | Playback](https://streamamg.stoplight.io/docs/playback-documentation-portal/ec642e6dcbb13-get-video-playback-data)
@Code(name: "PlayBackAPIError.swift", file: PlayBackAPIError.swift)
}
}
}
}
107 changes: 107 additions & 0 deletions Sources/PlaybackSDK/PrivacyInfo.xcprivacy
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyCollectedDataTypes</key>
<array>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeEmailAddress</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<true/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
</array>
</dict>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeOtherDiagnosticData</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<false/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
</array>
</dict>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeUserID</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<true/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeProductPersonalization</string>
</array>
</dict>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeOtherDataTypes</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<false/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
</array>
</dict>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeOtherUsageData</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<false/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
</array>
</dict>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypePaymentInfo</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<true/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeProductPersonalization</string>
</array>
</dict>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypePerformanceData</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<false/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
</array>
</dict>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypePurchaseHistory</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<true/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeProductPersonalization</string>
</array>
</dict>
</array>
<key>NSPrivacyTracking</key>
<false/>
</dict>
</plist>
2 changes: 1 addition & 1 deletion docs/data/documentation/playbacksdk.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"metadata":{"role":"collection","symbolKind":"module","externalID":"PlaybackSDK","roleHeading":"Framework","title":"PlaybackSDK","modules":[{"name":"PlaybackSDK"}]},"identifier":{"url":"doc:\/\/PlaybackSDK\/documentation\/PlaybackSDK","interfaceLanguage":"swift"},"sections":[],"hierarchy":{"paths":[[]]},"kind":"symbol","variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/playbacksdk"]}],"schemaVersion":{"minor":3,"major":0,"patch":0},"references":{"doc://PlaybackSDK/documentation/PlaybackSDK":{"title":"PlaybackSDK","role":"collection","abstract":[],"type":"topic","kind":"symbol","url":"\/documentation\/playbacksdk","identifier":"doc:\/\/PlaybackSDK\/documentation\/PlaybackSDK"}}}
{"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/PlaybackSDK\/documentation\/PlaybackSDK"},"sections":[],"metadata":{"role":"collection","modules":[{"name":"PlaybackSDK"}],"roleHeading":"Framework","externalID":"PlaybackSDK","symbolKind":"module","title":"PlaybackSDK"},"schemaVersion":{"patch":0,"major":0,"minor":3},"hierarchy":{"paths":[[]]},"kind":"symbol","variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/playbacksdk"]}],"references":{"doc://PlaybackSDK/documentation/PlaybackSDK":{"title":"PlaybackSDK","identifier":"doc:\/\/PlaybackSDK\/documentation\/PlaybackSDK","kind":"symbol","url":"\/documentation\/playbacksdk","abstract":[],"role":"collection","type":"topic"}}}
Loading