Skip to content

Conversation

@amirXenoss
Copy link
Collaborator

No description provided.

@amirXenoss amirXenoss requested a review from steffanc November 13, 2025 22:30
@amirXenoss amirXenoss self-assigned this Nov 13, 2025
amirXenoss and others added 2 commits November 14, 2025 03:58
- Remove duplicate bidder log line from onAdLoaded callbacks
- Add placementName, placementId, and externalPlacementId to logs
- Rename "Bidder" to "Bidder/Network" for clarity
- Apply changes to banner, MREC, and interstitial screens

This provides complete ad metadata visibility for testing and debugging.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@steffanc steffanc requested a review from Copilot November 20, 2025 00:42
Copilot finished reviewing on behalf of steffanc November 20, 2025 00:42
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces a comprehensive testing application for the CloudX Flutter SDK wrapper validation. The main purpose is to provide a structured test environment to verify that the Flutter wrapper correctly integrates with the native CloudX SDK on both iOS and Android platforms.

Key changes:

  • Adds a new cloudx-flutter-testing directory with a complete Flutter test application
  • Updates the demo app's CloudX Flutter dependency from ^0.17.0 to ^0.18.0
  • Implements a clean architecture approach with proper separation of concerns for testing

Reviewed Changes

Copilot reviewed 51 out of 77 changed files in this pull request and generated no comments.

Show a summary per file
File Description
cloudx_flutter_demo_app/pubspec.yaml Updates CloudX Flutter SDK dependency to version 0.18.0
cloudx-flutter-testing/pubspec.yaml Defines dependencies for the new testing app with local SDK path
cloudx-flutter-testing/test/widget_test.dart Adds a basic widget test for counter functionality
cloudx-flutter-testing/lib/main.dart Implements main app entry point with tab-based navigation
cloudx-flutter-testing/lib/services/sdk_manager.dart Creates SDK initialization and management service
cloudx-flutter-testing/lib/screens/*.dart Implements screens for settings, initialization, and ad testing
cloudx-flutter-testing/ios/* Adds complete iOS project configuration
cloudx-flutter-testing/android/* Adds complete Android project configuration
cloudx-flutter-testing/README.md Provides comprehensive documentation for the testing app
cloudx-flutter-testing/ARCHITECTURE.md Describes the clean architecture approach and testing strategy
README.md Updates root README to reference the new testing app
Files not reviewed (2)
  • cloudx-flutter-testing/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: Language not supported
  • cloudx-flutter-testing/ios/Runner.xcworkspace/contents.xcworkspacedata: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@override
void dispose() {
if (_adId != null) {
CloudX.destroyAd(adId: _adId!);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

destroyAd() is async but not awaited. Ad may not be properly cleaned up before widget disposal.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't use await in dispose

Comment on lines +13 to +15
# cloudx_flutter: ^0.16.0 // latest prod
cloudx_flutter: # local
path: ../cloudx_flutter_sdk
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use some switching mechanism?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need that switched, it is for pubs, we won't be working on this flutter demo app much. Thinking that would be overkill.

@steffanc
Copy link
Contributor

Code Review: PR #15 - CloudX Flutter Testing App

Thanks for this contribution! I've completed a comprehensive review. The testing app is functional and demonstrates CloudX SDK integration well, but there are several issues that need to be addressed before merging.


🚨 Critical Issues (Must Fix Before Merge)

1. Package Name Conflict

Location: cloudx-flutter-testing/pubspec.yaml:1

The package is named cloudx_flutter_demo_app which conflicts with the existing demo app. This will cause build conflicts and import errors.

Fix Required:

  • Rename to cloudx_flutter_testing in:
    • pubspec.yaml:1
    • test/widget_test.dart:11
    • ios/Runner/Info.plist:18

2. Memory Leaks - Missing destroyAd() Calls

Location:

  • lib/screens/banner_screen.dart:37-40
  • lib/screens/mrec_screen.dart:37-40

The dispose() methods only call _controller.dispose() but never call CloudX.destroyAd(adId). According to CLAUDE.md:

"Always call CloudX.destroyAd(adId) in Widget dispose() to prevent memory leaks and orphaned timers"

Impact: Memory leaks, orphaned auto-refresh timers, resource exhaustion in long-running test sessions.

Fix Required:

@override
void dispose() {
  // Need to destroy the underlying ad instance
  _controller.stopAutoRefresh(); // Stop timers
  CloudX.destroyAd(_adId); // Clean up native resources
  _controller.dispose();
  _scrollController.dispose();
  super.dispose();
}

Note: This may reveal a gap in the SDK wrapper - widget-based ads may not expose a proper destroy mechanism via controllers.

3. Hardcoded Production App Keys

Location:

  • lib/screens/settings_screen.dart:38-47
  • lib/services/sdk_manager.dart:42-43

Production CloudX keys are committed to version control:

static const String _defaultIosAppKey = 'uOPdnQC_zu0gJs8HP3cBs';     // Production!
static const String _defaultAndroidAppKey = '69TdNnN1EcNpeyWWkLhBS'; // Production!

Security Risk: Keys can be scraped, no rotation possible, violates best practices.

Fix Required:

static const String _defaultIosAppKey = 'YOUR_IOS_APP_KEY_HERE';
static const String _defaultAndroidAppKey = 'YOUR_ANDROID_APP_KEY_HERE';

Add documentation on where to obtain real keys for testing.

4. iOS Podfile Uses Non-Portable Local Paths

Location: ios/Podfile:36-40

pod 'CloudXCore', :path => '../../cloudx-ios-private/core/', :inhibit_warnings => true
pod 'CloudXMetaAdapter', :path => '../../cloudx-ios-private/adapter-meta/', :inhibit_warnings => true

Impact: Won't build for other developers, blocks CI/CD, prevents external contributions.

Fix Required: Remove these local path overrides. The Flutter plugin dependency should pull the correct published SDK version from CocoaPods.


⚠️ Important Issues (Should Fix)

5. Boilerplate Test File

Location: test/widget_test.dart:14-29

Contains default Flutter counter test that doesn't match the actual app - will always fail.

Fix: Replace with actual smoke test or remove.

6. Architecture Documentation Misalignment

Location: ARCHITECTURE.md (entire file)

The document extensively describes a clean architecture with:

  • Dependency injection (GetIt)
  • Service layer abstractions
  • Repository pattern
  • State management with ChangeNotifier
  • Domain layer with use cases

Reality: The code uses direct instantiation, no DI, simple StatefulWidget state management.

Impact:

  • Developer confusion
  • False expectations about testability
  • Documented testing strategy (lines 351-542) cannot be executed
  • Violates stated architecture goals

Fix: Either:

  1. Simplify ARCHITECTURE.md to match the actual straightforward implementation, OR
  2. Add clear "NOT YET IMPLEMENTED" disclaimer at the top

7. Auto-Refresh State Bug

Location:

  • lib/screens/banner_screen.dart:203-207
  • lib/screens/mrec_screen.dart:203-209

The _autoRefreshEnabled state is reset to true when stopping the banner, causing UI/SDK state desynchronization.

Fix: Don't reset auto-refresh state when stopping banner.

8. Missing Error Handling in Privacy Settings

Location: lib/services/sdk_manager.dart:109-142

Privacy API calls have no try-catch blocks - failures are silent.

Fix: Wrap each privacy setting call in try-catch with error logging.

9. Platform-Based TextField Disabling

Location: lib/screens/settings_screen.dart:146,161,167...

iOS fields are disabled on Android and vice versa via enabled: Platform.isIOS.

Impact: Can't view/copy settings, can't prepare configurations for both platforms from one device.

Fix: Keep all fields enabled, use visual indicators (badges, colors) to show which platform is active.


💡 Suggestions (Nice to Have)

  • Inconsistent logging format: Mix of ▶️ TEST: and emoji-only prefixes
  • README references: Still mentions cloudx_flutter_demo_app paths instead of cloudx-flutter-testing
  • Testing methodology docs: Missing how-to-test guide and Charles Proxy setup instructions
  • Placement naming: iOS has iOS suffix, Android doesn't - be consistent
  • Magic strings: Privacy strings like '1YNN' need comments explaining what they represent
  • SDK version validation: Add version logging to ensure correct SDK is being tested

✅ Positive Observations

  • Excellent callback logging: Great debugging insight with CLXAd metadata display
  • Clean auto-refresh control: Demonstrates proper SDK usage
  • Good platform-specific config: Flexible testing across platforms with SharedPreferences
  • Proper interstitial lifecycle: Correctly checks isInterstitialReady(), destroys after hidden
  • Comprehensive privacy coverage: Tests CCPA, COPPA, GPP, and user targeting APIs
  • Clear visual indicators: Good UX with state feedback via buttons and navigation

Overall Assessment

Status:Not Ready to Merge

This is a functional manual testing tool, but has 4 critical blockers that must be fixed:

  1. Package name conflict
  2. Memory leaks (missing destroyAd)
  3. Hardcoded production credentials
  4. Non-portable iOS dependencies

The most concerning issue is the memory leak - banner and MREC ads are never properly destroyed, which will cause resource exhaustion in long-running test sessions.

Recommendation: Fix all critical issues before merging. Also consider whether to implement the documented clean architecture or update ARCHITECTURE.md to reflect the simpler implementation actually used.

Happy to discuss any of these findings! 🚀

@amirXenoss
Copy link
Collaborator Author

  1. Done
  2. I checked, the timers are destroyed automatically on the SDK side, we don't need to call them on the Flutter side's. On the native side, we are cancelling the scope in BannerManager which is responsible for refresh logic.

@amirXenoss
Copy link
Collaborator Author

  1. I moved them to AppConstants. When it comes to the actual keys, we should leave them, I don't think there is going to be an issue or security risk. They are demo appKey and placements.

@amirXenoss
Copy link
Collaborator Author

  1. Done

@amirXenoss
Copy link
Collaborator Author

5-6: removed

@amirXenoss
Copy link
Collaborator Author

  1. I believe Claude didn't have to full context of whats happening in the native sdk side. We are resetting the auto refresh wevery time we create a new banner.

@amirXenoss
Copy link
Collaborator Author

  1. I don't think there is something that could crash when we call those privacy methods. They are just setting those values to the native side and that's it. Nothing major is happening in there. No need for try catch.

@amirXenoss
Copy link
Collaborator Author

  1. overkill, no need :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants