From 78fcd23c9cf790de4b907e2941730b82fd6ae20c Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Tue, 25 Aug 2026 18:47:21 +0530 Subject: [PATCH] Add SwiftUI player example app --- .gitignore | 5 + .../project.pbxproj | 231 ++++++++++++++++++ .../xcshareddata/swiftpm/Package.resolved | 24 ++ .../FastPixPlayerExample/ContentView.swift | 64 +++++ .../FastPixPlayerExampleApp.swift | 10 + Examples/SwiftUI/README.md | 40 +++ 6 files changed, 374 insertions(+) create mode 100644 .gitignore create mode 100644 Examples/SwiftUI/FastPixPlayerExample.xcodeproj/project.pbxproj create mode 100644 Examples/SwiftUI/FastPixPlayerExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 Examples/SwiftUI/FastPixPlayerExample/ContentView.swift create mode 100644 Examples/SwiftUI/FastPixPlayerExample/FastPixPlayerExampleApp.swift create mode 100644 Examples/SwiftUI/README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dd16e31 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +.build/ +DerivedData/ +*.xcuserstate +xcuserdata/ diff --git a/Examples/SwiftUI/FastPixPlayerExample.xcodeproj/project.pbxproj b/Examples/SwiftUI/FastPixPlayerExample.xcodeproj/project.pbxproj new file mode 100644 index 0000000..aaf917c --- /dev/null +++ b/Examples/SwiftUI/FastPixPlayerExample.xcodeproj/project.pbxproj @@ -0,0 +1,231 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + A0000010 /* FastPixPlayerSDK in Frameworks */ = {isa = PBXBuildFile; productRef = A0000009 /* FastPixPlayerSDK */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + A0000006 /* FastPixPlayerExample */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = FastPixPlayerExample; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + A0000003 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A0000010 /* FastPixPlayerSDK in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + A0000000 = { + isa = PBXGroup; + children = ( + A0000006 /* FastPixPlayerExample */, + A0000007 /* Products */, + ); + sourceTree = ""; + }; + A0000007 /* Products */ = { + isa = PBXGroup; + children = ( + A0000005 /* FastPixPlayerExample.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + A0000004 /* FastPixPlayerExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = A000000C /* Build configuration list for PBXNativeTarget "FastPixPlayerExample" */; + buildPhases = ( + A0000002 /* Sources */, + A0000003 /* Frameworks */, + A0000001 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + A0000006 /* FastPixPlayerExample */, + ); + name = FastPixPlayerExample; + packageProductDependencies = ( + A0000009 /* FastPixPlayerSDK */, + ); + productName = FastPixPlayerExample; + productReference = A0000005 /* FastPixPlayerExample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + A0000008 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1700; + LastUpgradeCheck = 1700; + TargetAttributes = { + A0000004 = { + CreatedOnToolsVersion = 17.0; + }; + }; + }; + buildConfigurationList = A000000B /* Build configuration list for PBXProject "FastPixPlayerExample" */; + compatibilityVersion = "Xcode 15.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = A0000000; + minimizedProjectReferenceProxies = 1; + packageReferences = ( + A0000000A /* XCLocalSwiftPackageReference "../.." */, + ); + productRefGroup = A0000007 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + A0000004 /* FastPixPlayerExample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + A0000001 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + A0000002 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + A000000D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + A000000E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + }; + name = Release; + }; + A000000F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.fastpix.FastPixPlayerExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + A0000011 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.fastpix.FastPixPlayerExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + A000000B /* Build configuration list for PBXProject "FastPixPlayerExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A000000D /* Debug */, + A000000E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A000000C /* Build configuration list for PBXNativeTarget "FastPixPlayerExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A000000F /* Debug */, + A0000011 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + A0000000A /* XCLocalSwiftPackageReference "../.." */ = { + isa = XCLocalSwiftPackageReference; + relativePath = "../.."; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + A0000009 /* FastPixPlayerSDK */ = { + isa = XCSwiftPackageProductDependency; + productName = FastPixPlayerSDK; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = A0000008 /* Project object */; +} diff --git a/Examples/SwiftUI/FastPixPlayerExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Examples/SwiftUI/FastPixPlayerExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..2be5afe --- /dev/null +++ b/Examples/SwiftUI/FastPixPlayerExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,24 @@ +{ + "originHash" : "987f2daf268d0018a5e2d701baa2a6cdddfa6000924140a767f6077a1bf4706d", + "pins" : [ + { + "identity" : "ios-core-data-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/FastPix/iOS-core-data-sdk.git", + "state" : { + "revision" : "1c349f3095ebd15d6fa221bc8f4d0456a95f7b1f", + "version" : "1.0.6" + } + }, + { + "identity" : "ios-data-avplayer-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/FastPix/iOS-data-avplayer-sdk", + "state" : { + "revision" : "1eab915f7140c90ec80dafd59313d304c6c9fbde", + "version" : "1.0.7" + } + } + ], + "version" : 3 +} diff --git a/Examples/SwiftUI/FastPixPlayerExample/ContentView.swift b/Examples/SwiftUI/FastPixPlayerExample/ContentView.swift new file mode 100644 index 0000000..ae68fa7 --- /dev/null +++ b/Examples/SwiftUI/FastPixPlayerExample/ContentView.swift @@ -0,0 +1,64 @@ +import SwiftUI +import AVKit +import FastPixPlayerSDK + +// MARK: - Configuration +// Replace these placeholders with your own values before running. +enum FastPixConfig { + /// Your FastPix workspace key. Find it in the FastPix dashboard under + /// Workspaces (https://dashboard.fastpix.io). A placeholder key still + /// wires the analytics pipeline — the backend just rejects its beacons. + static let workspaceKey = "WORKSPACE_KEY" + + /// The playback ID of the video to play, from your FastPix dashboard. + /// Defaults to a public FastPix demo asset so the example runs as-is. + static let playbackID = "7c8d5087-edf7-462f-a1b3-e2fbd30747fa" +} + +// MARK: - Player + SDK owner +// SwiftUI View structs are recreated on every render, so the player and the +// FastPix Data SDK attached to it must live in an ObservableObject that +// survives re-renders (held as @StateObject by the View). +final class PlayerModel: ObservableObject { + let controller = AVPlayerViewController() + + init() { + // Attach FastPix analytics before preparing playback. + controller.enableAnalytics(metadata: [ + "workspace_id": FastPixConfig.workspaceKey, + "video_title": "FastPix SwiftUI Example", + "viewer_id": "swiftui-demo-user", + "video_stream_type": "on-demand", + ]) + controller.prepare( + playbackID: FastPixConfig.playbackID, + playbackOptions: PlaybackOptions(streamType: "on-demand") + ) + } + + func play() { controller.play() } + func pause() { controller.pause() } + + deinit { + // Releasing the controller tears down the player and detaches the SDK. + controller.pause() + } +} + +// MARK: - SwiftUI bridge for AVPlayerViewController +struct FastPixPlayerView: UIViewControllerRepresentable { + let controller: AVPlayerViewController + func makeUIViewController(context: Context) -> AVPlayerViewController { controller } + func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) {} +} + +struct ContentView: View { + @StateObject private var model = PlayerModel() + + var body: some View { + FastPixPlayerView(controller: model.controller) + .ignoresSafeArea() + .onAppear { model.play() } + .onDisappear { model.pause() } // teardown on disappear + } +} diff --git a/Examples/SwiftUI/FastPixPlayerExample/FastPixPlayerExampleApp.swift b/Examples/SwiftUI/FastPixPlayerExample/FastPixPlayerExampleApp.swift new file mode 100644 index 0000000..1601e91 --- /dev/null +++ b/Examples/SwiftUI/FastPixPlayerExample/FastPixPlayerExampleApp.swift @@ -0,0 +1,10 @@ +import SwiftUI + +@main +struct FastPixPlayerExampleApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/Examples/SwiftUI/README.md b/Examples/SwiftUI/README.md new file mode 100644 index 0000000..f4434f0 --- /dev/null +++ b/Examples/SwiftUI/README.md @@ -0,0 +1,40 @@ +# FastPix iOS Player — SwiftUI Example + +A minimal, self-contained SwiftUI app that plays a video with the FastPix iOS +Player and attaches the FastPix Data (analytics) SDK. Copy this folder into +your own project, or open it as-is. + +## Run it + +1. Open `FastPixPlayerExample.xcodeproj` in Xcode. +2. Select an iOS Simulator (or your device) and press **Run**. + +The project references the `FastPixPlayerSDK` package from this repository via a +local package reference (`../..`), so no extra setup is needed. + +## Configure + +Both values live at the top of [`ContentView.swift`](FastPixPlayerExample/ContentView.swift): + +```swift +enum FastPixConfig { + static let workspaceKey = "WORKSPACE_KEY" // ← replace + static let playbackID = "7c8d5087-edf7-462f-a1b3-e2fbd30747fa" // demo asset +} +``` + +- **`workspaceKey`** — get yours from the [FastPix dashboard](https://dashboard.fastpix.io) + under Workspaces. The bundled placeholder still exercises the analytics + pipeline; the backend simply rejects its beacons until you set a real key. +- **`playbackID`** — defaults to a public FastPix demo video so the example + runs unchanged. Replace it with your own playback ID. + +## How it works + +- `PlayerModel` (an `ObservableObject`) owns the `AVPlayerViewController` so the + player and the attached SDK survive SwiftUI re-renders. +- `enableAnalytics(metadata:)` attaches the Data SDK **before** `prepare(...)`. +- `FastPixPlayerView` bridges the controller into SwiftUI via + `UIViewControllerRepresentable`. +- Playback starts in `.onAppear` and is torn down in `.onDisappear` / the + model's `deinit`.