Skip to content

[#53] Support Compose Multiplatform#67

Merged
l2hyunwoo merged 30 commits intomainfrom
develop/cmp
Aug 2, 2025
Merged

[#53] Support Compose Multiplatform#67
l2hyunwoo merged 30 commits intomainfrom
develop/cmp

Conversation

@l2hyunwoo
Copy link
Copy Markdown
Collaborator

@l2hyunwoo l2hyunwoo commented Jul 6, 2025

🎯 Goal

🛠 Implementation details

✍️ Explain examples

Explain examples with code for this updates.

Preparing a pull request for review

Ensure your change is properly formatted by running:

$ ./gradlew spotlessApply

Then dump binary API of this library that is public in sense of Kotlin visibilities and ensures that the public binary API wasn't changed in a way that make this change binary incompatible.

./gradlew apiDump

Please correct any failures before requesting a review.

Code reviews

All submissions, including submissions by project members, require review. We use GitHub pull requests for this purpose. Consult GitHub Help for more information on using pull requests.

Summary by CodeRabbit

  • New Features

    • Introduced a cross-platform blur modifier for Compose UI with platform-specific implementations for Android and iOS.
    • Added platform-agnostic bitmap abstraction and state management for blur operations.
    • Enabled dynamic CPU architecture detection and multiplatform build configuration.
    • Added Kotlin Multiplatform and Compose Multiplatform support in app and library modules.
    • Integrated iOS Compose UI with native blur effect and image bitmap conversions.
    • Added UUID generation implementations for Android and iOS platforms.
    • Added comprehensive unit tests for blur states and platform bitmap abstractions.
    • Added iOS Xcode project and SwiftUI integration files for Compose UI app.
  • Improvements

    • Enhanced documentation and error handling for bitmap processing and blur logic.
    • Improved resource management and type safety in bitmap handling.
    • Added detailed KDoc comments clarifying internal bitmap and blur processing.
    • Improved Android blur modifier with platform-agnostic bitmap caching and disposal.
    • Switched image loading in demo app from Glide to Coil with updated theming.
  • Refactor

    • Migrated project structure to Kotlin Multiplatform, reorganizing source sets and dependencies.
    • Replaced platform-specific bitmap types with unified multiplatform abstractions.
    • Refactored build scripts to fully adopt Kotlin Multiplatform and Compose Multiplatform.
    • Removed deprecated composable UI components and layouts from demo app.
    • Updated demo app package structure and internalized visibility for models and themes.
  • Chores

    • Updated build scripts and dependencies to support Compose Multiplatform.
    • Adjusted ignore rules for Kotlin-related files.
    • Removed Maven publish plugin application from publishing script.
    • Added new dependency versions and plugins for multiplatform and testing support.
  • Bug Fixes

    • Corrected blur radius calculation logic for iterative blur passes.

@l2hyunwoo l2hyunwoo self-assigned this Jul 6, 2025
@l2hyunwoo l2hyunwoo requested a review from skydoves as a code owner July 6, 2025 06:48
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jul 6, 2025

Warning

Rate limit exceeded

@l2hyunwoo has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 13 minutes and 17 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 27a9cff and c723a17.

📒 Files selected for processing (2)
  • iosApp/iosApp.xcodeproj/xcuserdata/hyunwoo.xcuserdatad/xcschemes/iosApp.xcscheme (1 hunks)
  • iosApp/iosApp.xcodeproj/xcuserdata/hyunwoo.xcuserdatad/xcschemes/xcschememanagement.plist (1 hunks)

Walkthrough

This update migrates the project to Kotlin Multiplatform, introducing shared code for Android and iOS, platform-agnostic bitmap abstractions, and multiplatform Compose UI support. It restructures build scripts, adds platform-specific implementations, and updates dependency management to enable Compose Multiplatform development and cross-platform blur functionality.

Changes

File(s) Change Summary
.gitignore, gradle/libs.versions.toml, build.gradle.kts, scripts/publish-module.gradle.kts Updated ignore rules for Kotlin, added Compose Multiplatform/Kotlin Multiplatform plugins and libraries, updated dependency versions, and removed Maven publish plugin application.
cloudy/build.gradle.kts, buildSrc/src/main/kotlin/com/skydoves/cloudy/ActiveArch.kt Migrated to Kotlin Multiplatform with dynamic iOS/Android target selection, multiplatform source sets, platform-specific dependencies; added architecture detection utility.
cloudy/api/cloudy.api, cloudy/src/main/kotlin/com/skydoves/cloudy/CloudyState.kt Refactored and removed old Android-only API/state; introduced multiplatform state management and platform-agnostic bitmap handling in API.
cloudy/src/commonMain/kotlin/com/skydoves/cloudy/Cloudy.kt, CloudyState.kt, PlatformBitmap.kt Added multiplatform expect declarations for blur modifier, state, and bitmap abstraction.
cloudy/src/androidMain/kotlin/com/skydoves/cloudy/Cloudy.android.kt, PlatformBitmap.android.kt Implemented Android-specific blur modifier and platform bitmap class with resource management and conversions; improved type safety and platform abstraction.
cloudy/src/androidMain/kotlin/com/skydoves/cloudy/internals/render/RenderScriptToolkit.kt Added detailed documentation and corrected blur pass logic in internal rendering utilities.
cloudy/src/iosMain/kotlin/com/skydoves/cloudy/Cloudy.ios.kt, PlatformBitmap.ios.kt Introduced iOS-specific blur modifier and platform bitmap class with Core Image processing, bitmap conversions, and asynchronous blur handling.
cloudy/src/androidUnitTest/kotlin/com/skydoves/cloudy/CloudyStateTest.kt, PlatformBitmapTest.kt Added unit tests for CloudyState and PlatformBitmap on Android, verifying state correctness, bitmap properties, and lifecycle.
cloudy/src/iosTest/kotlin/com/skydoves/cloudy/CloudyStateTest.kt, PlatformBitmapTest.kt, TestUtils.kt Added unit tests and utility functions for CloudyState and PlatformBitmap on iOS, verifying state correctness, bitmap properties, and test image creation.
app/build.gradle.kts, app/src/commonMain/kotlin/demo/*, app/src/androidMain/kotlin/demo/*, app/src/iosMain/kotlin/demo/* Migrated app module to Kotlin Multiplatform with Compose Multiplatform support; refactored package names, theming, image loading to Coil, and UUID generation for multiplatform; added iOS entry point and view controller integration.
app/src/main/kotlin/com/skydoves/cloudydemo/* (posters, theme, custom) Removed multiple UI components and theme typography files related to posters and staggered grid layout, simplifying the app's UI layer.
iosApp/iosApp.xcodeproj/project.pbxproj, iosApp/iosApp/* Added new iOS app Xcode project files, assets, Info.plist, and SwiftUI integration to host the Compose Multiplatform UI.

Sequence Diagram(s)

sequenceDiagram
    participant UI as Composable UI
    participant Modifier as Modifier.cloudy (common)
    participant Platform as Platform Impl (Android/iOS)
    participant State as CloudyState

    UI->>Modifier: Apply cloudy(radius, enabled, onStateChanged)
    Modifier->>Platform: Delegate to platform-specific implementation
    Platform->>Platform: Capture composable content as bitmap
    Platform->>Platform: Apply blur (native algorithm/Core Image)
    Platform->>State: Update CloudyState (Loading → Success/Error)
    State-->>UI: Notify onStateChanged callback
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Assessment against linked issues

Objective (Issue #) Addressed Explanation
Support Compose Multiplatform (Will this support Compose Multiplatform?) (#53)

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes detected. All changes align with the linked issue objectives.

Possibly related PRs

Suggested reviewers

  • skydoves

Poem

A rabbit hopped from Android land,
To iOS fields, all carefully planned.
Bitmaps now travel, platform-free,
With Compose Multiplatform for all to see.
Blur effects shimmer on every device,
Multiplatform magic—oh, isn't it nice?
🐇✨

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch develop/cmp

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@l2hyunwoo l2hyunwoo marked this pull request as draft July 6, 2025 06:48
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (5)
cloudy/src/commonMain/kotlin/com/skydoves/cloudy/Cloudy.kt (1)

22-37: Remove duplicate KDoc comments.

There are two sets of documentation comments describing the same function. Keep only one comprehensive version.

-/**
- * `Modifier.cloudy()` is a blur modifier that applies blur effects to composables,
- * compatible with all supported platforms.
- *
- * @param radius Radius of the blur along both the x and y axis. Must be non-negative.
- *               On Android, values > 25 are achieved through iterative passes which may affect performance.
- * @param enabled Enabling the blur effects.
- * @param onStateChanged Lambda function that will be invoked when the blur process has been updated.
- */
 /**
  * Applies a cross-platform blur effect to the current modifier.
  *
  * @param radius The blur radius in pixels for both the x and y axes. Must be non-negative. On Android, values above 25 may impact performance due to iterative passes.
  * @param enabled If false, disables the blur effect.
  * @param onStateChanged Callback invoked when the blur state changes.
  * @return A [Modifier] with the blur effect applied.
  */
cloudy/src/commonMain/kotlin/com/skydoves/cloudy/CloudyState.kt (1)

58-58: Add public visibility modifier for consistency.

The throwable property should have an explicit public modifier to maintain consistency with the rest of the public API declarations in this file.

-  public data class Error(val throwable: Throwable) : CloudyState
+  public data class Error(public val throwable: Throwable) : CloudyState
cloudy/src/androidMain/kotlin/com/skydoves/cloudy/Cloudy.android.kt (1)

39-51: Remove redundant documentation block.

This documentation block is redundant with the properly formatted one below (lines 52-61). The extra asterisks also cause formatting issues.

-/**
- * Android implementation of the cloudy modifier that applies blur effects to composables.
- * This is the actual implementation for the expect function declared in commonMain.
- * * For Android 12+ devices in preview mode, it falls back to the platform's blur modifier.
- * For runtime execution, it uses a custom implementation with graphics layers and
- * native iterative blur processing for optimal performance.
- * * The implementation captures the composable content in a graphics layer, applies
- * iterative blur processing using native code, and overlays the result.
- * * @param radius The blur radius in pixels (1-25). Higher values create more blur but take longer to process.
- * @param enabled Whether the blur effect is enabled. When false, returns the original modifier unchanged.
- * @param onStateChanged Callback that receives updates about the blur processing state.
- * @return Modified Modifier with blur effect applied.
- */
cloudy/src/iosMain/kotlin/com/skydoves/cloudy/Cloudy.ios.kt (1)

42-51: Remove redundant documentation block.

Similar to the Android implementation, this documentation block is redundant with the properly formatted one below (lines 52-60).

-/**
- * iOS implementation of the cloudy modifier that applies blur effects to composables.
- * This is the actual implementation for the expect function declared in commonMain.
- * * Uses Core Image filters for blur processing on iOS, with graphics layer content capture
- * to blur the actual composable content instead of placeholder images.
- * * @param radius The blur radius in pixels. Higher values create more blur.
- * @param enabled Whether the blur effect is enabled. When false, returns the original modifier unchanged.
- * @param onStateChanged Callback that receives updates about the blur processing state.
- * @return Modified Modifier with blur effect applied.
- */
cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt (1)

184-187: Consider logging conversion failures for debugging.

While returning placeholder images on failure maintains app stability, consider adding debug logging to help identify conversion issues during development.

   } catch (_: Exception) {
+    // TODO: Add debug logging for conversion failures
     // Fallback to creating a basic placeholder
     createBasicPlaceholderUIImage(this.width, this.height)
   }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 92acaf8 and f395eb1.

📒 Files selected for processing (16)
  • .gitignore (1 hunks)
  • build.gradle.kts (1 hunks)
  • buildSrc/src/main/kotlin/com/skydoves/cloudy/ActiveArch.kt (1 hunks)
  • cloudy/api/cloudy.api (1 hunks)
  • cloudy/build.gradle.kts (1 hunks)
  • cloudy/src/androidMain/kotlin/com/skydoves/cloudy/Cloudy.android.kt (3 hunks)
  • cloudy/src/androidMain/kotlin/com/skydoves/cloudy/PlatformBitmap.android.kt (1 hunks)
  • cloudy/src/androidMain/kotlin/com/skydoves/cloudy/internals/render/RenderScriptToolkit.kt (6 hunks)
  • cloudy/src/commonMain/kotlin/com/skydoves/cloudy/Cloudy.kt (1 hunks)
  • cloudy/src/commonMain/kotlin/com/skydoves/cloudy/CloudyState.kt (1 hunks)
  • cloudy/src/commonMain/kotlin/com/skydoves/cloudy/PlatformBitmap.kt (1 hunks)
  • cloudy/src/iosMain/kotlin/com/skydoves/cloudy/Cloudy.ios.kt (1 hunks)
  • cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt (1 hunks)
  • cloudy/src/main/kotlin/com/skydoves/cloudy/CloudyState.kt (0 hunks)
  • gradle/libs.versions.toml (2 hunks)
  • scripts/publish-module.gradle.kts (0 hunks)
💤 Files with no reviewable changes (2)
  • scripts/publish-module.gradle.kts
  • cloudy/src/main/kotlin/com/skydoves/cloudy/CloudyState.kt
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:12:51.545Z
Learning: User l2hyunwoo prefers to keep PRs focused on their primary objective and avoid scope creep. They acknowledge technical debt items but prefer to address them in separate PRs rather than expanding the current PR scope.
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:13:55.799Z
Learning: In the Cloudy project, the UIImage.asImageBitmap() function in cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt currently uses a placeholder implementation that generates synthetic gradients instead of extracting actual CGImage pixels. This is known technical debt that should be addressed in a future PR focused on pixel conversion optimization.
cloudy/src/commonMain/kotlin/com/skydoves/cloudy/CloudyState.kt (1)
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:13:55.799Z
Learning: In the Cloudy project, the UIImage.asImageBitmap() function in cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt currently uses a placeholder implementation that generates synthetic gradients instead of extracting actual CGImage pixels. This is known technical debt that should be addressed in a future PR focused on pixel conversion optimization.
cloudy/src/iosMain/kotlin/com/skydoves/cloudy/Cloudy.ios.kt (1)
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:13:55.799Z
Learning: In the Cloudy project, the UIImage.asImageBitmap() function in cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt currently uses a placeholder implementation that generates synthetic gradients instead of extracting actual CGImage pixels. This is known technical debt that should be addressed in a future PR focused on pixel conversion optimization.
cloudy/src/androidMain/kotlin/com/skydoves/cloudy/Cloudy.android.kt (1)
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:13:55.799Z
Learning: In the Cloudy project, the UIImage.asImageBitmap() function in cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt currently uses a placeholder implementation that generates synthetic gradients instead of extracting actual CGImage pixels. This is known technical debt that should be addressed in a future PR focused on pixel conversion optimization.
cloudy/src/commonMain/kotlin/com/skydoves/cloudy/Cloudy.kt (1)
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:13:55.799Z
Learning: In the Cloudy project, the UIImage.asImageBitmap() function in cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt currently uses a placeholder implementation that generates synthetic gradients instead of extracting actual CGImage pixels. This is known technical debt that should be addressed in a future PR focused on pixel conversion optimization.
cloudy/src/commonMain/kotlin/com/skydoves/cloudy/PlatformBitmap.kt (1)
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:13:55.799Z
Learning: In the Cloudy project, the UIImage.asImageBitmap() function in cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt currently uses a placeholder implementation that generates synthetic gradients instead of extracting actual CGImage pixels. This is known technical debt that should be addressed in a future PR focused on pixel conversion optimization.
cloudy/src/androidMain/kotlin/com/skydoves/cloudy/PlatformBitmap.android.kt (1)
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:13:55.799Z
Learning: In the Cloudy project, the UIImage.asImageBitmap() function in cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt currently uses a placeholder implementation that generates synthetic gradients instead of extracting actual CGImage pixels. This is known technical debt that should be addressed in a future PR focused on pixel conversion optimization.
cloudy/build.gradle.kts (1)
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:13:55.799Z
Learning: In the Cloudy project, the UIImage.asImageBitmap() function in cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt currently uses a placeholder implementation that generates synthetic gradients instead of extracting actual CGImage pixels. This is known technical debt that should be addressed in a future PR focused on pixel conversion optimization.
cloudy/src/androidMain/kotlin/com/skydoves/cloudy/internals/render/RenderScriptToolkit.kt (1)
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:13:55.799Z
Learning: In the Cloudy project, the UIImage.asImageBitmap() function in cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt currently uses a placeholder implementation that generates synthetic gradients instead of extracting actual CGImage pixels. This is known technical debt that should be addressed in a future PR focused on pixel conversion optimization.
cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt (1)
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:13:55.799Z
Learning: In the Cloudy project, the UIImage.asImageBitmap() function in cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt currently uses a placeholder implementation that generates synthetic gradients instead of extracting actual CGImage pixels. This is known technical debt that should be addressed in a future PR focused on pixel conversion optimization.
cloudy/api/cloudy.api (1)
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:13:55.799Z
Learning: In the Cloudy project, the UIImage.asImageBitmap() function in cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt currently uses a placeholder implementation that generates synthetic gradients instead of extracting actual CGImage pixels. This is known technical debt that should be addressed in a future PR focused on pixel conversion optimization.
🧬 Code Graph Analysis (1)
cloudy/src/androidMain/kotlin/com/skydoves/cloudy/Cloudy.android.kt (1)
cloudy/src/androidMain/kotlin/com/skydoves/cloudy/internals/render/RenderScriptToolkit.kt (1)
  • iterativeBlur (379-406)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: build
  • GitHub Check: API check
  • GitHub Check: Spotless check
🔇 Additional comments (17)
.gitignore (1)

12-13: LGTM! Appropriate addition for multiplatform setup.

Adding the .kotlin/ directory to ignore list is standard practice for Kotlin Multiplatform projects as it contains build artifacts.

build.gradle.kts (1)

8-9: LGTM! Standard multiplatform plugin declarations.

Adding the multiplatform plugin aliases with apply false is the correct approach for root build files, enabling subprojects to apply them as needed.

buildSrc/src/main/kotlin/com/skydoves/cloudy/ActiveArch.kt (2)

6-25: Well-structured architecture detection enum.

The Arch enum and companion object provide clean architecture detection for multiplatform builds. Good use of fallback to ALL when no match is found.


27-35: Robust property reading implementation.

The activeArch extension property handles file existence checks, proper charset encoding, and environment variable fallback appropriately.

cloudy/src/commonMain/kotlin/com/skydoves/cloudy/Cloudy.kt (1)

38-43: Excellent expect function declaration.

The function signature is well-designed with appropriate parameter types, default values, and the @IntRange annotation for radius validation.

cloudy/src/commonMain/kotlin/com/skydoves/cloudy/PlatformBitmap.kt (2)

24-40: Excellent multiplatform bitmap abstraction.

The PlatformBitmap expect class provides a clean abstraction with appropriate immutability annotations and well-defined properties.


42-54: Well-designed extension functions.

The createCompatible() and dispose() extension functions provide clear resource management patterns that will work across platforms.

cloudy/src/androidMain/kotlin/com/skydoves/cloudy/PlatformBitmap.android.kt (1)

21-76: LGTM! Well-implemented platform abstraction.

The Android implementation properly handles bitmap lifecycle management with appropriate null safety checks and recycling logic. The extension functions provide clean conversion APIs.

cloudy/src/androidMain/kotlin/com/skydoves/cloudy/Cloudy.android.kt (1)

117-175: Excellent resource management and platform abstraction.

The implementation properly:

  • Migrates from Android Bitmap to PlatformBitmap abstraction
  • Manages resource lifecycle with dispose() calls
  • Handles state transitions correctly
  • Provides descriptive error messages
cloudy/src/iosMain/kotlin/com/skydoves/cloudy/Cloudy.ios.kt (2)

176-192: Implementation aligns with incremental approach.

The simplified UIImage conversion is acknowledged in the comments. Based on previous discussions, I understand this is intentional technical debt to be addressed in a future PR focused on pixel conversion optimization.


204-225: Clean Core Image implementation.

The Gaussian blur implementation properly uses Core Image APIs with appropriate error handling and null safety checks.

gradle/libs.versions.toml (1)

21-21: Compose Multiplatform version (1.8.2) is up-to-date
Confirmed that 1.8.2 is the latest stable release as of July 2025. No changes required.

cloudy/api/cloudy.api (1)

34-42: API changes align well with multiplatform architecture.

The migration from Android's Bitmap to PlatformBitmap in CloudyState$Success is a clean abstraction that enables cross-platform compatibility.

cloudy/build.gradle.kts (2)

43-59: Dynamic iOS target selection is well-implemented.

The architecture-based target selection provides flexibility for different build environments and reduces unnecessary compilation overhead.


66-77: CMake configuration properly supports modern Android requirements.

The C++17 standard and flexible page sizes flag ensure compatibility with recent Android versions.

cloudy/src/androidMain/kotlin/com/skydoves/cloudy/internals/render/RenderScriptToolkit.kt (1)

379-406: Excellent fix for the iterative blur algorithm.

The corrected logic properly handles blur radius decomposition:

  • Correctly calculates full passes with integer division
  • Applies remainder blur first when needed
  • Eliminates previous off-by-one errors

This ensures accurate blur effects for radii greater than 25.

cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt (1)

201-256: Placeholder implementation acknowledged as known technical debt.

As discussed in previous PRs, this synthetic gradient implementation is a temporary solution that will be replaced with actual pixel extraction in a future focused PR. The current implementation is sufficient for initial multiplatform support.

@l2hyunwoo l2hyunwoo marked this pull request as ready for review August 2, 2025 02:05
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
cloudy/src/iosMain/kotlin/com/skydoves/cloudy/Cloudy.ios.kt (1)

42-60: Remove duplicate documentation comments.

There are two documentation blocks for the same function. Keep only one comprehensive documentation block.

-/**
- * iOS implementation of the cloudy modifier that applies blur effects to composables.
- * This is the actual implementation for the expect function declared in commonMain.
- * * Uses Core Image filters for blur processing on iOS, with graphics layer content capture
- * to blur the actual composable content instead of placeholder images.
- * * @param radius The blur radius in pixels. Higher values create more blur.
- * @param enabled Whether the blur effect is enabled. When false, returns the original modifier unchanged.
- * @param onStateChanged Callback that receives updates about the blur processing state.
- * @return Modified Modifier with blur effect applied.
- */
 /**
  * Applies a Gaussian blur effect to the composable content using Core Image when enabled.
  *
  * @param radius The blur radius in points. Must be non-negative.
  * @param enabled If false, no blur is applied and the modifier is unchanged.
  * @param onStateChanged Callback invoked with the current blur processing state.
  * @return The modifier with the blur effect applied if enabled; otherwise, the original modifier.
  * @throws IllegalArgumentException if radius is negative.
  */
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f395eb1 and 1ec5ec8.

📒 Files selected for processing (21)
  • .gitignore (1 hunks)
  • build.gradle.kts (1 hunks)
  • buildSrc/src/main/kotlin/com/skydoves/cloudy/ActiveArch.kt (1 hunks)
  • cloudy/api/cloudy.api (1 hunks)
  • cloudy/build.gradle.kts (1 hunks)
  • cloudy/src/androidMain/kotlin/com/skydoves/cloudy/Cloudy.android.kt (4 hunks)
  • cloudy/src/androidMain/kotlin/com/skydoves/cloudy/PlatformBitmap.android.kt (1 hunks)
  • cloudy/src/androidMain/kotlin/com/skydoves/cloudy/internals/render/RenderScriptToolkit.kt (6 hunks)
  • cloudy/src/androidUnitTest/kotlin/com/skydoves/cloudy/CloudyStateTest.kt (1 hunks)
  • cloudy/src/androidUnitTest/kotlin/com/skydoves/cloudy/PlatformBitmapTest.kt (1 hunks)
  • cloudy/src/commonMain/kotlin/com/skydoves/cloudy/Cloudy.kt (1 hunks)
  • cloudy/src/commonMain/kotlin/com/skydoves/cloudy/CloudyState.kt (1 hunks)
  • cloudy/src/commonMain/kotlin/com/skydoves/cloudy/PlatformBitmap.kt (1 hunks)
  • cloudy/src/iosMain/kotlin/com/skydoves/cloudy/Cloudy.ios.kt (1 hunks)
  • cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt (1 hunks)
  • cloudy/src/iosTest/kotlin/com/skydoves/cloudy/CloudyStateTest.kt (1 hunks)
  • cloudy/src/iosTest/kotlin/com/skydoves/cloudy/PlatformBitmapTest.kt (1 hunks)
  • cloudy/src/iosTest/kotlin/com/skydoves/cloudy/TestUtils.kt (1 hunks)
  • cloudy/src/main/kotlin/com/skydoves/cloudy/CloudyState.kt (0 hunks)
  • gradle/libs.versions.toml (2 hunks)
  • scripts/publish-module.gradle.kts (0 hunks)
💤 Files with no reviewable changes (2)
  • scripts/publish-module.gradle.kts
  • cloudy/src/main/kotlin/com/skydoves/cloudy/CloudyState.kt
✅ Files skipped from review due to trivial changes (1)
  • cloudy/src/commonMain/kotlin/com/skydoves/cloudy/Cloudy.kt
🚧 Files skipped from review as they are similar to previous changes (9)
  • build.gradle.kts
  • .gitignore
  • cloudy/src/commonMain/kotlin/com/skydoves/cloudy/PlatformBitmap.kt
  • cloudy/src/commonMain/kotlin/com/skydoves/cloudy/CloudyState.kt
  • cloudy/src/androidMain/kotlin/com/skydoves/cloudy/PlatformBitmap.android.kt
  • buildSrc/src/main/kotlin/com/skydoves/cloudy/ActiveArch.kt
  • cloudy/src/androidMain/kotlin/com/skydoves/cloudy/Cloudy.android.kt
  • cloudy/build.gradle.kts
  • cloudy/api/cloudy.api
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:13:55.820Z
Learning: In the Cloudy project, the UIImage.asImageBitmap() function in cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt currently uses a placeholder implementation that generates synthetic gradients instead of extracting actual CGImage pixels. This is known technical debt that should be addressed in a future PR focused on pixel conversion optimization.
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:12:51.564Z
Learning: User l2hyunwoo prefers to keep PRs focused on their primary objective and avoid scope creep. They acknowledge technical debt items but prefer to address them in separate PRs rather than expanding the current PR scope.
📚 Learning: in the cloudy project, the uiimage.asimagebitmap() function in cloudy/src/iosmain/kotlin/com/skydove...
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:13:55.820Z
Learning: In the Cloudy project, the UIImage.asImageBitmap() function in cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt currently uses a placeholder implementation that generates synthetic gradients instead of extracting actual CGImage pixels. This is known technical debt that should be addressed in a future PR focused on pixel conversion optimization.

Applied to files:

  • cloudy/src/iosTest/kotlin/com/skydoves/cloudy/CloudyStateTest.kt
  • cloudy/src/iosMain/kotlin/com/skydoves/cloudy/Cloudy.ios.kt
  • cloudy/src/androidUnitTest/kotlin/com/skydoves/cloudy/PlatformBitmapTest.kt
  • cloudy/src/iosTest/kotlin/com/skydoves/cloudy/TestUtils.kt
  • cloudy/src/androidUnitTest/kotlin/com/skydoves/cloudy/CloudyStateTest.kt
  • cloudy/src/iosTest/kotlin/com/skydoves/cloudy/PlatformBitmapTest.kt
  • cloudy/src/androidMain/kotlin/com/skydoves/cloudy/internals/render/RenderScriptToolkit.kt
  • cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt
🧬 Code Graph Analysis (1)
cloudy/src/iosTest/kotlin/com/skydoves/cloudy/CloudyStateTest.kt (1)
cloudy/src/iosTest/kotlin/com/skydoves/cloudy/TestUtils.kt (1)
  • createTestPlatformBitmap (29-31)
🔇 Additional comments (41)
cloudy/src/androidMain/kotlin/com/skydoves/cloudy/internals/render/RenderScriptToolkit.kt (9)

236-243: LGTM! Comprehensive KDoc documentation added.

The detailed KDoc for Rgba3dArray clearly documents the class purpose, parameters, and behavior. The documentation helps understand the 3D RGBA data structure and its indexed access pattern.


262-270: Excellent documentation for internal method.

The KDoc for indexOfVector provides clear documentation of the coordinate-to-index calculation, including parameter descriptions and exception behavior. This is particularly valuable for understanding the 3D array indexing logic.


279-288: Well-documented validation function.

The KDoc clearly explains the bitmap validation requirements, parameters, and exception conditions. This documentation will help developers understand when and why bitmap validation might fail.


314-319: Clear documentation for bitmap creation utility.

The KDoc succinctly describes the purpose and behavior of createCompatibleBitmap, making it easy to understand its role in creating matching bitmap instances.


323-327: Good documentation for validation utility.

The KDoc clearly explains the restriction validation logic and error conditions, helping developers understand the bounds checking requirements.


349-357: Comprehensive documentation for pixel size calculation.

The KDoc clearly explains the supported bitmap configurations and their corresponding byte sizes, along with exception behavior for unsupported formats.


368-377: Excellent documentation for complex blur algorithm.

The KDoc provides a clear explanation of the iterative blur strategy, explaining why multiple passes are needed and how the algorithm handles radius limits.


383-394: Critical fix: Corrected blur radius calculation.

The iterative blur logic has been properly corrected to fix off-by-one errors:

  • iterate = radius / 25 correctly calculates full passes
  • remainder = radius % 25 correctly calculates remaining radius
  • Proper handling when remainder is 0 vs non-zero

This fix ensures accurate blur radius application.


396-402: LGTM! Correct iterative application.

The use of repeat(iterate) correctly applies the calculated number of full 25-radius blur passes. The logic properly chains the blur operations through the bitmap variable.

gradle/libs.versions.toml (4)

21-26: Good addition of multiplatform dependencies.

The new version entries for Compose Multiplatform (1.8.2) and testing libraries (junit4, kotlinx-coroutines-test, mockito) provide proper version management for the multiplatform migration.


32-33: LGTM! Essential multiplatform plugins added.

The addition of kotlin-multiplatform and compose-multiplatform plugins are correctly configured and essential for the multiplatform support objective.


58-71: Comprehensive multiplatform library support.

The new library entries provide excellent coverage for:

  • Compose Multiplatform runtime and UI components
  • Kotlin test utilities
  • AndroidX Compose testing libraries
  • Mockito testing framework

These additions align well with the multiplatform testing strategy evidenced in the other files.


2-2: No action required: AGP 8.12.0 compatibility confirmed

Android Gradle Plugin 8.12.0 is fully compatible with Kotlin 2.2.0 and Compose Multiplatform 1.8.2. You can continue using agp = "8.12.0" in gradle/libs.versions.toml as is.
For optimal alignment, ensure the Compose Compiler Gradle plugin version matches Kotlin 2.2.0.

cloudy/src/iosTest/kotlin/com/skydoves/cloudy/TestUtils.kt (3)

16-21: Proper foreign API handling.

The @file:OptIn(ExperimentalForeignApi::class) annotation and import are correctly used for interacting with iOS platform APIs.


29-31: Clean test utility design.

The createTestPlatformBitmap function provides a simple, focused interface for creating test bitmaps by delegating to the more specific createTestUIImage function.


43-48: Robust UIImage creation with proper error handling.

The createTestUIImage function correctly:

  • Uses proper iOS graphics APIs
  • Handles the graphics context lifecycle properly
  • Provides fallback to empty UIImage if creation fails
  • Uses appropriate parameter types and scaling
cloudy/src/iosTest/kotlin/com/skydoves/cloudy/CloudyStateTest.kt (6)

25-30: LGTM! Proper singleton validation.

The test correctly verifies that CloudyState.Nothing maintains singleton behavior using reference equality (===).


32-37: LGTM! Consistent singleton testing.

The test correctly verifies that CloudyState.Loading maintains singleton behavior, consistent with the Nothing state test.


39-44: Good integration with test utilities.

The test properly uses createTestPlatformBitmap from the test utilities and validates that the Success state correctly holds the bitmap reference.


46-51: Comprehensive error state testing.

The test properly validates that the Error state correctly holds and returns the provided throwable.


53-59: LGTM! Proper equality testing.

The test correctly verifies that Success states with the same bitmap are considered equal, which is important for state management.


61-68: Thorough inequality validation.

The test properly validates that Success states with different bitmaps are not equal, completing the equality testing coverage.

cloudy/src/iosTest/kotlin/com/skydoves/cloudy/PlatformBitmapTest.kt (5)

24-31: LGTM! Proper dimension validation.

The test correctly validates that PlatformBitmap reports the correct width and height from the underlying UIImage.


33-37: Good iOS-specific behavior validation.

The test correctly validates that iOS PlatformBitmap instances are always recyclable, which aligns with the iOS platform behavior where bitmap recycling is handled automatically.


39-45: Thorough compatibility testing.

The test properly validates that createCompatible() produces a new bitmap with the same dimensions, which is essential for the blur operations.


47-52: Robust dispose behavior testing.

The test correctly validates that dispose() can be called multiple times without throwing exceptions, which is important for safe resource management in the multiplatform context.


54-59: Good extension method validation.

The test properly validates that the toUIImage() extension returns the correct underlying UIImage, ensuring proper platform integration.

cloudy/src/androidUnitTest/kotlin/com/skydoves/cloudy/PlatformBitmapTest.kt (5)

33-47: Well-structured test for basic properties.

The test comprehensively verifies that PlatformBitmap correctly exposes width and height from the underlying Android Bitmap, with appropriate mocking of all required properties.


49-86: Excellent edge case coverage for recyclability.

The three tests comprehensively cover all recyclability scenarios based on the bitmap's mutable and recycled states, ensuring the isRecyclable property behaves correctly in all cases.


88-144: Proper static method mocking and comprehensive createCompatible testing.

Excellent use of mockStatic with try-with-resources pattern. The tests thoroughly verify that createCompatible preserves both dimensions and bitmap configuration.


146-177: Thorough dispose() method testing.

Good coverage of both the normal disposal case and the edge case of already-recycled bitmaps. The use of verify(mockBitmap, times(0)).recycle() correctly ensures no redundant recycling occurs.


179-205: Correct extension function testing with proper identity checks.

Good use of assertSame to ensure the extension functions maintain bitmap instance identity during wrapping/unwrapping operations.

cloudy/src/iosMain/kotlin/com/skydoves/cloudy/Cloudy.ios.kt (4)

79-110: Well-structured modifier element implementation.

The CloudyModifierNodeElement correctly implements the ModifierNodeElement interface with proper data class semantics and inspector support.


151-152: Known limitation: placeholder image conversion.

The toUIImage().asImageBitmap() conversion currently uses a synthetic gradient implementation rather than actual pixel extraction, as noted in the learnings. This is acknowledged technical debt to be addressed in a future PR.


204-225: Robust Core Image blur implementation.

Excellent error handling with proper null checks and exception catching. The CIGaussianBlur filter usage is correct and follows iOS best practices.


20-20: Confirm IntRange annotation compatibility on iOS
The use of @IntRange(from = 0) from androidx.annotation shows up in both your common and iOS‐specific code, but I didn’t find an explicit androidx.annotation dependency in your Gradle scripts. Without a dedicated multiplatform artifact, this annotation may not be available when compiling for iOS.

• Verify that androidx.annotation:IntRange is pulled in transitively for all targets (including iOS).
• If not, consider replacing it with a Kotlin native or custom multiplatform annotation in commonMain to enforce the non-negative radius.

cloudy/src/androidUnitTest/kotlin/com/skydoves/cloudy/CloudyStateTest.kt (2)

29-43: Correct singleton verification for stateless variants.

Good use of assertSame to verify that Nothing and Loading states are true singletons, which is important for performance and memory efficiency.


45-85: Comprehensive Success state testing.

Excellent coverage including the null bitmap case and proper equality semantics based on bitmap identity.

cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt (3)

48-56: Correct iOS-specific bitmap property implementations.

Good adaptation to iOS conventions:

  • Width/height properly account for retina display scale
  • isRecyclable correctly reflects iOS's ARC memory management

98-188: Documented placeholder implementation for ImageBitmap to UIImage conversion.

The implementation creates a representative image by sampling colors and generating patterns. The extensive comments clearly document this as an approximation. This aligns with the known technical debt to be addressed in a future PR focused on pixel conversion optimization.


190-256: Known placeholder implementation for UIImage to ImageBitmap conversion.

As documented in the comments and confirmed by learnings, this generates synthetic gradients rather than extracting actual CGImage pixels. This technical debt is acknowledged and will be addressed in a future PR focused on pixel conversion optimization.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
cloudy/src/iosMain/kotlin/com/skydoves/cloudy/Cloudy.ios.kt (2)

144-144: Consider using a more appropriate dispatcher

For CPU-intensive image processing operations, consider using Dispatchers.IO instead of Dispatchers.Default to avoid blocking the default dispatcher pool.

-    coroutineScope.launch(Dispatchers.Default) {
+    coroutineScope.launch(Dispatchers.IO) {

178-182: Document the temporary nature of the placeholder implementation

Consider adding a TODO comment to track the future replacement of this placeholder implementation with actual graphics layer capture.

   /**
    * Creates a placeholder bitmap representing the composable content.
    * This is a temporary solution until graphics layer capture is available on iOS.
+   * TODO: Replace with actual graphics layer capture when available in Compose iOS
    */
   private fun createPlaceholderBitmap(width: Int, height: Int): ImageBitmap? {
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ec5ec8 and f30370a.

📒 Files selected for processing (2)
  • cloudy/src/iosMain/kotlin/com/skydoves/cloudy/Cloudy.ios.kt (1 hunks)
  • cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:13:55.820Z
Learning: In the Cloudy project, the UIImage.asImageBitmap() function in cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt currently uses a placeholder implementation that generates synthetic gradients instead of extracting actual CGImage pixels. This is known technical debt that should be addressed in a future PR focused on pixel conversion optimization.
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:12:51.564Z
Learning: User l2hyunwoo prefers to keep PRs focused on their primary objective and avoid scope creep. They acknowledge technical debt items but prefer to address them in separate PRs rather than expanding the current PR scope.
📚 Learning: in the cloudy project, the uiimage.asimagebitmap() function in cloudy/src/iosmain/kotlin/com/skydove...
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:13:55.820Z
Learning: In the Cloudy project, the UIImage.asImageBitmap() function in cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt currently uses a placeholder implementation that generates synthetic gradients instead of extracting actual CGImage pixels. This is known technical debt that should be addressed in a future PR focused on pixel conversion optimization.

Applied to files:

  • cloudy/src/iosMain/kotlin/com/skydoves/cloudy/Cloudy.ios.kt
  • cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Spotless check
  • GitHub Check: build
  • GitHub Check: API check
🔇 Additional comments (4)
cloudy/src/iosMain/kotlin/com/skydoves/cloudy/Cloudy.ios.kt (1)

71-102: LGTM!

The CloudyModifierNodeElement implementation correctly follows Compose Modifier patterns with proper inspector properties and node lifecycle management.

cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt (3)

53-72: LGTM!

The PlatformBitmap implementation correctly handles iOS UIImage wrapping with proper scale-aware dimension calculations.


74-98: Platform-appropriate implementations

Both createCompatible() and dispose() are correctly implemented for iOS, with clear documentation explaining the ARC-based memory management approach.


199-288: Excellent pixel extraction implementation

This implementation properly extracts actual CGImage pixels instead of using synthetic gradients, addressing the technical debt mentioned in previous reviews. The RGBA to BGRA conversion for Skia compatibility is correctly handled.

Comment thread cloudy/src/iosMain/kotlin/com/skydoves/cloudy/Cloudy.ios.kt
* @return The blurred bitmap, or null if the blur operation fails.
*/
@OptIn(ExperimentalForeignApi::class)
private suspend fun createBlurredBitmapFromContent(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove redundant @OptIn annotation

This annotation is unnecessary as applyGaussianBlur already has the same annotation at line 254.

-@OptIn(ExperimentalForeignApi::class)
 private suspend fun createBlurredBitmapFromContent(
🤖 Prompt for AI Agents
In cloudy/src/iosMain/kotlin/com/skydoves/cloudy/Cloudy.ios.kt at line 223,
remove the redundant @OptIn annotation from the createBlurredBitmapFromContent
function since the applyGaussianBlur function at line 254 already has this
annotation, making it unnecessary here.

// This method is kept for API consistency across platforms
}

/****
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix documentation comment syntax

-/****
+/**
🤖 Prompt for AI Agents
In cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt at line
100, the documentation comment syntax is incorrect. Replace the current /***
style comment with the proper Kotlin documentation comment syntax /** to ensure
it is recognized as a valid KDoc comment.

@l2hyunwoo l2hyunwoo force-pushed the develop/cmp branch 2 times, most recently from 17786f0 to 473e202 Compare August 2, 2025 06:43
l2hyunwoo and others added 19 commits August 2, 2025 15:44
Update buildSrc/src/main/kotlin/com/skydoves/cloudy/ActiveArch.kt

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
[#53] revert for resolve build issue
[#53] Apply spotlessApply, apiDump

[#53] Apply code review

[#53] Apply code review
Docstrings generation was requested by @l2hyunwoo.

* #63 (comment)

The following files were modified:

* `buildSrc/src/main/kotlin/com/skydoves/cloudy/ActiveArch.kt`
* `cloudy/src/androidMain/kotlin/com/skydoves/cloudy/Cloudy.android.kt`
* `cloudy/src/androidMain/kotlin/com/skydoves/cloudy/PlatformBitmap.android.kt`
* `cloudy/src/androidMain/kotlin/com/skydoves/cloudy/internals/render/RenderScriptToolkit.kt`
* `cloudy/src/commonMain/kotlin/com/skydoves/cloudy/Cloudy.kt`
* `cloudy/src/commonMain/kotlin/com/skydoves/cloudy/PlatformBitmap.kt`
* `cloudy/src/iosMain/kotlin/com/skydoves/cloudy/Cloudy.ios.kt`
* `cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt`

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
- Add iOS targets (iosX64, iosArm64, iosSimulatorArm64)
- Configure ComposeApp framework for iOS
- Set up multiplatform source sets (androidMain, commonMain, iosMain)
- Move demo code from cloudy module to app/commonMain
- Add MainViewController for iOS
- Add platform-specific UUID implementations
- Remove all demo code and dependencies from cloudy module
- Remove Landscapist and Material dependencies (moved to app module)
- Keep only core Cloudy blur functionality
- Make cloudy module a pure library without app-specific code
- Remove old src/main directory structure
- Clean up old Android-specific code in app module
- Prepare for new multiplatform source set layout
- Add Coil 3.2.0 dependencies for Compose Multiplatform
- Add landscapist-coil3 for cross-platform image loading
- Replace Android-specific GlideImage with KMP-compatible CoilImage
- Remove Kotlin source files and Gradle configuration from iosApp
- Update ContentView.swift to use ComposeApp framework
- Remove iosApp from Gradle settings (now pure Xcode project)
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
cloudy/build.gradle.kts (1)

20-26: Redundant Compose compiler plugin

org.jetbrains.compose (Compose Multiplatform) already applies the Compose compiler plugin internally. Adding org.jetbrains.kotlin.plugin.compose separately introduces duplicated compiler plugin registrations and longer configuration times.

Safe to drop the explicit line:

-  id(libs.plugins.compose.compiler.get().pluginId)
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f30370a and a084728.

⛔ Files ignored due to path filters (2)
  • app/src/androidMain/res/drawable/poster.png is excluded by !**/*.png
  • app/src/commonMain/composeResources/drawable/poster.png is excluded by !**/*.png
📒 Files selected for processing (44)
  • .gitignore (1 hunks)
  • app/build.gradle.kts (1 hunks)
  • app/src/androidMain/kotlin/com/skydoves/cloudydemo/MainActivity.kt (1 hunks)
  • app/src/androidMain/kotlin/demo/model/Poster.android.kt (1 hunks)
  • app/src/commonMain/kotlin/demo/Main.kt (5 hunks)
  • app/src/commonMain/kotlin/demo/model/MockUtil.kt (2 hunks)
  • app/src/commonMain/kotlin/demo/model/Poster.kt (1 hunks)
  • app/src/commonMain/kotlin/demo/theme/Color.kt (1 hunks)
  • app/src/commonMain/kotlin/demo/theme/PosterTheme.kt (3 hunks)
  • app/src/commonMain/kotlin/demo/theme/Type.kt (1 hunks)
  • app/src/iosMain/kotlin/com/skydoves/cloudydemo/MainViewController.kt (1 hunks)
  • app/src/iosMain/kotlin/demo/model/Poster.ios.kt (1 hunks)
  • app/src/main/kotlin/com/skydoves/cloudydemo/custom/StaggeredVerticalGrid.kt (0 hunks)
  • app/src/main/kotlin/com/skydoves/cloudydemo/posters/HomePosters.kt (0 hunks)
  • app/src/main/kotlin/com/skydoves/cloudydemo/posters/LibraryPosters.kt (0 hunks)
  • app/src/main/kotlin/com/skydoves/cloudydemo/posters/RadioPosters.kt (0 hunks)
  • app/src/main/kotlin/com/skydoves/cloudydemo/theme/Type.kt (0 hunks)
  • build.gradle.kts (1 hunks)
  • buildSrc/src/main/kotlin/com/skydoves/cloudy/ActiveArch.kt (1 hunks)
  • cloudy/api/cloudy.api (1 hunks)
  • cloudy/build.gradle.kts (1 hunks)
  • cloudy/src/androidMain/kotlin/com/skydoves/cloudy/Cloudy.android.kt (4 hunks)
  • cloudy/src/androidMain/kotlin/com/skydoves/cloudy/PlatformBitmap.android.kt (1 hunks)
  • cloudy/src/androidMain/kotlin/com/skydoves/cloudy/internals/render/RenderScriptToolkit.kt (6 hunks)
  • cloudy/src/androidUnitTest/kotlin/com/skydoves/cloudy/CloudyStateTest.kt (1 hunks)
  • cloudy/src/androidUnitTest/kotlin/com/skydoves/cloudy/PlatformBitmapTest.kt (1 hunks)
  • cloudy/src/commonMain/kotlin/com/skydoves/cloudy/Cloudy.kt (1 hunks)
  • cloudy/src/commonMain/kotlin/com/skydoves/cloudy/CloudyState.kt (1 hunks)
  • cloudy/src/commonMain/kotlin/com/skydoves/cloudy/PlatformBitmap.kt (1 hunks)
  • cloudy/src/iosMain/kotlin/com/skydoves/cloudy/Cloudy.ios.kt (1 hunks)
  • cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt (1 hunks)
  • cloudy/src/iosTest/kotlin/com/skydoves/cloudy/CloudyStateTest.kt (1 hunks)
  • cloudy/src/iosTest/kotlin/com/skydoves/cloudy/PlatformBitmapTest.kt (1 hunks)
  • cloudy/src/iosTest/kotlin/com/skydoves/cloudy/TestUtils.kt (1 hunks)
  • cloudy/src/main/kotlin/com/skydoves/cloudy/CloudyState.kt (0 hunks)
  • gradle/libs.versions.toml (2 hunks)
  • iosApp/iosApp.xcodeproj/project.pbxproj (1 hunks)
  • iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json (1 hunks)
  • iosApp/iosApp/Assets.xcassets/Contents.json (1 hunks)
  • iosApp/iosApp/ContentView.swift (1 hunks)
  • iosApp/iosApp/Info.plist (1 hunks)
  • iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json (1 hunks)
  • iosApp/iosApp/iOSApp.swift (1 hunks)
  • scripts/publish-module.gradle.kts (0 hunks)
💤 Files with no reviewable changes (7)
  • scripts/publish-module.gradle.kts
  • app/src/main/kotlin/com/skydoves/cloudydemo/theme/Type.kt
  • cloudy/src/main/kotlin/com/skydoves/cloudy/CloudyState.kt
  • app/src/main/kotlin/com/skydoves/cloudydemo/posters/RadioPosters.kt
  • app/src/main/kotlin/com/skydoves/cloudydemo/custom/StaggeredVerticalGrid.kt
  • app/src/main/kotlin/com/skydoves/cloudydemo/posters/HomePosters.kt
  • app/src/main/kotlin/com/skydoves/cloudydemo/posters/LibraryPosters.kt
✅ Files skipped from review due to trivial changes (6)
  • app/src/iosMain/kotlin/com/skydoves/cloudydemo/MainViewController.kt
  • app/src/commonMain/kotlin/demo/theme/Type.kt
  • iosApp/iosApp/Info.plist
  • iosApp/iosApp/ContentView.swift
  • cloudy/src/commonMain/kotlin/com/skydoves/cloudy/Cloudy.kt
  • app/src/commonMain/kotlin/demo/model/MockUtil.kt
🚧 Files skipped from review as they are similar to previous changes (28)
  • app/src/commonMain/kotlin/demo/theme/PosterTheme.kt
  • build.gradle.kts
  • iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json
  • iosApp/iosApp/Assets.xcassets/Contents.json
  • .gitignore
  • app/src/androidMain/kotlin/com/skydoves/cloudydemo/MainActivity.kt
  • app/src/androidMain/kotlin/demo/model/Poster.android.kt
  • app/src/iosMain/kotlin/demo/model/Poster.ios.kt
  • app/src/commonMain/kotlin/demo/model/Poster.kt
  • cloudy/src/iosTest/kotlin/com/skydoves/cloudy/TestUtils.kt
  • iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json
  • buildSrc/src/main/kotlin/com/skydoves/cloudy/ActiveArch.kt
  • cloudy/src/iosTest/kotlin/com/skydoves/cloudy/CloudyStateTest.kt
  • cloudy/src/commonMain/kotlin/com/skydoves/cloudy/CloudyState.kt
  • cloudy/src/androidUnitTest/kotlin/com/skydoves/cloudy/CloudyStateTest.kt
  • cloudy/src/androidMain/kotlin/com/skydoves/cloudy/PlatformBitmap.android.kt
  • cloudy/src/iosTest/kotlin/com/skydoves/cloudy/PlatformBitmapTest.kt
  • iosApp/iosApp.xcodeproj/project.pbxproj
  • app/src/commonMain/kotlin/demo/theme/Color.kt
  • app/src/commonMain/kotlin/demo/Main.kt
  • app/build.gradle.kts
  • cloudy/src/androidUnitTest/kotlin/com/skydoves/cloudy/PlatformBitmapTest.kt
  • cloudy/src/androidMain/kotlin/com/skydoves/cloudy/internals/render/RenderScriptToolkit.kt
  • cloudy/src/commonMain/kotlin/com/skydoves/cloudy/PlatformBitmap.kt
  • cloudy/api/cloudy.api
  • cloudy/src/androidMain/kotlin/com/skydoves/cloudy/Cloudy.android.kt
  • cloudy/src/iosMain/kotlin/com/skydoves/cloudy/Cloudy.ios.kt
  • cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:13:55.820Z
Learning: In the Cloudy project, the UIImage.asImageBitmap() function in cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt currently uses a placeholder implementation that generates synthetic gradients instead of extracting actual CGImage pixels. This is known technical debt that should be addressed in a future PR focused on pixel conversion optimization.
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:12:51.564Z
Learning: User l2hyunwoo prefers to keep PRs focused on their primary objective and avoid scope creep. They acknowledge technical debt items but prefer to address them in separate PRs rather than expanding the current PR scope.
📚 Learning: in the cloudy project, the uiimage.asimagebitmap() function in cloudy/src/iosmain/kotlin/com/skydove...
Learnt from: l2hyunwoo
PR: skydoves/Cloudy#63
File: cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt:0-0
Timestamp: 2025-07-06T06:13:55.820Z
Learning: In the Cloudy project, the UIImage.asImageBitmap() function in cloudy/src/iosMain/kotlin/com/skydoves/cloudy/PlatformBitmap.ios.kt currently uses a placeholder implementation that generates synthetic gradients instead of extracting actual CGImage pixels. This is known technical debt that should be addressed in a future PR focused on pixel conversion optimization.

Applied to files:

  • cloudy/build.gradle.kts
🪛 SwiftLint (0.57.0)
iosApp/iosApp/iOSApp.swift

[Error] 4-4: Type name 'iOSApp' should start with an uppercase character

(type_name)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: API check
  • GitHub Check: build
  • GitHub Check: Spotless check
🔇 Additional comments (5)
iosApp/iosApp/iOSApp.swift (3)

1-1: LGTM!

Standard SwiftUI import for the app entry point.


3-4: Type name follows iOS conventions correctly.

The SwiftLint warning about the type name is a false positive. iOSApp follows established iOS naming conventions where "iOS" is a proper noun that traditionally starts with lowercase 'i' followed by uppercase 'O' and 'S'. This is consistent with Apple's own naming patterns (e.g., iOS, iOSMain, etc.).


5-9: Clean SwiftUI App structure implementation.

The implementation correctly follows SwiftUI App protocol conventions with a WindowGroup hosting the ContentView. This establishes the proper entry point for the iOS side of the Compose Multiplatform integration, allowing the Kotlin Compose UI to be displayed through the ContentView bridge.

cloudy/build.gradle.kts (1)

31-38: Verify compiler flag -Xexpect-actual-classes

-Xexpect-actual-classes was an experimental flag and has been removed/renamed in recent Kotlin 2.2 nightly builds. If you bump Kotlin further this will break the build. Double-check its necessity or gate it behind a Kotlin-version check.

gradle/libs.versions.toml (1)

59-63: Potential duplication between JetBrains-Compose and AndroidX Compose artifacts

compose-runtime/foundation/ui from JetBrains Compose (lines 59-63) and their AndroidX counterparts (lines 44-49) ship identical code but with different Maven coordinates. Mixing them in the same source-set can lead to duplicate classes and packaging conflicts.

Confirm that:
• JetBrains Compose libs are used for common/desktop/iOS source-sets.
• Android source-sets depend exclusively on AndroidX Compose libs.

If not, restrict each set of coordinates to the appropriate platform-specific source-set to avoid runtime and packaging clashes.

Comment thread cloudy/build.gradle.kts
Comment on lines +61 to 100
android {
compileSdk = Configuration.compileSdk
namespace = "com.skydoves.cloudy"
defaultConfig {
minSdk = Configuration.minSdk
externalNativeBuild {
cmake {
cppFlags += "-std=c++17"
arguments += listOf("-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON")
}
}
}
externalNativeBuild {
cmake {
cppFlags += "-std=c++17"
arguments += listOf("-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON")
path = file("src/main/cpp/CMakeLists.txt")
}
}
}
externalNativeBuild {
cmake {
path = file("src/main/cpp/CMakeLists.txt")

buildFeatures {
compose = true
}
}

buildFeatures {
compose = true
}
packaging {
resources {
excludes.add("/META-INF/{AL2.0,LGPL2.1}")
}
}

packaging {
resources {
excludes.add("/META-INF/{AL2.0,LGPL2.1}")
sourceSets {
getByName("main") {
assets.srcDirs("src/androidMain/assets")
java.srcDirs("src/androidMain/kotlin")
res.srcDirs("src/androidMain/res")
}
getByName("test") {
assets.srcDirs("src/androidUnitTest/assets")
java.srcDirs("src/androidUnitTest/kotlin")
res.srcDirs("src/androidUnitTest/res")
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

android {} block is misplaced – move it out of the kotlin {} scope

The android { … } DSL belongs to the Android Gradle plugin (com.android.library) and must be declared at the module level, not inside the Kotlin Multiplatform kotlin {} block.
Keeping it nested will make Gradle treat it as a Kotlin target-specific configuration and the properties such as compileSdk, defaultConfig, externalNativeBuild, buildFeatures, packaging, and sourceSets will simply be ignored, leading to missing compileSdk and other essential settings at build time.

-kotlin {
-
-  android {
-     compileSdk = …
-
-  }
-}
+
+kotlin {
+  … // keep KMP targets & source-sets
+}
+
+android {           // <-- move to top-level
+  compileSdk = Configuration.compileSdk
+  namespace = "com.skydoves.cloudy"
+  defaultConfig {
+     minSdk = Configuration.minSdk
+
+  }
+
+}

[fatal build-breaking issue]

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In cloudy/build.gradle.kts between lines 61 and 100, the android { } block is
incorrectly placed inside the kotlin { } block. This causes Gradle to ignore
essential Android build configurations like compileSdk and defaultConfig. To fix
this, move the entire android { } block out of the kotlin { } scope so it is
declared at the module level, ensuring all Android Gradle plugin settings are
properly applied.

Comment thread gradle/libs.versions.toml
Comment on lines +66 to +68
landscapist-coil = { group = "com.github.skydoves", name = "landscapist-coil3", version.ref = "landscapist" }
coil = { group = "io.coil-kt.coil3", name = "coil-compose", version.ref = "coil" }
coil-network = { group = "io.coil-kt.coil3", name = "coil-network-ktor3", version.ref = "coil" }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Incorrect Maven coordinates for Coil 3 artifacts

The group id should be io.coil-kt, not io.coil-kt.coil3. Using the wrong coordinates will make Gradle fail to resolve the dependency.

-landscapist-coil = { group = "com.github.skydoves", name = "landscapist-coil3", version.ref = "landscapist" }
-coil          = { group = "io.coil-kt.coil3", name = "coil-compose",       version.ref = "coil" }
-coil-network  = { group = "io.coil-kt.coil3", name = "coil-network-ktor3", version.ref = "coil" }
+
+landscapist-coil = { group = "com.github.skydoves", name = "landscapist-coil3", version.ref = "landscapist" }
+coil          = { group = "io.coil-kt", name = "coil-compose",       version.ref = "coil" }
+coil-network  = { group = "io.coil-kt", name = "coil-network-ktor", version.ref = "coil" }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
landscapist-coil = { group = "com.github.skydoves", name = "landscapist-coil3", version.ref = "landscapist" }
coil = { group = "io.coil-kt.coil3", name = "coil-compose", version.ref = "coil" }
coil-network = { group = "io.coil-kt.coil3", name = "coil-network-ktor3", version.ref = "coil" }
landscapist-coil = { group = "com.github.skydoves", name = "landscapist-coil3", version.ref = "landscapist" }
coil = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" }
coil-network = { group = "io.coil-kt", name = "coil-network-ktor", version.ref = "coil" }
🤖 Prompt for AI Agents
In gradle/libs.versions.toml around lines 66 to 68, the group id for the Coil 3
dependencies is incorrectly set to "io.coil-kt.coil3". Update the group id to
"io.coil-kt" for both "coil" and "coil-network" entries to ensure Gradle can
correctly resolve these dependencies.

@l2hyunwoo l2hyunwoo merged commit fef9e2e into main Aug 2, 2025
4 checks passed
@l2hyunwoo l2hyunwoo deleted the develop/cmp branch August 2, 2025 07:20
@coderabbitai coderabbitai Bot mentioned this pull request Dec 17, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Jan 15, 2026
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.

Will this support Compose Multiplatform?

1 participant