Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions .github/workflows/ios-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
name: iOS CI

on: [push, pull_request]

env:
CLOUDINARY_URL: ${{ secrets.CLOUDINARY_URL }}

jobs:
build:
name: Xcode ${{ matrix.xcode_version }} - iOS ${{ matrix.os_version }}
runs-on: macos-latest

strategy:
matrix:
include:
- xcode_version: '16.2'
ios_name: 'iPhone 16'
os_version: '18.4'

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Ruby (for CocoaPods)
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.2

- name: Setup Xcode ${{ matrix.xcode_version }}
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ matrix.xcode_version }}

- name: Install CocoaPods
run: sudo gem install cocoapods

- name: Install xcpretty
run: sudo gem install xcpretty

- name: Setup CLOUDINARY_URL
run: |
echo "CLOUDINARY_URL=$(bash tools/get_test_cloud.sh)" >> $GITHUB_ENV
echo "cloud_name: $(echo $CLOUDINARY_URL | cut -d'@' -f2)"

- name: Clean Derived Data
run: rm -rf ~/Library/Developer/Xcode/DerivedData

- name: Debug directory structure
run: |
echo "Contents of root directory:"
ls -la
echo "Contents of Example directory:"
ls -la Example/
echo "Looking for workspace files:"
find . -name "*.xcworkspace" -type d

- name: List available schemes
working-directory: Example
run: |
echo "Current directory contents:"
ls -la
echo "Available schemes and destinations:"
if [ -d "Cloudinary.xcworkspace" ]; then
xcodebuild -workspace Cloudinary.xcworkspace -list
else
echo "Cloudinary.xcworkspace not found, trying alternative paths..."
find . -name "*.xcworkspace" -type d
fi

- name: Install Pods
working-directory: Example
run: pod install

- name: Check all scheme destinations
working-directory: Example
run: |
echo "Getting list of all schemes first..."
SCHEMES=$(xcodebuild -workspace Cloudinary.xcworkspace -list | grep -A 100 "Schemes:" | grep "^ " | sed 's/^ //')
echo "Found schemes: $SCHEMES"

for scheme in $SCHEMES; do
echo "=== Checking destinations for scheme: $scheme ==="
xcodebuild -workspace Cloudinary.xcworkspace -scheme "$scheme" -showdestinations 2>/dev/null || echo "Could not get destinations for $scheme"
echo ""
done

- name: Build and Test (Mac Catalyst)
run: |
xcodebuild test \
-workspace Example/Cloudinary.xcworkspace \
-scheme travis_public_scheme \
-destination "platform=macOS,variant=Mac Catalyst" \
CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty

- name: Test Results (Success)
if: success()
run: |
echo "✅ Tests PASSED using Xcode 16.2!"
echo "✅ Successfully running iOS code via Mac Catalyst"

- name: Test Results (Failure)
if: failure()
run: |
echo "❌ Tests FAILED - iOS 18.2 requirement issue persists"
echo "🔧 ROOT CAUSE: Xcode project requires iOS 18.2, but GitHub Actions only has iOS 18.4+"
echo "💡 SOLUTION: Update Xcode project deployment targets from iOS 18.2 → iOS 18.4+"

- name: Notify on Failure
if: failure()
run: echo "Notify [email protected] of failure"
31 changes: 0 additions & 31 deletions .travis.yml

This file was deleted.

35 changes: 19 additions & 16 deletions Cloudinary/Classes/ios/Video/CLDVideoPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ import AVKit

var providedData: [String: Any]?



override init() {
super.init()
setAnalyticsObservers()
Expand Down Expand Up @@ -128,22 +130,13 @@ import AVKit
}
}

public func flushEvents() {
guard analytics else { return }
eventsManager.sendEvents()
}

public func flushEventsAndCloseSession() {
guard analytics else { return }
eventsManager.sendViewEndEvent(providedData: providedData)
flushEvents()
}

deinit {
guard analytics else { return }
removeObserver(self, forKeyPath: PlayerKeyPath.status.rawValue)
removeObserver(self, forKeyPath: PlayerKeyPath.timeControlStatus.rawValue)
flushEventsAndCloseSession()
if analytics {
removeObserver(self, forKeyPath: PlayerKeyPath.status.rawValue)
removeObserver(self, forKeyPath: PlayerKeyPath.timeControlStatus.rawValue)
eventsManager.sendViewEndEvent(providedData: providedData)
eventsManager.sendEvents()
}
}

func setAnalyticsObservers() {
Expand Down Expand Up @@ -178,6 +171,15 @@ import AVKit

@available(iOS 10.0, *)
extension CLDVideoPlayer {
func observeDuration(of playerItem: AVPlayerItem) {
let duration = playerItem.asset.duration

let durationInSeconds = Int(CMTimeGetSeconds(duration))
if !loadMetadataSent {
loadMetadataSent = true
self.eventsManager.sendLoadMetadataEvent(duration: durationInSeconds)
}
}
func handleStatusChanged(_ status: AVPlayer.Status) {
switch status {
case .readyToPlay:
Expand All @@ -186,6 +188,7 @@ extension CLDVideoPlayer {
eventsManager.sendViewStartEvent(videoUrl: mediaURL.absoluteString, providedData: providedData)
isIntialized = true

// Load duration asynchronously - more reliable on iOS 18+
loadDurationAsynchronously()
}
break
Expand Down Expand Up @@ -256,7 +259,7 @@ extension CLDVideoPlayer {
}
}

public func setProvidedData(data: [String: Any]) {
func setProvidedData(data: [String: Any]) {
providedData = data
}
}