diff --git a/.github/workflows/ios-tests.yml b/.github/workflows/ios-tests.yml new file mode 100644 index 00000000..ff8882be --- /dev/null +++ b/.github/workflows/ios-tests.yml @@ -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 sdk_developers@cloudinary.com of failure" diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 354fb569..00000000 --- a/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -language: objective-c -os: osx -before_script: > - export CLOUDINARY_URL=$(bash tools/get_test_cloud.sh); - echo cloud_name: "$(echo $CLOUDINARY_URL | cut -d'@' -f2)" -notifications: - email: - recipients: - - sdk_developers@cloudinary.com -jobs: - - osx_image: xcode12 - xcode_workspace: Example/Cloudinary.xcworkspace - xcode_scheme: travis_public_scheme - podfile: Example/Podfile - install: pod install --project-directory=Example - env: CLOUDINARY_URL=$CLOUDINARY_URL - xcode_destination: platform=iOS Simulator,OS=13.0,name=iPhone 8 - - osx_image: xcode12 - xcode_workspace: Example/Cloudinary.xcworkspace - xcode_scheme: travis_public_scheme - podfile: Example/Podfile - install: pod install --project-directory=Example - env: CLOUDINARY_URL=$CLOUDINARY_URL - xcode_destination: platform=iOS Simulator,OS=14.0,name=iPhone 8 - - osx_image: xcode13.2 - xcode_workspace: Example/Cloudinary.xcworkspace - xcode_scheme: travis_public_scheme - podfile: Example/Podfile - install: pod install --project-directory=Example - env: CLOUDINARY_URL=$CLOUDINARY_URL - xcode_destination: platform=iOS Simulator,OS=15.2,name=iPhone 8 diff --git a/Cloudinary/Classes/ios/Video/CLDVideoPlayer.swift b/Cloudinary/Classes/ios/Video/CLDVideoPlayer.swift index e94a5b29..f26a6873 100644 --- a/Cloudinary/Classes/ios/Video/CLDVideoPlayer.swift +++ b/Cloudinary/Classes/ios/Video/CLDVideoPlayer.swift @@ -43,6 +43,8 @@ import AVKit var providedData: [String: Any]? + + override init() { super.init() setAnalyticsObservers() @@ -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() { @@ -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: @@ -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 @@ -256,7 +259,7 @@ extension CLDVideoPlayer { } } - public func setProvidedData(data: [String: Any]) { + func setProvidedData(data: [String: Any]) { providedData = data } }