Skip to content

Conversation

@zibet27
Copy link
Collaborator

@zibet27 zibet27 commented Oct 16, 2025

Subsystem
WebRTC Client.

Motivation
KTOR-8977 Expose Core dependencies in WebRTC Client

Solution
Use api instead of implementation for necessary dependencies.
It works for Android and JS, but on iOS, it's not possible to make the Cocoapod dependency transitive.
The user needs to add the WebRTC-SDK Cocoapod dependency for the linker.

Minor:

  • WebRtcPeerConnection.getNative was not available on Android and iOS.

@zibet27 zibet27 self-assigned this Oct 16, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 16, 2025

Walkthrough

Exposes underlying native WebRTC peer connections via new public getNative() extensions on WebRtcPeerConnection for Android and iOS, adjusts peerConnection field visibility to internal, updates API signatures (capturer/ senders), and changes build configuration and dependency exposures for the webrtc client module.

Changes

Cohort / File(s) Summary
Android engine & peer connection
ktor-client/ktor-client-webrtc/android/src/io/ktor/client/webrtc/Engine.kt, ktor-client/ktor-client-webrtc/android/src/io/ktor/client/webrtc/PeerConnection.kt
Adds/adjusts Android WebRTC wiring: visibility of peerConnection -> internal; introduces WebRtcPeerConnection.getNative(): org.webrtc.PeerConnection accessor; engine factory wiring changed between diffs (creation/config cast updated).
iOS peer connection
ktor-client/ktor-client-webrtc/ios/src/io/ktor/client/webrtc/PeerConnection.kt
Changes peerConnection visibility to internal and adds WebRtcPeerConnection.getNative(): RTCPeerConnection extension returning the internal peerConnection.
Public API metadata / klib
ktor-client/ktor-client-webrtc/api/android/ktor-client-webrtc.api, ktor-client/ktor-client-webrtc/api/ktor-client-webrtc.klib.api
Records new public getNative functions for PeerConnection and RtpSender, documents Capturer now extends AutoCloseable, adds defaultVideoCapturerFactory() and a close() on CameraVideoCapturer where applicable.
Build configuration
ktor-client/ktor-client-webrtc/build.gradle.kts
Adds ExperimentalKotlinGradlePluginApi OptIn/import; changes implementationapi for kotlinWrappers.browser and libs.stream.webrtc.android; sets CocoaPods pod extraOpts -fmodules and marks generated framework isStatic = true.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • bjhham
  • e5l

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "WebRTC Client. Expose core dependancies." directly relates to the main objective of the changeset, which is to expose core dependencies through the WebRTC Client by changing dependencies from implementation to api in the build configuration. While the title contains a minor spelling error ("dependancies" should be "dependencies"), it is specific and clear about the primary change. The title appropriately captures the main focus of the PR, even though it does not mention the secondary addition of getNative() extension functions—which is acceptable as titles are not expected to cover every detail.
Description Check ✅ Passed The pull request description follows the required template structure and includes all three mandatory sections. The Subsystem section identifies "WebRTC Client," the Motivation section provides a ticket reference (KTOR-8977) and explains the problem, and the Solution section clearly describes the approach of using api instead of implementation, including platform-specific considerations for iOS and mention of the minor getNative() additions. The description is substantive and provides sufficient context for reviewers to understand the scope and rationale of the changes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch zibet27/webrtc-client-expose-core-dependancies

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
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 (8)
ktor-client/ktor-client-webrtc/api/ktor-client-webrtc.klib.api (2)

1072-1073: Consider adding documentation and opt-in annotation.

The getNative() extension exposes platform-specific internals. Consider:

  1. Adding KDoc to explain the returned type and typical use cases
  2. Adding an @ExperimentalAPI or similar opt-in annotation to signal that this API provides low-level access

Example documentation:

/**
 * Returns the native RTCPeerConnection instance used by this WebRTC peer connection.
 * 
 * This provides direct access to the underlying WebRTC SDK's peer connection for advanced
 * use cases that require platform-specific APIs. Use with caution as this bypasses
 * Ktor's abstraction layer.
 * 
 * @return The native RTCPeerConnection from the WebRTC SDK
 */

As per coding guidelines


1076-1077: Add documentation for the factory function.

The defaultVideoCapturerFactory() function should document what default implementation it provides and when developers should use it versus creating custom factories.

As per coding guidelines

ktor-client/ktor-client-webrtc/ios/src/io/ktor/client/webrtc/PeerConnection.kt (1)

259-265: Good documentation and proper annotations.

The function is well-documented with an appropriate warning. The @OptIn(ExperimentalForeignApi::class) annotation correctly reflects that this exposes platform-specific APIs.

One minor suggestion: Consider adding a @see or reference to the WebRTC SDK documentation for developers unfamiliar with RTCPeerConnection.

ktor-client/ktor-client-webrtc/android/src/io/ktor/client/webrtc/Engine.kt (1)

63-66: Add KDoc for the factory object.

The new AndroidWebRtc factory object provides a clean API for creating WebRTC engines. Consider adding KDoc to explain its purpose and provide usage examples.

Example:

/**
 * Factory for creating Android WebRTC client engines.
 * 
 * Example usage:
 * ```kotlin
 * val client = WebRtcClient(AndroidWebRtc) {
 *     context = applicationContext
 * }
 * ```
 */
public object AndroidWebRtc : WebRtcClientEngineFactory<AndroidWebRtcEngineConfig> {
    // ...
}

As per coding guidelines

ktor-client/ktor-client-webrtc/android/src/io/ktor/client/webrtc/PeerConnection.kt (1)

223-228: Well-documented with room for enhancement.

The function includes an appropriate warning. Consider enhancing the documentation to match the level of detail suggested for the iOS version:

/**
 * Returns the native PeerConnection instance used by this WebRTC peer connection.
 * 
 * This provides direct access to the underlying WebRTC SDK's peer connection for advanced
 * use cases that require platform-specific APIs. Use with caution as this bypasses
 * Ktor's abstraction layer.
 * 
 * @return The native PeerConnection from the org.webrtc library
 * @see <a href="https://webrtc.github.io/webrtc-org/native-code/native-apis/">WebRTC Native APIs</a>
 */
public fun WebRtcPeerConnection.getNative(): PeerConnection {
    return (this as AndroidWebRtcPeerConnection).peerConnection
}
ktor-client/ktor-client-webrtc/build.gradle.kts (3)

43-43: Exposing kotlin-wrappers as API: verify it’s required

Using api(kotlinWrappers.browser) leaks wrappers to consumers and pins their versions. If your public API does not expose wrapper types/symbols, prefer implementation to avoid constraining consumers.

Can you confirm whether any public APIs in this module surface browser wrapper types? If not, please keep it as implementation.


53-53: Android WebRTC dependency via api: confirm public surface and versioning impact

api(libs.stream.webrtc.android) makes this a transitive requirement for consumers. This is fine if public APIs surface types from that SDK; otherwise, prefer implementation to avoid version lock/conflicts in apps that already depend on the same SDK.

If exposure is required, consider documenting the expected version range and any known conflicts in the module’s README/release notes to aid consumers.


74-74: Static iOS framework: check interplay with WebRTC-SDK and doc the integration

isStatic = true can reduce transitive linking issues, but if WebRTC-SDK is distributed as (or requires) dynamic frameworks, consumers may still need to add the pod to avoid linker errors and could hit duplicate symbols if both sides provide the same objects.

  • Please validate with a sample iOS app in both scenarios: with and without adding the WebRTC-SDK pod.
  • Add a brief note to docs on when iOS consumers must add the pod explicitly and potential duplicate symbol pitfalls.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 931540f and af7c51f.

📒 Files selected for processing (6)
  • ktor-client/ktor-client-webrtc/android/src/io/ktor/client/webrtc/Engine.kt (2 hunks)
  • ktor-client/ktor-client-webrtc/android/src/io/ktor/client/webrtc/PeerConnection.kt (2 hunks)
  • ktor-client/ktor-client-webrtc/api/android/ktor-client-webrtc.api (1 hunks)
  • ktor-client/ktor-client-webrtc/api/ktor-client-webrtc.klib.api (4 hunks)
  • ktor-client/ktor-client-webrtc/build.gradle.kts (4 hunks)
  • ktor-client/ktor-client-webrtc/ios/src/io/ktor/client/webrtc/PeerConnection.kt (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{kt,kts}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{kt,kts}: Follow Kotlin official style guide for all Kotlin source and build scripts
Use star imports for io.ktor.* packages
Max line length is 120 characters
Indent with 4 spaces in Kotlin code
Include a copyright header in new Kotlin files

Files:

  • ktor-client/ktor-client-webrtc/ios/src/io/ktor/client/webrtc/PeerConnection.kt
  • ktor-client/ktor-client-webrtc/android/src/io/ktor/client/webrtc/Engine.kt
  • ktor-client/ktor-client-webrtc/android/src/io/ktor/client/webrtc/PeerConnection.kt
  • ktor-client/ktor-client-webrtc/build.gradle.kts
**/*.kt

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.kt: Document all public Kotlin APIs, including parameters, return types, and exceptions
Annotate internal APIs with @internalapi
Follow Kotlin error-handling conventions and use specific Ktor exceptions

Files:

  • ktor-client/ktor-client-webrtc/ios/src/io/ktor/client/webrtc/PeerConnection.kt
  • ktor-client/ktor-client-webrtc/android/src/io/ktor/client/webrtc/Engine.kt
  • ktor-client/ktor-client-webrtc/android/src/io/ktor/client/webrtc/PeerConnection.kt
**/api/**

📄 CodeRabbit inference engine (CLAUDE.md)

Track all public API changes in the /api/ directories

Files:

  • ktor-client/ktor-client-webrtc/api/android/ktor-client-webrtc.api
  • ktor-client/ktor-client-webrtc/api/ktor-client-webrtc.klib.api
🧠 Learnings (3)
📚 Learning: 2025-08-26T06:07:28.352Z
Learnt from: CR
PR: ktorio/ktor#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-26T06:07:28.352Z
Learning: Project is Kotlin Multiplatform (JVM, JS, Native)

Applied to files:

  • ktor-client/ktor-client-webrtc/build.gradle.kts
📚 Learning: 2025-08-14T15:17:11.466Z
Learnt from: zibet27
PR: ktorio/ktor#5044
File: ktor-client/ktor-client-webrtc/ktor-client-webrtc-rs/build.gradle.kts:12-12
Timestamp: 2025-08-14T15:17:11.466Z
Learning: The Cargo plugin (dev.gobley.cargo) used in the Ktor WebRTC RS module depends on the kotlin("plugin.atomicfu") plugin, so atomicfu should not be removed even if there are no direct kotlinx.atomicfu imports in the module's source code.

Applied to files:

  • ktor-client/ktor-client-webrtc/build.gradle.kts
📚 Learning: 2025-08-14T15:17:11.466Z
Learnt from: zibet27
PR: ktorio/ktor#5044
File: ktor-client/ktor-client-webrtc/ktor-client-webrtc-rs/build.gradle.kts:12-12
Timestamp: 2025-08-14T15:17:11.466Z
Learning: The Gobley Cargo plugin (dev.gobley.cargo) used in the Ktor WebRTC RS module requires the kotlin("plugin.atomicfu") plugin as a dependency, so the atomicfu plugin should not be removed even if there are no direct kotlinx.atomicfu imports in the module's source code.

Applied to files:

  • ktor-client/ktor-client-webrtc/build.gradle.kts
🔇 Additional comments (7)
ktor-client/ktor-client-webrtc/api/ktor-client-webrtc.klib.api (1)

831-831: LGTM: Capturer now extends AutoCloseable.

This change improves resource management by ensuring all video capturers can be properly closed. The implementing classes (CameraVideoCapturer and SimulatorVideoCapturer) both provide the required close() method.

ktor-client/ktor-client-webrtc/ios/src/io/ktor/client/webrtc/PeerConnection.kt (1)

30-30: LGTM: Visibility change enables native access.

Changing peerConnection to internal is necessary to support the public getNative() extension function, aligning with the PR's objective to expose core dependencies.

ktor-client/ktor-client-webrtc/android/src/io/ktor/client/webrtc/Engine.kt (1)

34-36: LGTM: Improved error handling.

The refactored localFactory property provides clearer error messaging and properly handles the case where a custom MediaTrackFactory is provided without a corresponding rtcFactory.

ktor-client/ktor-client-webrtc/android/src/io/ktor/client/webrtc/PeerConnection.kt (1)

21-21: LGTM: Consistent visibility change across platforms.

The visibility change from private to internal mirrors the iOS implementation and is necessary to support the getNative() extension function.

ktor-client/ktor-client-webrtc/api/android/ktor-client-webrtc.api (1)

163-165: KDoc Present for getNative Methods KDoc exists for both getNative methods in PeerConnection.kt and Senders.kt; no further documentation required.

ktor-client/ktor-client-webrtc/build.gradle.kts (2)

4-4: Opt-in annotations/imports look correct

Annotation placement and imports are valid for Wasm DSL and Gradle Plugin API usage. LGTM.

Also applies to: 7-8


69-69: CocoaPods cinterop flag (-fmodules): validate portability

Ensure this flag is actually applied to cinterop (not just the Kotlin compiler) and that builds pass for:

  • device + simulator (arm64 + x86_64)
  • Xcode 15/16

If this was added to address a specific header/module parsing error, briefly document the rationale in a comment for future maintainers.

@zibet27 zibet27 requested review from bjhham and osipxd October 16, 2025 13:35
@zibet27 zibet27 enabled auto-merge (squash) October 20, 2025 08:15
Copy link
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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af7c51f and a220f1d.

📒 Files selected for processing (2)
  • ktor-client/ktor-client-webrtc/android/src/io/ktor/client/webrtc/PeerConnection.kt (2 hunks)
  • ktor-client/ktor-client-webrtc/ios/src/io/ktor/client/webrtc/PeerConnection.kt (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • ktor-client/ktor-client-webrtc/android/src/io/ktor/client/webrtc/PeerConnection.kt
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{kt,kts}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{kt,kts}: Follow Kotlin official style guide for all Kotlin source and build scripts
Use star imports for io.ktor.* packages
Max line length is 120 characters
Indent with 4 spaces in Kotlin code
Include a copyright header in new Kotlin files

Files:

  • ktor-client/ktor-client-webrtc/ios/src/io/ktor/client/webrtc/PeerConnection.kt
**/*.kt

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.kt: Document all public Kotlin APIs, including parameters, return types, and exceptions
Annotate internal APIs with @internalapi
Follow Kotlin error-handling conventions and use specific Ktor exceptions

Files:

  • ktor-client/ktor-client-webrtc/ios/src/io/ktor/client/webrtc/PeerConnection.kt
🔇 Additional comments (1)
ktor-client/ktor-client-webrtc/ios/src/io/ktor/client/webrtc/PeerConnection.kt (1)

30-30: LGTM! Appropriate visibility for internal access.

Changing to internal allows the getNative() extension function to access this property while keeping it unexposed outside the module.

Comment on lines +260 to +266
/**
* Returns implementation of the peer connection that is used under the hood. Use it with caution.
*/
@OptIn(ExperimentalForeignApi::class)
public fun WebRtcPeerConnection.getNative(): RTCPeerConnection {
return (this as IosWebRtcConnection).peerConnection
}
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 20, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Document exceptions and add initialization check.

The function has the following concerns:

  1. Uninitialized property access risk: Accessing peerConnection without verifying initialization will throw UninitializedPropertyAccessException if initialize() hasn't been called.

  2. Insufficient documentation: Per coding guidelines, public Kotlin APIs must document parameters, return types, and exceptions. The current documentation doesn't mention:

    • The precondition that initialize() must be called first
    • Potential exceptions (ClassCastException, UninitializedPropertyAccessException)
    • When and why developers should use this function

As per coding guidelines

Apply this diff to improve safety and documentation:

 /**
- * Returns implementation of the peer connection that is used under the hood. Use it with caution.
+ * Returns the native RTCPeerConnection implementation used under the hood.
+ *
+ * This function provides direct access to the platform-specific peer connection for advanced use cases
+ * that require native iOS WebRTC SDK APIs not exposed by the Ktor wrapper.
+ *
+ * @return the underlying [RTCPeerConnection] instance.
+ * @throws UninitializedPropertyAccessException if [IosWebRtcConnection.initialize] has not been called.
+ * @throws ClassCastException if called on a non-iOS WebRtcPeerConnection implementation.
  */
 @OptIn(ExperimentalForeignApi::class)
 public fun WebRtcPeerConnection.getNative(): RTCPeerConnection {
-    return (this as IosWebRtcConnection).peerConnection
+    val connection = this as IosWebRtcConnection
+    check(connection::peerConnection.isInitialized) {
+        "Peer connection must be initialized before accessing native implementation. Call initialize() first."
+    }
+    return connection.peerConnection
 }

Alternatively, if you prefer to let it fail naturally without the check (for performance reasons), at minimum enhance the documentation to warn users about the precondition.



---

🏁 Script executed:

```shell
#!/bin/bash
# Find IosWebRtcConnection class definition and peerConnection property
find . -type f -name "*.kt" | head -20

Length of output: 1355


🏁 Script executed:

#!/bin/bash
# Search for IosWebRtcConnection class
rg -n "class IosWebRtcConnection" --type=kt

Length of output: 26


🏁 Script executed:

#!/bin/bash
# Search for peerConnection property declaration
rg -n "peerConnection" --type=kt -A 2 -B 2 | head -50

Length of output: 26


🏁 Script executed:

#!/bin/bash
# Search for initialize function in WebRTC context
rg -n "fun initialize" --type=kt -C 3 | grep -A5 -B5 -i "webrtc\|peer"

Length of output: 26


🏁 Script executed:

#!/bin/bash
# Search for calls to getNative()
rg -n "getNative\(\)" --type=kt -B 3 -A 3

Length of output: 26


🏁 Script executed:

#!/bin/bash
# Look at the full PeerConnection.kt file structure
fd "PeerConnection.kt" --type=f

Length of output: 340


🏁 Script executed:

#!/bin/bash
# Check if there's initialization guarantee before getNative access
rg -n "class.*WebRtcPeerConnection\|lateinit.*peerConnection\|fun initialize" --type=kt -C 2

Length of output: 26


🏁 Script executed:

#!/bin/bash
# Read the iOS PeerConnection.kt file directly
cat ktor-client/ktor-client-webrtc/ios/src/io/ktor/client/webrtc/PeerConnection.kt

Length of output: 10734


🏁 Script executed:

#!/bin/bash
# Search for IosWebRtcConnection without type restriction
rg -n "class IosWebRtcConnection" 

Length of output: 115


🏁 Script executed:

#!/bin/bash
# Search for peerConnection property
rg -n "lateinit.*peerConnection\|val peerConnection\|var peerConnection" 

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Search for getNative usage
rg -n "getNative"

Length of output: 14308


🏁 Script executed:

#!/bin/bash
# Search for initialize() function in WebRTC files
rg -n "fun initialize\(" ktor-client/ktor-client-webrtc/

Length of output: 757


🏁 Script executed:

#!/bin/bash
# Read the common WebRtcPeerConnection interface
cat ktor-client/ktor-client-webrtc/common/src/io/ktor/client/webrtc/WebRtcPeerConnection.kt

Length of output: 4153


Enhance documentation and add initialization check for better robustness and clarity.

The review identifies two valid concerns in the getNative() function:

  1. Documentation gap: The current documentation violates Kotlin guidelines by not documenting exceptions or preconditions. The function should clarify that initialize() must be called first and document potential exceptions (UninitializedPropertyAccessException).

  2. Initialization risk: The function directly accesses the lateinit property without verification. This is inconsistent with the defensive pattern used elsewhere in the same class—for example, getStatistics() (line 33) checks if (!this::peerConnection.isInitialized) before access.

The suggested improvements are reasonable and align with Kotlin coding standards.

🤖 Prompt for AI Agents
In
ktor-client/ktor-client-webrtc/ios/src/io/ktor/client/webrtc/PeerConnection.kt
around lines 260 to 266, the getNative() function should be hardened and
documented: update the KDoc to state that initialize() must be called before use
and document that an UninitializedPropertyAccessException is thrown if the
underlying peerConnection is not initialized, then add a defensive runtime check
(e.g., if (!this::peerConnection.isInitialized) throw
UninitializedPropertyAccessException("peerConnection has not been initialized;
call initialize() first")) before returning peerConnection so behavior is
consistent with other methods like getStatistics().

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The user of the API should never see this Exception or call initialize().

Copy link
Contributor

Choose a reason for hiding this comment

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

@zibet27, thank you for the clarification! That's an important design detail.

If initialize() is called internally by the framework and users never need to invoke it directly, then:

  1. Should initialize() be internal instead of public? Currently it's a public method (line 48), which might confuse users about whether they need to call it.

  2. For the documentation: Instead of mentioning initialization requirements, we could simplify to focus on the actual usage:

/**
 * Returns the native RTCPeerConnection implementation for advanced use cases
 * requiring direct access to iOS WebRTC SDK APIs.
 *
 * @return the underlying [RTCPeerConnection] instance.
 * @throws ClassCastException if called on a non-iOS WebRtcPeerConnection implementation.
 */
@OptIn(ExperimentalForeignApi::class)
public fun WebRtcPeerConnection.getNative(): RTCPeerConnection {
    return (this as IosWebRtcConnection).peerConnection
}

Could you clarify:

  • Is initialize() guaranteed to be called before users can obtain a WebRtcPeerConnection instance?
  • Should initialize() visibility be changed to internal?

This will help ensure the documentation accurately reflects the intended API usage pattern.

@zibet27 zibet27 merged commit dab3e88 into main Oct 20, 2025
21 of 23 checks passed
@zibet27 zibet27 deleted the zibet27/webrtc-client-expose-core-dependancies branch October 20, 2025 08:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants