Skip to content

Conversation

@ArnabChatterjee20k
Copy link
Contributor

@ArnabChatterjee20k ArnabChatterjee20k commented Dec 18, 2025

What does this PR do?

Here is a code example for the databases channel builder with realtime subscription

Channel.database('db').collection('col').document('*')
=> "databases.db.collections.col.documents.*"

Channel.database('db').collection('*').document('*').delete()
=> "databases.db.collections.*.documents.*.delete"

Channel.documents
=> "documents"

// with realtime subscription
client.subscribe([
  Channel.account('user42'),

  Channel.buckets('assets').file('*').create(),

  Channel.database('crm').collection('customers').document('*').update(),

  Channel.tablesdb('analytics').table('events').row('*').delete(),

  Channel.function('mailer').execution('*')
], callback);

Test Plan

(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)

Related PRs and Issues

(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)

Have you read the Contributing Guidelines on issues?

(Write your answer here.)

Summary by CodeRabbit

  • New Features

    • Channel builder API added to Android, iOS, Flutter, React Native and Web SDKs and exported for public use.
  • API Changes

    • Realtime subscribe methods now accept Channel builder/value types in addition to strings across Web, Flutter, Android and iOS.
    • WebSocket onOpen/onClose signatures updated to use the unified channel type.
  • Tests

    • Added and extended Channel builder tests and updated test expectations across platforms.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 18, 2025

Walkthrough

Adds Channel builders and a Channel DSL across SDK generators and templates for Android (Kotlin), Apple (Swift), Flutter (Dart), Web (TypeScript), and React Native (TypeScript). New Channel template files and tests were added; client and realtime subscribe APIs were extended to accept Channel-like types with normalization helpers. Realtime subscription overloads and WebSocket delegate signatures (Swift NIO types) were updated. Test suites and many language-specific test expectations were extended via a new CHANNEL_HELPER_RESPONSES constant.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Added channel helpers for realtime in the client sdks' accurately describes the primary change—introducing channel builder helpers across multiple SDK language templates for realtime subscriptions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
tests/languages/web/node.js (1)

1-1: Critical: Channel not imported.

Channel is used extensively in lines 253-273 but is never imported from the SDK. This will cause runtime errors when the tests execute.

🔎 Apply this diff to add Channel to the imports:
-const { Client, Foo, Bar, General, Query, Permission, Role, ID, Operator, Condition, MockType } = require('./dist/cjs/sdk.js');
+const { Client, Foo, Bar, General, Query, Permission, Role, ID, Channel, Operator, Condition, MockType } = require('./dist/cjs/sdk.js');
tests/languages/node/test.js (1)

1-13: Critical: Channel not imported.

Channel is used in lines 329-349 but is not included in the import statement. This will cause runtime errors.

🔎 Apply this diff to add Channel to the imports:
 const {
     Client,
     Permission,
     Query,
     Role,
     ID,
+    Channel,
     Operator,
     Condition,
     MockType,
     Foo,
     Bar,
     General
 } = require('./dist/index.js');
tests/languages/web/index.html (1)

24-24: Critical: Channel not destructured from Appwrite.

Channel is used in lines 322-342 but is not included in the destructuring assignment. This will cause runtime errors.

🔎 Apply this diff to add Channel to the destructuring:
-            const { Client, Foo, Bar, General, Realtime, Query, Permission, Role, ID, Operator, Condition, MockType } = Appwrite;
+            const { Client, Foo, Bar, General, Realtime, Query, Permission, Role, ID, Channel, Operator, Condition, MockType } = Appwrite;
🧹 Nitpick comments (4)
templates/dart/lib/channel.dart.twig (1)

13-24: Consider using an enum for the action parameter for type safety.

The action parameter accepts any String?, but the documentation indicates only 'create', 'update', or 'delete' are valid. Consider defining an enum to enforce valid values at compile time:

🔎 Example enum approach
enum ChannelAction { create, update, delete }

static String database({
  String databaseId = '*',
  String collectionId = '*',
  String documentId = '*',
  ChannelAction? action,
}) {
  String channel = 'databases.$databaseId.collections.$collectionId.documents.$documentId';
  if (action != null) {
    channel += '.${action.name}';
  }
  return channel;
}

This would provide compile-time safety and IDE autocompletion. The current String approach is acceptable if flexibility for future action types is desired.

templates/android/library/src/main/java/io/package/Channel.kt.twig (2)

18-24: Consider using a Kotlin enum for the action parameter.

Similar to the Dart implementation, the action parameter accepts any String?. Kotlin supports enums that could provide compile-time safety:

🔎 Example enum approach
enum class ChannelAction {
    CREATE, UPDATE, DELETE;
    
    override fun toString(): String = name.lowercase()
}

fun database(
    databaseId: String = "*",
    collectionId: String = "*",
    documentId: String = "*",
    action: ChannelAction? = null
): String {
    var channel = "databases.$databaseId.collections.$collectionId.documents.$documentId"
    if (action != null) {
        channel += ".$action"
    }
    return channel
}

The current String approach maintains consistency with the Dart SDK if that's a design goal.


16-17: Minor: KDoc uses @return, not @returns.

The KDoc standard uses @return for documenting return values. @returns is JSDoc syntax. While most tools will handle it, consider using the correct KDoc syntax for consistency.

🔎 Apply this diff
-         * @returns The channel string
+         * @return The channel string

This applies to all methods in the class.

templates/react-native/src/channel.ts.twig (1)

1-110: Consider sharing this template with the Web SDK to reduce duplication.

This file is identical to templates/web/src/channel.ts.twig. Since both Web and React Native use TypeScript, consolidating to a single shared template would eliminate the need to maintain duplicate code.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ac31311 and 29471d1.

📒 Files selected for processing (22)
  • src/SDK/Language/Android.php (1 hunks)
  • src/SDK/Language/Apple.php (1 hunks)
  • src/SDK/Language/Dart.php (1 hunks)
  • src/SDK/Language/Flutter.php (2 hunks)
  • src/SDK/Language/ReactNative.php (1 hunks)
  • src/SDK/Language/Web.php (1 hunks)
  • templates/android/library/src/main/java/io/package/Channel.kt.twig (1 hunks)
  • templates/dart/lib/channel.dart.twig (1 hunks)
  • templates/dart/lib/package.dart.twig (1 hunks)
  • templates/dart/test/channel_test.dart.twig (1 hunks)
  • templates/react-native/src/channel.ts.twig (1 hunks)
  • templates/react-native/src/index.ts.twig (1 hunks)
  • templates/swift/Sources/Channel.swift.twig (1 hunks)
  • templates/web/src/channel.ts.twig (1 hunks)
  • templates/web/src/index.ts.twig (1 hunks)
  • tests/languages/android/Tests.kt (1 hunks)
  • tests/languages/apple/Tests.swift (1 hunks)
  • tests/languages/flutter/tests.dart (1 hunks)
  • tests/languages/node/test.js (1 hunks)
  • tests/languages/swift/Tests.swift (1 hunks)
  • tests/languages/web/index.html (1 hunks)
  • tests/languages/web/node.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/languages/android/Tests.kt (1)
tests/languages/kotlin/Tests.kt (1)
  • writeToFile (271-274)
⏰ 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). (18)
  • GitHub Check: build (8.3, AppleSwift56)
  • GitHub Check: build (8.3, Python312)
  • GitHub Check: build (8.3, Ruby31)
  • GitHub Check: build (8.3, Swift56)
  • GitHub Check: build (8.3, DartBeta)
  • GitHub Check: build (8.3, FlutterBeta)
  • GitHub Check: build (8.3, Android14Java17)
  • GitHub Check: build (8.3, KotlinJava17)
  • GitHub Check: build (8.3, Android5Java17)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: swift (server)
  • GitHub Check: python (server)
  • GitHub Check: react-native (client)
  • GitHub Check: flutter (client)
  • GitHub Check: apple (client)
  • GitHub Check: dotnet (server)
  • GitHub Check: kotlin (server)
  • GitHub Check: android (client)
🔇 Additional comments (18)
tests/languages/web/node.js (1)

253-273: LGTM! Comprehensive Channel helper test coverage.

The test coverage for Channel helpers is thorough, testing all methods with default parameters, specific IDs, and optional actions.

tests/languages/node/test.js (1)

329-349: LGTM! Comprehensive test coverage.

The Channel helper tests are well-structured and cover all the required methods with various parameter combinations.

tests/languages/web/index.html (1)

322-342: LGTM! Thorough test coverage.

The Channel helper tests are comprehensive and follow the established testing patterns.

templates/dart/test/channel_test.dart.twig (1)

1-111: LGTM! Excellent test template implementation.

The test template is well-structured with:

  • Proper conditional logic for Dart vs Flutter test packages
  • Comprehensive coverage of all Channel methods
  • Multiple test cases per method (defaults, specific IDs, actions)
  • Correct use of Dart/Flutter testing conventions
src/SDK/Language/Web.php (1)

83-87: LGTM!

The channel.ts file generation entry is correctly configured. The template file templates/web/src/channel.ts.twig exists and Channel is properly exported in templates/web/src/index.ts.twig.

tests/languages/flutter/tests.dart (1)

250-270: LGTM! Comprehensive Channel helper test coverage.

The tests exercise all Channel helper methods with various parameter combinations (default wildcards, specific IDs, and action suffixes), consistent with the existing test patterns in this file for Query, Permission, and Role helpers.

templates/dart/lib/channel.dart.twig (1)

45-50: Account helper correctly omits action parameter.

Unlike other channel types, the account channel appropriately excludes the action parameter, which aligns with how account subscriptions work in Appwrite realtime.

src/SDK/Language/Flutter.php (2)

83-87: LGTM! Channel file generation configuration.

The channel.dart generation entry follows the established pattern for other helper files (id.dart, permission.dart, role.dart, query.dart, operator.dart).


298-302: LGTM! Channel test file generation configuration.

Test file generation entry follows the same pattern as existing test entries and correctly references the shared Dart test template.

templates/web/src/channel.ts.twig (1)

14-20: Good use of TypeScript union type for action parameter.

The union type 'create' | 'update' | 'delete' | null provides compile-time type safety, which is better than the untyped String approach in Dart. This is a good pattern.

templates/web/src/index.ts.twig (1)

18-18: LGTM! Channel export added to public API.

The Channel class is correctly exported, making it accessible to SDK consumers.

templates/react-native/src/index.ts.twig (1)

11-11: LGTM!

The Channel export follows the existing pattern and is correctly positioned among other helper utilities.

src/SDK/Language/ReactNative.php (1)

63-67: LGTM!

The Channel file generation entry follows the established pattern for other helper utilities and is correctly positioned in the file list.

src/SDK/Language/Apple.php (1)

73-77: LGTM!

The Channel.swift generation entry is consistent with other Swift helper file entries and correctly positioned.

tests/languages/swift/Tests.swift (1)

238-259: Comprehensive test coverage!

The Channel helper tests are well-structured and cover all methods with various parameter combinations:

  • Default parameters (wildcards)
  • Specific IDs
  • Optional action parameters

The tests follow the existing pattern in the test suite.

src/SDK/Language/Android.php (1)

123-127: LGTM!

The Channel.kt generation entry is consistent with other Android helper file entries and correctly positioned in the file list.

tests/languages/android/Tests.kt (1)

272-293: Excellent test coverage!

The Channel helper tests comprehensively cover all methods with various parameter combinations and match the test coverage in other language implementations. The tests follow the existing Android test pattern using writeToFile().

templates/swift/Sources/Channel.swift.twig (1)

1-108: Well-implemented Channel helper with comprehensive documentation and consistent cross-language implementation.

The implementation is clean and follows consistent patterns across all seven methods. Each method correctly builds channel strings using string interpolation and conditionally appends the optional action parameter, with support for both the traditional documents/collections terminology and the new relational model with tables/rows. All language implementations (Swift, Kotlin, TypeScript for React-Native and Web, and Dart) have identical method signatures and channel string formats, confirming consistency across the SDK generator templates.

- Updated Apple and Dart SDKs to include Channel templates.
- Added Channel class in Swift template for Apple SDK.
- Included Channel in Dart SDK template and test files.
- Updated test cases across various platforms to incorporate Channel responses.
- Modified web and node.js files to import Channel functionality.
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

🧹 Nitpick comments (1)
templates/apple/Sources/Channel.swift.twig (1)

1-107: Consider optional action parameter validation.

The documentation suggests that the action parameter should be "create", "update", or "delete", but the implementation accepts any string. While this provides flexibility, you might consider adding validation to ensure only valid actions are used, especially if the realtime API rejects invalid action values.

Example validation approach:

You could add an enum for valid actions:

public enum ChannelAction: String {
    case create = "create"
    case update = "update"
    case delete = "delete"
}

Then update method signatures to accept ChannelAction? instead of String?:

public static func database(databaseId: String = "*", collectionId: String = "*", documentId: String = "*", action: ChannelAction? = nil) -> String {
    var channel = "databases.\(databaseId).collections.\(collectionId).documents.\(documentId)"
    if let action = action {
        channel += ".\(action.rawValue)"
    }
    return channel
}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 29471d1 and 5d19169.

📒 Files selected for processing (19)
  • src/SDK/Language/Apple.php (1 hunks)
  • src/SDK/Language/Dart.php (2 hunks)
  • templates/apple/Sources/Channel.swift.twig (1 hunks)
  • templates/dart/test/channel_test.dart.twig (1 hunks)
  • templates/flutter/lib/package.dart.twig (1 hunks)
  • tests/Android14Java11Test.php (1 hunks)
  • tests/Android14Java17Test.php (1 hunks)
  • tests/Android14Java8Test.php (1 hunks)
  • tests/Android5Java17Test.php (1 hunks)
  • tests/AppleSwift56Test.php (1 hunks)
  • tests/Base.php (1 hunks)
  • tests/FlutterBetaTest.php (1 hunks)
  • tests/FlutterStableTest.php (1 hunks)
  • tests/WebChromiumTest.php (1 hunks)
  • tests/WebNodeTest.php (1 hunks)
  • tests/languages/android/Tests.kt (2 hunks)
  • tests/languages/node/test.js (2 hunks)
  • tests/languages/web/index.html (2 hunks)
  • tests/languages/web/node.js (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/languages/web/node.js
  • src/SDK/Language/Dart.php
  • templates/dart/test/channel_test.dart.twig
🧰 Additional context used
🧬 Code graph analysis (9)
tests/WebChromiumTest.php (1)
tests/Base.php (1)
  • Base (17-349)
tests/Android14Java17Test.php (1)
tests/Base.php (1)
  • Base (17-349)
tests/AppleSwift56Test.php (1)
tests/Base.php (1)
  • Base (17-349)
tests/languages/android/Tests.kt (1)
tests/languages/kotlin/Tests.kt (1)
  • writeToFile (271-274)
tests/FlutterBetaTest.php (1)
tests/Base.php (1)
  • Base (17-349)
tests/WebNodeTest.php (1)
tests/Base.php (1)
  • Base (17-349)
tests/Android5Java17Test.php (1)
tests/Base.php (1)
  • Base (17-349)
tests/Android14Java8Test.php (1)
tests/Base.php (1)
  • Base (17-349)
tests/FlutterStableTest.php (1)
tests/Base.php (1)
  • Base (17-349)
⏰ 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). (20)
  • GitHub Check: build (8.3, Node18)
  • GitHub Check: build (8.3, Python39)
  • GitHub Check: build (8.3, Swift56)
  • GitHub Check: build (8.3, FlutterBeta)
  • GitHub Check: build (8.3, Python312)
  • GitHub Check: build (8.3, WebChromium)
  • GitHub Check: build (8.3, Python311)
  • GitHub Check: build (8.3, AppleSwift56)
  • GitHub Check: build (8.3, PHP83)
  • GitHub Check: build (8.3, KotlinJava8)
  • GitHub Check: build (8.3, PHP80)
  • GitHub Check: build (8.3, KotlinJava17)
  • GitHub Check: build (8.3, CLINode16)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: build (8.3, DartStable)
  • GitHub Check: build (8.3, Android5Java17)
  • GitHub Check: build (8.3, Android14Java17)
  • GitHub Check: swift (server)
  • GitHub Check: apple (client)
  • GitHub Check: android (client)
🔇 Additional comments (24)
tests/languages/node/test.js (2)

7-7: LGTM!

The Channel import is correctly added to the destructured require statement.


330-350: Comprehensive test coverage for Channel helpers.

The test calls cover all seven Channel helper methods with appropriate parameter variations (defaults, specific IDs, and actions). The test patterns align with the expected responses defined in Base.php.

templates/apple/Sources/Channel.swift.twig (7)

11-17: LGTM!

The database channel helper correctly constructs the channel string with proper parameter defaults and optional action handling.


28-34: LGTM!

The tablesdb channel helper follows the same pattern as database() and correctly constructs the channel string for table-based operations.


42-44: LGTM!

The account channel helper is correctly implemented with appropriate simplicity for account-based subscriptions.


54-60: LGTM!

The files channel helper correctly constructs the channel string for bucket/file operations with proper action handling.


70-76: LGTM!

The executions channel helper correctly constructs the channel string for function execution subscriptions.


85-91: LGTM!

The teams channel helper correctly constructs the channel string with appropriate simplicity and optional action handling.


100-106: LGTM!

The memberships channel helper is correctly implemented and follows the established pattern.

tests/Android5Java17Test.php (1)

35-35: LGTM!

The expectedOutput array is correctly extended to include Channel helper responses, aligning with the new Channel functionality across the SDK.

tests/Android14Java11Test.php (1)

36-36: LGTM!

The expectedOutput array is correctly extended to include Channel helper responses, consistent with the test data expansion across all language test suites.

tests/Android14Java17Test.php (1)

35-35: LGTM!

The expectedOutput array is correctly extended to include Channel helper responses, maintaining consistency with other test files in the PR.

tests/FlutterBetaTest.php (1)

34-34: LGTM!

The expectedOutput array is correctly extended to include Channel helper responses, properly aligned with the Flutter SDK's Channel helper implementation.

tests/Android14Java8Test.php (1)

36-36: LGTM!

The expectedOutput array is correctly extended to include Channel helper responses, consistent with the test expectations across all Android SDK variants.

tests/WebNodeTest.php (1)

38-38: LGTM!

The expectedOutput array is correctly extended to include Channel helper responses, completing the test coverage expansion for the Web SDK.

tests/AppleSwift56Test.php (1)

34-34: LGTM!

The addition of CHANNEL_HELPER_RESPONSES to the expected output is consistent with the broader PR changes adding Channel helpers across SDK languages. The placement between ID and Operator helper responses maintains a logical test ordering.

src/SDK/Language/Apple.php (1)

73-77: LGTM!

The Channel.swift file entry is correctly structured and positioned logically in the manifest. The use of the apple/Sources/Channel.swift.twig template path aligns with the Apple-specific implementation strategy.

templates/flutter/lib/package.dart.twig (1)

33-33: LGTM!

The part 'channel.dart'; directive is correctly positioned and follows proper Dart syntax for library part declarations.

tests/Base.php (1)

166-187: LGTM!

The CHANNEL_HELPER_RESPONSES constant provides comprehensive test coverage for all Channel helper methods. The patterns are well-structured with:

  • Wildcard patterns for defaults (e.g., databases.*.collections.*.documents.*)
  • Specific ID patterns (e.g., databases.db1.collections.col1.documents.doc1)
  • Action-specific patterns covering create, update, and delete operations

The inclusion of "tablesdb" patterns alongside "databases" suggests backwards compatibility support, which is appropriate.

tests/languages/android/Tests.kt (1)

10-10: LGTM!

The Channel import is correctly added alongside other helper imports (Permission, Role, ID, Query, Operator).

tests/WebChromiumTest.php (1)

38-38: LGTM!

The addition of CHANNEL_HELPER_RESPONSES maintains consistency with other test files and is positioned correctly in the expected output sequence.

tests/FlutterStableTest.php (1)

34-34: LGTM!

The addition of CHANNEL_HELPER_RESPONSES follows the same pattern as other test files and maintains proper ordering in the expected output array.

tests/languages/web/index.html (2)

24-24: LGTM!

Channel is correctly added to the destructured imports, positioned logically alongside other helper classes (Query, Permission, Role, ID, Operator).


322-342: LGTM! Excellent cross-platform test consistency.

The Channel helper tests mirror the Android implementation exactly, testing all seven methods with identical arguments:

  • database, tablesdb, account, files, executions, teams, memberships
  • Each with default args, specific IDs, and action-suffixed variants

The test structure demonstrates excellent consistency across SDK platforms, which will make maintenance easier and ensure uniform behavior across client libraries.

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: 2

🧹 Nitpick comments (2)
templates/apple/Sources/Channel.swift.twig (2)

13-19: Consider validating the action parameter.

The documentation specifies that action should be "create", "update", or "delete", but there's no validation to enforce this. While the server likely validates channel strings, client-side validation would provide better developer experience by failing fast with clear error messages rather than silently creating invalid subscriptions.

🔎 View suggested validation approach:

You could add validation in each method, for example in the database method:

 public static func database(databaseId: String = "*", collectionId: String = "*", documentId: String = "*", action: String? = nil) -> String {
+    if let action = action, !["create", "update", "delete"].contains(action) {
+        preconditionFailure("Invalid action '\(action)'. Must be 'create', 'update', or 'delete'.")
+    }
     var channel = "databases.\(databaseId).collections.\(collectionId).documents.\(documentId)"
     if let action = action {
         channel += ".\(action)"
     }
     return channel
 }

Apply similar validation to the other methods with action parameters.

Also applies to: 30-36, 56-62, 72-78, 87-93, 102-108


4-12: Consider using Swift-standard documentation syntax.

The documentation uses @param and @returns, which are not standard Swift documentation markers. Swift documentation typically uses - Parameter (or - Parameters:) and - Returns: for better integration with Xcode and documentation generation tools like jazzy.

🔎 Example with Swift-standard syntax:
 /**
  * Generate a database channel string.
  *
- * @param databaseId The database ID (default: "*")
- * @param collectionId The collection ID (default: "*")
- * @param documentId The document ID (default: "*")
- * @param action Optional action: "create", "update", or "delete" (default: nil)
- * @returns The channel string
+ * - Parameter databaseId: The database ID (default: "*")
+ * - Parameter collectionId: The collection ID (default: "*")
+ * - Parameter documentId: The document ID (default: "*")
+ * - Parameter action: Optional action: "create", "update", or "delete" (default: nil)
+ * - Returns: The channel string
  */

Also applies to: 21-29, 38-43, 48-55, 64-71, 80-86, 95-101

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5d19169 and b18671f.

📒 Files selected for processing (3)
  • templates/apple/Sources/Channel.swift.twig (1 hunks)
  • templates/swift/Sources/WebSockets/WebSocketClient.swift.twig (7 hunks)
  • templates/swift/Sources/WebSockets/WebSocketClientDelegate.swift.twig (1 hunks)
⏰ 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). (19)
  • GitHub Check: build (8.3, Swift56)
  • GitHub Check: build (8.3, Ruby31)
  • GitHub Check: build (8.3, WebChromium)
  • GitHub Check: build (8.3, WebNode)
  • GitHub Check: apple (client)
  • GitHub Check: build (8.3, AppleSwift56)
  • GitHub Check: build (8.3, Ruby27)
  • GitHub Check: build (8.3, Python311)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: kotlin (server)
  • GitHub Check: build (8.3, KotlinJava8)
  • GitHub Check: build (8.3, Node20)
  • GitHub Check: android (client)
  • GitHub Check: build (8.3, DotNet80)
  • GitHub Check: build (8.3, Android14Java17)
  • GitHub Check: swift (server)
  • GitHub Check: build (8.3, CLINode18)
  • GitHub Check: build (8.3, Android5Java17)
  • GitHub Check: build (8.3, KotlinJava11)
🔇 Additional comments (4)
templates/swift/Sources/WebSockets/WebSocketClient.swift.twig (3)

35-43: Property type qualification is correct and thread-safe.

The channel property and its backing field _channel have been consistently updated to use NIOCore.Channel?. The synchronized access pattern using channelQueue ensures thread safety.


64-90: Callback storage updates are consistent and thread-safe.

The callback storage for onOpen and onClose has been properly updated to use NIOCore.Channel in their function signatures. The synchronization using the locker queue ensures thread-safe access to the callback storage.


265-265: Internal method signatures correctly updated.

The private methods openChannel and upgradePipelineHandler have been updated to use NIOCore.Channel, maintaining internal consistency with the property types and callback signatures. The delegate call at line 302 will work correctly since the protocol has been updated accordingly.

Also applies to: 293-293

templates/swift/Sources/WebSockets/WebSocketClientDelegate.swift.twig (1)

7-7: Consistent namespace qualification to prevent naming conflicts.

The refactoring from Channel to NIOCore.Channel is consistent across the protocol declaration and default extension implementations. NIO is an umbrella module exporting NIOCore, NIOEmbedded and NIOPosix, so NIOCore.Channel is accessible via the existing import NIO on line 2. All four method signatures (lines 7, 10, 16, 22) are properly updated. This is a breaking change to the public API but necessary for avoiding type name collisions with new Channel helpers for realtime subscriptions.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
templates/apple/Sources/Services/Realtime.swift.twig (1)

254-254: Qualify Channel parameter as NIOCore.Channel in delegate method to avoid naming conflict.

The onOpen(channel: Channel) and onClose(channel: Channel, ...) delegate methods in Realtime.swift.twig (line 254+) use an unqualified Channel type. Since Channel.swift.twig defines a new Channel helper class for subscription channels, this creates ambiguity. The delegate parameter should explicitly use NIOCore.Channel to match the pattern in WebSocketClient.swift.twig (which consistently qualifies all Channel references as NIOCore.Channel), ensuring the correct network channel type is referenced and avoiding resolution to the subscription helper class.

🧹 Nitpick comments (10)
tests/Base.php (3)

283-290: Consider removing verbose debug logging before merging.

The extensive [LOG] statements throughout this method appear to be debug instrumentation. If these were added to troubleshoot specific CI issues, consider removing them before merging to keep test output clean, or wrap them in a conditional flag (e.g., $verbose or environment variable check) if you want them available on-demand.


295-322: Duplicate build directory cleanup logic.

The build directory is cleaned twice with identical logic (lines 295-300 and 317-322). If both cleanups are truly necessary to avoid PCH cache issues, consider extracting this into a helper method to reduce duplication.

🔎 Proposed refactor
+    private function cleanBuildDirectory(string $context = ''): void
+    {
+        $buildDir = __DIR__ . '/sdks/' . $this->language . '/.build';
+        if (is_dir($buildDir)) {
+            echo "[LOG] Cleaning build directory{$context}: {$buildDir}\n";
+            $this->rmdirRecursive($buildDir);
+        }
+    }

Then replace both cleanup blocks with:

$this->cleanBuildDirectory();
// ... build commands ...
$this->cleanBuildDirectory(' before test');

364-366: Minor: Use count() for consistency.

The file uses count() everywhere else (lines 332, 341, 354, 363, 370, 371, 405), but sizeof() here. While functionally equivalent, prefer count() for consistency.

-    } while ($removed != 'Test Started' && sizeof($output) != 0);
+    } while ($removed != 'Test Started' && count($output) != 0);
templates/flutter/lib/src/realtime_mixin.dart.twig (1)

171-177: Consider documenting expected channel types.

The _channelToString helper correctly converts channel values to strings. However, since the parameter is Object, any value could be passed at runtime (in Dart, type checking is not always enforced). Consider adding a doc comment explaining that channels should be String or implement toString() meaningfully.

💡 Optional: Add documentation
+  /// Convert channel value to string.
+  /// Accepts String or Channel builder instances that implement toString().
   String _channelToString(Object channel) {
     if (channel is String) {
       return channel;
     }
     return channel.toString();
   }
templates/web/src/services/realtime.ts.twig (2)

248-257: Simplify the channelToString helper.

The toString method exists on all JavaScript objects, making the explicit check on lines 253-254 and the fallback on line 256 redundant. The helper can be simplified.

🔎 Suggested simplification
     private channelToString(channel: string | ChannelValue): string {
         if (typeof channel === 'string') {
             return channel;
         }
-        // Channel builder instances have toString() method
-        if (channel && typeof channel.toString === 'function') {
-            return channel.toString();
-        }
-        return String(channel);
+        return channel.toString();
     }

266-269: Type signature mismatch with JSDoc.

The JSDoc on line 262 states the parameter accepts "string or Channel builder instance", but the type signature only allows ChannelValue. For consistency with the array overloads and the documentation, consider accepting string | ChannelValue.

🔎 Proposed fix
     public async subscribe(
-        channel: ChannelValue,
+        channel: string | ChannelValue,
         callback: (event: RealtimeResponseEvent<any>) => void
     ): Promise<RealtimeSubscription>;

Apply the same change to the generic single-channel overload on line 291:

     public async subscribe<T>(
-        channel: ChannelValue,
+        channel: string | ChannelValue,
         callback: (event: RealtimeResponseEvent<T>) => void
     ): Promise<RealtimeSubscription>;
templates/android/library/src/main/java/io/package/Channel.kt.twig (1)

179-188: Consider consistency: account returns String while other methods return channel builders.

The account() method returns a String directly, unlike other companion methods which return channel builder objects. This works but creates an inconsistent API surface. If intentional (since account has no sub-resources), consider adding a brief doc note explaining this difference.

templates/apple/Sources/Channel.swift.twig (2)

46-64: Consider removing unused stored properties.

The databaseId and collectionId properties are stored but only used during initialization to construct base. If they're not needed for future extensions, they could be removed to reduce memory footprint.

🔎 Proposed refactor
 public class CollectionChannel {
     private let base: String
-    private let databaseId: String
-    private let collectionId: String
     
     init(databaseId: String, collectionId: String) {
-        self.databaseId = databaseId
-        self.collectionId = collectionId
         self.base = "databases.\(databaseId).collections.\(collectionId)"
     }

192-200: Consider using Swift-style documentation comments.

The current documentation uses Java-style @param/@returns annotations. Swift convention uses - Parameter: and - Returns: with triple-slash comments.

🔎 Swift-idiomatic documentation example
-    /**
-     * Generate a database channel builder.
-     *
-     * @param databaseId The database ID (default: "*")
-     * @returns DatabaseChannel
-     */
+    /// Generate a database channel builder.
+    ///
+    /// - Parameter databaseId: The database ID (default: "*")
+    /// - Returns: A `DatabaseChannel` instance
     public static func database(_ databaseId: String = "*") -> DatabaseChannel {
templates/flutter/lib/channel.dart.twig (1)

181-181: Consider if ChannelValue type alias provides sufficient type safety.

The typedef ChannelValue = Object is very broad and doesn't constrain what can be passed. While Dart lacks TypeScript's union types, you could consider a marker interface or sealed class if stronger type safety is desired. However, this matches the SDK's lightweight helper philosophy.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b18671f and 693fe53.

📒 Files selected for processing (20)
  • src/SDK/Language/Flutter.php (2 hunks)
  • templates/android/library/src/main/java/io/package/Channel.kt.twig (1 hunks)
  • templates/android/library/src/main/java/io/package/services/Realtime.kt.twig (2 hunks)
  • templates/apple/Sources/Channel.swift.twig (1 hunks)
  • templates/apple/Sources/Services/Realtime.swift.twig (3 hunks)
  • templates/flutter/lib/channel.dart.twig (1 hunks)
  • templates/flutter/lib/src/realtime.dart.twig (1 hunks)
  • templates/flutter/lib/src/realtime_base.dart.twig (1 hunks)
  • templates/flutter/lib/src/realtime_browser.dart.twig (1 hunks)
  • templates/flutter/lib/src/realtime_io.dart.twig (1 hunks)
  • templates/flutter/lib/src/realtime_mixin.dart.twig (1 hunks)
  • templates/web/src/channel.ts.twig (1 hunks)
  • templates/web/src/client.ts.twig (3 hunks)
  • templates/web/src/services/realtime.ts.twig (2 hunks)
  • tests/Base.php (2 hunks)
  • tests/languages/android/Tests.kt (2 hunks)
  • tests/languages/apple/Tests.swift (1 hunks)
  • tests/languages/flutter/tests.dart (1 hunks)
  • tests/languages/web/index.html (2 hunks)
  • tests/languages/web/node.js (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/languages/web/index.html
  • src/SDK/Language/Flutter.php
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:30.593Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:14.446Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.
📚 Learning: 2025-12-18T18:42:14.446Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:14.446Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.

Applied to files:

  • templates/flutter/lib/src/realtime_io.dart.twig
  • templates/flutter/lib/src/realtime_mixin.dart.twig
  • templates/web/src/client.ts.twig
  • templates/android/library/src/main/java/io/package/services/Realtime.kt.twig
  • templates/flutter/lib/src/realtime_base.dart.twig
  • templates/flutter/lib/src/realtime.dart.twig
  • templates/apple/Sources/Channel.swift.twig
  • tests/languages/apple/Tests.swift
  • templates/flutter/lib/channel.dart.twig
  • templates/flutter/lib/src/realtime_browser.dart.twig
  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/apple/Sources/Services/Realtime.swift.twig
  • templates/web/src/channel.ts.twig
  • templates/web/src/services/realtime.ts.twig
📚 Learning: 2025-12-18T18:43:30.593Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:30.593Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.

Applied to files:

  • tests/languages/web/node.js
  • tests/languages/flutter/tests.dart
  • templates/apple/Sources/Channel.swift.twig
  • tests/languages/apple/Tests.swift
  • templates/flutter/lib/channel.dart.twig
  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/web/src/channel.ts.twig
  • tests/languages/android/Tests.kt
🧬 Code graph analysis (1)
tests/languages/android/Tests.kt (1)
tests/languages/kotlin/Tests.kt (1)
  • writeToFile (271-274)
🔇 Additional comments (36)
tests/languages/android/Tests.kt (2)

10-10: LGTM!

The import for the new Channel helper class is correctly added alongside the existing helper imports.


273-293: Comprehensive Channel helper test coverage.

The 20 test cases systematically exercise all Channel helper methods with appropriate variations:

  • Default wildcards (e.g., Channel.database().collection().document())
  • Specific IDs (e.g., Channel.database("db1").collection("col1").document("doc1"))
  • Action methods (e.g., .create(), .update(), .delete())

The fluent builder API is well-tested across database, tablesdb, buckets, functions, teams, and memberships, while account correctly returns a String directly. Based on learnings, the design intentionally accepts IDs without validation, consistent with other SDK helpers like Query.

tests/Base.php (1)

166-188: LGTM!

The CHANNEL_HELPER_RESPONSES constant is well-structured with 20 entries covering all the channel helper patterns (databases, tables, buckets, functions, teams, memberships, account). The patterns follow a consistent naming convention and include variations with wildcards, specific IDs, and action suffixes.

templates/flutter/lib/src/realtime_io.dart.twig (1)

46-48: LGTM! Signature broadened to support Channel builders.

The method signature correctly accepts List<Object> to support both string channels and Channel builder instances, delegating conversion logic to the mixin.

tests/languages/flutter/tests.dart (1)

250-271: LGTM! Comprehensive Channel helper test coverage.

The tests appropriately exercise the Channel builder API across all resource types (database, tablesdb, account, buckets, functions, teams, memberships) with various parameter combinations and action modifiers.

templates/flutter/lib/src/realtime_browser.dart.twig (1)

38-40: LGTM! Consistent signature update for browser platform.

The signature change matches the IO platform implementation, correctly delegating to the mixin for channel conversion.

templates/flutter/lib/src/realtime_base.dart.twig (1)

6-6: LGTM! Abstract contract correctly broadened.

The abstract method signature properly defines the contract for platform implementations to accept both string and Channel builder types.

tests/languages/apple/Tests.swift (1)

251-271: LGTM! Channel helper tests for Apple SDK.

The tests comprehensively exercise the Channel builder API with appropriate coverage of all resource types and action variants. The previous concern about Channel class availability was addressed in commit 3f13cb1.

templates/flutter/lib/src/realtime.dart.twig (1)

45-53: LGTM! Clear documentation with practical Channel builder examples.

The updated signature and documentation effectively demonstrate how to use Channel builders alongside traditional string channels. The examples are practical and show mixed usage patterns.

templates/web/src/client.ts.twig (1)

585-613: LGTM! Robust channel type normalization logic.

The implementation correctly handles the union type string | string[] | ChannelValue | ChannelValue[] by:

  • Normalizing to array form
  • Converting each channel to string via toString() method or String() fallback
  • Maintaining internal consistency with string-based channel storage

The defensive typeof ch.toString === 'function' check ensures safe handling of edge cases, and the documentation clearly demonstrates Channel builder usage.

templates/flutter/lib/src/realtime_mixin.dart.twig (1)

179-201: LGTM! Clean channel type normalization in subscription logic.

The implementation correctly:

  • Accepts List<Object> to support both strings and Channel builders
  • Normalizes all channels to strings using _channelToString
  • Maintains string-based internal storage for consistency
  • Uses channelStrings throughout for subscription tracking and cleanup
templates/web/src/services/realtime.ts.twig (1)

311-317: LGTM!

The implementation correctly normalizes input to an array, converts all channels to strings, and creates a Set for deduplication. The logic integrates well with the existing subscription management.

templates/android/library/src/main/java/io/package/Channel.kt.twig (3)

1-7: LGTM!

The ResolvedChannel wrapper is a clean approach for representing finalized channel strings with action suffixes.


9-25: LGTM!

The ActionChannel abstract class provides a clean abstraction for channels that support CRUD action suffixes. The pattern correctly separates the base channel path from action qualifiers.


27-54: LGTM!

The database channel hierarchy correctly models the Appwrite resource structure with a fluent API: Channel.database().collection().document().

tests/languages/web/node.js (2)

1-1: LGTM!

The Channel import is correctly added alongside other helper classes.


253-273: LGTM!

Comprehensive test coverage for the Channel helper API. Tests verify:

  • Default wildcard parameters
  • Specific ID parameters
  • Nested path construction (database → collection → document)
  • Action methods (create, update, delete)
  • All channel types (database, tablesdb, account, buckets, functions, teams, memberships)
templates/android/library/src/main/java/io/package/services/Realtime.kt.twig (2)

112-120: LGTM!

The channelToString helper is clean and idiomatic Kotlin. Using a when expression provides clear handling for String passthrough and fallback to toString() for other types.


122-150: LGTM!

The new overloads correctly extend the API to accept channel builder objects while maintaining backward compatibility with existing String-based signatures. The delegation pattern keeps the implementation DRY.

templates/apple/Sources/Services/Realtime.swift.twig (3)

6-17: LGTM!

The ChannelValue protocol is a clean Swift idiom for abstracting channel types. Extending String to conform allows seamless backward compatibility with existing string-based API usage.


147-198: LGTM!

The subscribe method overloads are well-structured:

  • channelToString helper encapsulates the conversion
  • Public API accepts ChannelValue for flexibility
  • Internal implementation uses Set<String> for deduplication
  • All overloads correctly delegate to the core implementation

19-31: No action required. All channel types shown in the extensions already implement the toString() -> String method in Channel.swift.twig, confirming the empty extensions to ChannelValue protocol are valid.

templates/apple/Sources/Channel.swift.twig (4)

1-37: LGTM!

The ResolvedChannel wrapper and ActionChannel base class are well-structured. The use of open class for ActionChannel appropriately allows subclassing, and the fluent create()/update()/delete() methods provide a clean API.


84-127: LGTM!

The TablesDB hierarchy follows the same consistent builder pattern as the Database hierarchy. The implementation is correct and enables fluent channel construction like Channel.tablesdb("db").table("tbl").row("*").create().


129-177: LGTM!

The Buckets and Functions hierarchies are correctly implemented following the established builder pattern.


179-189: LGTM!

Simple and correct implementation for team and membership channels as leaf-level builders.

templates/flutter/lib/channel.dart.twig (4)

1-34: LGTM!

Clean Dart implementation of the base channel classes. Using abstract class for ActionChannel is appropriate, and the @override annotations on toString() are correct since all Dart classes extend Object.


36-77: LGTM!

The Database → Collection → Document hierarchy is well-implemented using Dart's initializer list syntax. The builder pattern enables fluent channel construction.


79-170: LGTM!

Consistent implementation across all resource hierarchies (TablesDB, Buckets, Functions) following the established builder pattern.


183-239: LGTM!

The Channel facade is well-implemented with proper Dart-style documentation comments. The static factory methods provide a clean public API for channel construction.

templates/web/src/channel.ts.twig (6)

1-37: LGTM!

The base channel classes are well-implemented. ResolvedChannel is appropriately exported while ActionChannel remains internal. The empty lines at the start of the file (lines 1-2) appear intentional for template rendering.


39-81: LGTM!

Good use of TypeScript's parameter property shorthand (private databaseId: string). The non-exported classes provide proper encapsulation, forcing users to access them through the Channel facade.


83-175: LGTM!

Consistent implementation across TablesDB, Buckets, and Functions hierarchies. The builder pattern enables fluent construction like Channel.tablesdb('db').table('tb').row('*').create().


177-187: LGTM!

Simple and correct implementation for team and membership channels.


189-194: LGTM!

The ChannelValue union type provides excellent compile-time type safety, allowing consumers to use any channel type or raw strings.


196-266: LGTM!

The Channel facade provides a clean public API with well-documented static factory methods. The builder pattern implementation addresses the style requested in previous reviews, enabling fluent construction like Channel.database('db').collection('col').document('doc').create().

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 (3)
templates/flutter/lib/src/realtime_mixin.dart.twig (1)

179-201: Type broadening implemented correctly.

The changes successfully broaden the API to accept List<Object> channels and convert them consistently to strings for internal processing. The conversion is done upfront and applied uniformly throughout the subscription lifecycle.

Optional: Remove redundant cast on line 181.

Since _channelToString returns String, the .map() operation already produces an Iterable<String>, and .toList() yields a List<String>. The .cast<String>() call is redundant.

🔎 Suggested simplification
-    final channelStrings = channels.map((ch) => _channelToString(ch)).toList().cast<String>();
+    final channelStrings = channels.map((ch) => _channelToString(ch)).toList();
templates/apple/Sources/Services/Realtime.swift.twig (1)

147-152: Consider inlining or removing this helper.

The channelToString method is a trivial wrapper around channel.toString(). It could be inlined at call sites (e.g., channels.map { $0.toString() }), reducing indirection. However, keeping it is acceptable if you prefer a single conversion point for maintainability.

templates/web/src/client.ts.twig (1)

556-612: Well-implemented ChannelValue support in subscribe method.

The implementation properly extends the subscribe method to accept both string and ChannelValue types, with clean normalization logic that converts Channel builder instances to strings via toString(). The fallback handling and documentation are appropriate.

💡 Optional: Extract channel normalization helper

The channel normalization logic (lines 588-596) could be extracted to a private helper method similar to the pattern used in templates/web/src/services/realtime.ts.twig (lines 248-257). This would improve consistency and reusability, though the current inline implementation is perfectly acceptable.

+    private channelToString(channel: string | ChannelValue): string {
+        if (typeof channel === 'string') {
+            return channel;
+        }
+        if (channel && typeof channel.toString === 'function') {
+            return channel.toString();
+        }
+        return String(channel);
+    }
+
     subscribe<T extends unknown>(channels: string | string[] | ChannelValue | ChannelValue[], callback: (payload: RealtimeResponseEvent<T>) => void): () => void {
         const channelArray = Array.isArray(channels) ? channels : [channels];
-        // Convert Channel instances to strings
-        const channelStrings = channelArray.map(ch => {
-            if (typeof ch === 'string') {
-                return ch;
-            }
-            // Channel builder instances have toString() method
-            if (ch && typeof ch.toString === 'function') {
-                return ch.toString();
-            }
-            return String(ch);
-        });
+        const channelStrings = channelArray.map(ch => this.channelToString(ch));
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 693fe53 and fd57b65.

📒 Files selected for processing (19)
  • src/SDK/Language/Flutter.php (2 hunks)
  • templates/android/library/src/main/java/io/package/Channel.kt.twig (1 hunks)
  • templates/android/library/src/main/java/io/package/services/Realtime.kt.twig (2 hunks)
  • templates/apple/Sources/Channel.swift.twig (1 hunks)
  • templates/apple/Sources/Services/Realtime.swift.twig (3 hunks)
  • templates/flutter/lib/channel.dart.twig (1 hunks)
  • templates/flutter/lib/src/realtime.dart.twig (1 hunks)
  • templates/flutter/lib/src/realtime_base.dart.twig (1 hunks)
  • templates/flutter/lib/src/realtime_browser.dart.twig (1 hunks)
  • templates/flutter/lib/src/realtime_io.dart.twig (1 hunks)
  • templates/flutter/lib/src/realtime_mixin.dart.twig (1 hunks)
  • templates/web/src/channel.ts.twig (1 hunks)
  • templates/web/src/client.ts.twig (3 hunks)
  • templates/web/src/services/realtime.ts.twig (2 hunks)
  • tests/languages/android/Tests.kt (2 hunks)
  • tests/languages/apple/Tests.swift (1 hunks)
  • tests/languages/flutter/tests.dart (1 hunks)
  • tests/languages/web/index.html (2 hunks)
  • tests/languages/web/node.js (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • templates/flutter/lib/src/realtime_io.dart.twig
  • templates/flutter/lib/src/realtime_base.dart.twig
  • tests/languages/apple/Tests.swift
  • src/SDK/Language/Flutter.php
  • templates/web/src/channel.ts.twig
  • templates/flutter/lib/channel.dart.twig
  • templates/flutter/lib/src/realtime.dart.twig
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:30.593Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:14.446Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.
📚 Learning: 2025-12-18T18:42:14.446Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:14.446Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.

Applied to files:

  • templates/flutter/lib/src/realtime_browser.dart.twig
  • templates/web/src/client.ts.twig
  • templates/flutter/lib/src/realtime_mixin.dart.twig
  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/android/library/src/main/java/io/package/services/Realtime.kt.twig
  • templates/web/src/services/realtime.ts.twig
  • templates/apple/Sources/Services/Realtime.swift.twig
  • templates/apple/Sources/Channel.swift.twig
📚 Learning: 2025-12-18T18:43:30.593Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:30.593Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.

Applied to files:

  • tests/languages/flutter/tests.dart
  • tests/languages/web/node.js
  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • tests/languages/web/index.html
  • tests/languages/android/Tests.kt
  • templates/apple/Sources/Channel.swift.twig
🧬 Code graph analysis (1)
tests/languages/android/Tests.kt (1)
tests/languages/kotlin/Tests.kt (1)
  • writeToFile (271-274)
⏰ 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). (14)
  • GitHub Check: build (8.3, Swift56)
  • GitHub Check: build (8.3, Go118)
  • GitHub Check: build (8.3, Node20)
  • GitHub Check: build (8.3, Node18)
  • GitHub Check: build (8.3, Go112)
  • GitHub Check: build (8.3, FlutterBeta)
  • GitHub Check: build (8.3, KotlinJava8)
  • GitHub Check: build (8.3, Android14Java17)
  • GitHub Check: build (8.3, CLINode18)
  • GitHub Check: build (8.3, CLINode16)
  • GitHub Check: build (8.3, Android5Java17)
  • GitHub Check: android (client)
  • GitHub Check: swift (server)
  • GitHub Check: apple (client)
🔇 Additional comments (25)
templates/flutter/lib/src/realtime_browser.dart.twig (1)

38-40: LGTM! Type broadening aligns with Channel helper support.

The signature change from List<String> to List<Object> allows this method to accept Channel helper objects alongside plain strings, which is the core objective of this PR. The delegation to subscribeTo remains unchanged.

templates/flutter/lib/src/realtime_mixin.dart.twig (1)

171-177: LGTM! Clean conversion helper.

The _channelToString helper appropriately handles both string and non-string channel values by delegating to .toString() for Channel objects. This aligns with the established pattern for helper classes in this SDK.

templates/android/library/src/main/java/io/package/services/Realtime.kt.twig (3)

112-120: LGTM! Clean channel conversion helper.

The channelToString implementation correctly handles both String and other channel types (like Channel helper objects). The String check is a good optimization to avoid unnecessary toString() calls.

As per learnings, no validation or escaping is performed on IDs, which aligns with the intentional design pattern across the SDK.


122-129: LGTM! Backward-compatible channel helper support.

This overload cleanly enables developers to pass Channel helper objects (or other types) to subscribe() while maintaining full backward compatibility. The delegation to the existing String-based implementation ensures consistent behavior.

Kotlin's type resolution will prefer the String overload when strings are passed directly, and this Any overload when Channel objects are used.


140-150: LGTM! Type-safe channel helper integration.

This typed overload correctly extends the Channel helper support to include custom payload types while preserving type safety. The delegation pattern cleanly maps channels to strings and forwards to the existing typed implementation, properly returning the RealtimeSubscription handle.

This allows developers to write type-safe subscriptions like:

realtime.subscribe(
    Channel.database("db123", "col456"),
    payloadType = MyDocument::class.java
) { event -> 
    // event.payload is MyDocument
}
templates/apple/Sources/Services/Realtime.swift.twig (7)

6-11: LGTM!

The ChannelValue protocol is clean and minimal. Using toString() rather than Swift's CustomStringConvertible is acceptable here for cross-SDK consistency.


13-17: LGTM!

The String extension ensures backward compatibility for existing callers passing string channels directly.


154-163: LGTM!

The signature change to ChannelValue is backward compatible, and the delegation logic is correct.


165-174: Breaking API change: Set<String>[ChannelValue].

Changing the parameter type from Set<String> to [ChannelValue] is a breaking change for callers using the Set type. While migration is straightforward, this is worth noting in release documentation.

The implementation correctly converts to Set<String> internally, preserving deduplication behavior.


176-186: LGTM!

The generic overload correctly handles the ChannelValue parameter and delegates appropriately.


188-198: LGTM!

This new overload completes the API surface, allowing multi-channel subscriptions with typed payloads using ChannelValue. Implementation is consistent with other overloads.


19-31: No action required. All Channel types (ResolvedChannel, DatabaseChannel, etc.) already implement the toString() -> String method in their base definitions, satisfying the ChannelValue protocol requirements. The empty extensions correctly declare protocol conformance.

templates/apple/Sources/Channel.swift.twig (1)

1-261: LGTM! Well-structured Channel DSL for Swift.

The implementation provides a clean, fluent API for constructing channel strings with proper type safety and default wildcard support. The class hierarchy (ResolvedChannel, ActionChannel, resource-specific channels) is well-organized and follows Swift conventions. Based on learnings, this intentionally follows the SDK pattern of accepting IDs as-is without validation or escaping.

tests/languages/flutter/tests.dart (1)

250-270: Excellent test coverage for Channel helpers.

The Channel helper tests comprehensively exercise all factory methods (database, tablesdb, account, buckets, functions, teams, memberships) with various scenarios: default wildcards, specific IDs, nested builders, and action methods. Test patterns are consistent with other helper tests in the file.

tests/languages/web/index.html (2)

24-24: Import updated correctly.

Channel properly added to the public API imports for testing.


322-342: Comprehensive Channel helper test coverage.

Tests thoroughly exercise all Channel factory methods and builders with appropriate scenarios including default wildcards, specific IDs, nested chains, and action methods (create/update/delete). Coverage aligns with other language test implementations.

tests/languages/web/node.js (2)

1-1: Import correctly updated for Node.js.

Channel added to CommonJS require imports.


253-273: Complete Channel helper test coverage.

Node.js tests mirror the browser test coverage, ensuring Channel helpers work correctly in both environments. All factory methods and builder patterns are validated.

tests/languages/android/Tests.kt (2)

10-10: Import added correctly.

Channel class imported for test usage.


273-293: Complete Channel helper test coverage.

The 20 Channel helper test cases comprehensively cover all factory methods (database, tablesdb, account, buckets, functions, teams, memberships) with default wildcards, specific IDs, and actions. Coverage is consistent with other language implementations.

templates/web/src/client.ts.twig (1)

2-2: Import added correctly.

ChannelValue imported for type support in subscribe method.

templates/android/library/src/main/java/io/package/Channel.kt.twig (1)

1-190: Excellent Kotlin Channel DSL implementation.

The implementation provides a clean, idiomatic Kotlin API with proper use of default parameters, companion object for factory methods, and a well-organized class hierarchy. The design is consistent with other language implementations while following Kotlin best practices. Based on learnings, this correctly follows the SDK pattern of accepting IDs without validation or escaping.

templates/web/src/services/realtime.ts.twig (3)

2-2: Import added correctly.

ChannelValue imported with proper relative path from service subdirectory.


241-257: Clean channel normalization helper.

The channelToString helper method provides a clean, reusable way to normalize both string and ChannelValue inputs. The logic handles all cases appropriately with proper type checking and fallback.


259-348: Well-designed subscribe overloads with ChannelValue support.

The implementation provides excellent type safety through multiple overloads, allowing users to pass single or multiple channels as either strings or Channel builder instances. The normalization logic (lines 311-317) cleanly converts all inputs to a Set for internal handling. Documentation is comprehensive and the use of the channelToString helper keeps the code maintainable.

Comment on lines 1 to 239
part of '{{ language.params.packageName }}.dart';

class ResolvedChannel {
final String _value;

ResolvedChannel(this._value);

String toString() {
return _value;
}
}

abstract class ActionChannel {
final String base;

ActionChannel(this.base);

ResolvedChannel create() {
return ResolvedChannel('$base.create');
}

ResolvedChannel update() {
return ResolvedChannel('$base.update');
}

ResolvedChannel delete() {
return ResolvedChannel('$base.delete');
}

@override
String toString() {
return base;
}
}

/**
* ----------------------------
* Database → Collection → Document
* ----------------------------
*/
class DocumentChannel extends ActionChannel {
DocumentChannel(String base) : super(base);
}

class CollectionChannel {
final String base;
final String databaseId;
final String collectionId;

CollectionChannel(this.databaseId, this.collectionId)
: base = 'databases.$databaseId.collections.$collectionId';

DocumentChannel document([String documentId = '*']) {
return DocumentChannel('$base.documents.$documentId');
}

@override
String toString() {
return base;
}
}

class DatabaseChannel {
final String base;
final String databaseId;

DatabaseChannel(this.databaseId) : base = 'databases.$databaseId';

CollectionChannel collection([String collectionId = '*']) {
return CollectionChannel(databaseId, collectionId);
}

@override
String toString() {
return base;
}
}

/**
* ----------------------------
* TablesDB → Table → Row
* ----------------------------
*/
class RowChannel extends ActionChannel {
RowChannel(String base) : super(base);
}

class TableChannel {
final String base;
final String databaseId;
final String tableId;

TableChannel(this.databaseId, this.tableId)
: base = 'tablesdb.$databaseId.tables.$tableId';

RowChannel row([String rowId = '*']) {
return RowChannel('$base.rows.$rowId');
}

@override
String toString() {
return base;
}
}

class TablesDBChannel {
final String base;
final String databaseId;

TablesDBChannel(this.databaseId) : base = 'tablesdb.$databaseId';

TableChannel table([String tableId = '*']) {
return TableChannel(databaseId, tableId);
}

@override
String toString() {
return base;
}
}

/**
* ----------------------------
* Buckets → File
* ----------------------------
*/
class FileChannel extends ActionChannel {
FileChannel(String base) : super(base);
}

class BucketChannel {
final String base;
final String bucketId;

BucketChannel(this.bucketId) : base = 'buckets.$bucketId';

FileChannel file([String fileId = '*']) {
return FileChannel('$base.files.$fileId');
}

@override
String toString() {
return base;
}
}

/**
* ----------------------------
* Functions → Execution
* ----------------------------
*/
class ExecutionChannel extends ActionChannel {
ExecutionChannel(String base) : super(base);
}

class FunctionChannel {
final String base;
final String functionId;

FunctionChannel(this.functionId) : base = 'functions.$functionId';

ExecutionChannel execution([String executionId = '*']) {
return ExecutionChannel('$base.executions.$executionId');
}

@override
String toString() {
return base;
}
}

class TeamChannel extends ActionChannel {
TeamChannel([String teamId = '*']) : super('teams.$teamId');
}

class MembershipChannel extends ActionChannel {
MembershipChannel([String membershipId = '*'])
: super('memberships.$membershipId');
}

typedef ChannelValue = Object;

class Channel {
/// Generate a database channel builder.
///
/// [databaseId] The database ID (default: "*")
/// Returns [DatabaseChannel]
static DatabaseChannel database([String databaseId = '*']) {
return DatabaseChannel(databaseId);
}

/// Generate a tables database channel builder.
///
/// [databaseId] The database ID (default: "*")
/// Returns [TablesDBChannel]
static TablesDBChannel tablesdb([String databaseId = '*']) {
return TablesDBChannel(databaseId);
}

/// Generate a buckets channel builder.
///
/// [bucketId] The bucket ID (default: "*")
/// Returns [BucketChannel]
static BucketChannel buckets([String bucketId = '*']) {
return BucketChannel(bucketId);
}

/// Generate a functions channel builder.
///
/// [functionId] The function ID (default: "*")
/// Returns [FunctionChannel]
static FunctionChannel functions([String functionId = '*']) {
return FunctionChannel(functionId);
}

/// Generate a teams channel builder.
///
/// [teamId] The team ID (default: "*")
/// Returns [TeamChannel]
static TeamChannel teams([String teamId = '*']) {
return TeamChannel(teamId);
}

/// Generate a memberships channel builder.
///
/// [membershipId] The membership ID (default: "*")
/// Returns [MembershipChannel]
static MembershipChannel memberships([String membershipId = '*']) {
return MembershipChannel(membershipId);
}

/// Generate an account channel string.
///
/// [userId] The user ID (default: "*")
/// Returns The channel string
static String account([String userId = '*']) {
return 'account.$userId';
}
} No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

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

It was interesting to look at the PR, and I noticed signs of Class Explosion and a violation of the DRY principle.
I propose a version with a single Builder and a system of interface constraints. It preserves strict typing of calls, ridding the project of unnecessary classes and duplication of logic.

part of appwrite_channel;

// The result of building the channel.
final class ResolvedChannel {
  final String value;
  const ResolvedChannel(this.value);

  @override
  String toString() => value;
}

// Helper function for normalizing ID
String _normalize(String id) => id.trim().isEmpty ? '*' : id;

// State interfaces

abstract interface class Actionable {
  ResolvedChannel create();
  ResolvedChannel update();
  ResolvedChannel delete();
  String get channel;
}

abstract interface class IDatabase {
  ICollection collection([String id = '*']);
}

abstract interface class ICollection {
  IDocument document([String id = '*']);
}

abstract interface class IDocument implements Actionable {}

abstract interface class ITablesDB {
  ITable table([String id = '*']);
}

abstract interface class ITable {
  IRow row([String id = '*']);
}

abstract interface class IRow implements Actionable {}

abstract interface class IBucket {
  IFile file([String id = '*']);
}

abstract interface class IFile implements Actionable {}

abstract interface class IFunction {
  IExecution execution([String id = '*']);
}

abstract interface class IExecution implements Actionable {}

// Builder implementation

class _ChannelBuilder implements 
    IDatabase, ICollection, IDocument, 
    ITablesDB, ITable, IRow,
    IBucket, IFile, 
    IFunction, IExecution, Actionable {
  
  final List<String> _segments;

  _ChannelBuilder._(this._segments);

  _ChannelBuilder _next(String segment, String id) {
    return _ChannelBuilder._([
      ..._segments,
      segment,
      _normalize(id),
    ]);
  }

  @override
  ICollection collection([String id = '*']) => _next('collections', id);
  
  @override
  IDocument document([String id = '*']) => _next('documents', id);

  @override
  ITable table([String id = '*']) => _next('tables', id);

  @override
  IRow row([String id = '*']) => _next('rows', id);

  @override
  IFile file([String id = '*']) => _next('files', id);

  @override
  IExecution execution([String id = '*']) => _next('executions', id);

  @override
  ResolvedChannel create() => _resolve('create');
  @override
  ResolvedChannel update() => _resolve('update');
  @override
  ResolvedChannel delete() => _resolve('delete');

  @override
  String get channel => _segments.join('.');

  @override
  String toString() => channel;

  ResolvedChannel _resolve(String action) => ResolvedChannel('$channel.$action');
}

// Entry point

abstract final class Channel {
  static IDatabase database([String id = '*']) => 
      _ChannelBuilder._(['databases', _normalize(id)]);
      
  static ITablesDB tablesdb([String id = '*']) => 
      _ChannelBuilder._(['tablesdb', _normalize(id)]);
      
  static IBucket buckets([String id = '*']) => 
      _ChannelBuilder._(['buckets', _normalize(id)]);
      
  static IFunction functions([String id = '*']) => 
      _ChannelBuilder._(['functions', _normalize(id)]);
  
  static Actionable teams([String id = '*']) => 
      _ChannelBuilder._(['teams', _normalize(id)]);
      
  static Actionable memberships([String id = '*']) => 
      _ChannelBuilder._(['memberships', _normalize(id)]);

  static String account([String userId = '']) => 
      userId.isEmpty ? 'account' : 'account.${_normalize(userId)}';
  // Global events (without ID)
  static String get documents => 'documents';
  static String get rows => 'rows';
  static String get files => 'files';
  static String get executions => 'executions';
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the suggestion. It really makes sense. Will surely apply this

Copy link
Contributor

Choose a reason for hiding this comment

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

Glad it helped,
I also looked at the classes, and there is no single builder used for Kotlin and Swift.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Glad it helped, I also looked at the classes, and there is no single builder used for Kotlin and Swift.

I just pushed swift and kotlin channel builder. Should be good now I think, waiting for the tests

…lates

- Removed deprecated channel types and streamlined channel value handling in Realtime service.
- Introduced Actionable and ResolvedChannel interfaces for better type safety.
- Updated database, tablesdb, buckets, functions, teams, and memberships channel builders to use new interfaces.
- Enhanced test cases to reflect changes in channel creation and subscription methods.
- Improved documentation for channel subscription methods to clarify expected types.
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: 2

🧹 Nitpick comments (4)
templates/apple/Sources/Services/Realtime.swift.twig (1)

136-153: Consider simplifying the channel conversion logic.

The explicit check for ResolvedChannel at lines 148-150 appears redundant. Since ResolvedChannel conforms to ChannelValue, calling channel.toString() in the fallback (line 152) would produce the same result.

The code could be simplified to:

private func channelToString(_ channel: ChannelValue) -> String {
    // Check if it's an Actionable instance (has channel property)
    if let actionable = channel as? Actionable {
        return actionable.channel
    }
    // Use toString() for all ChannelValue types
    return channel.toString()
}

This eliminates the redundant branch while maintaining the special handling for Actionable types.

templates/flutter/lib/src/realtime_mixin.dart.twig (1)

171-185: Redundant type check and cast.

The check if (channel is ResolvedChannel) on line 181 is redundant because line 184 already handles this case via channel.toString(). All Dart objects have a toString() method, so ResolvedChannel will be handled correctly by the fallback.

Additionally, the .cast<String>() on line 189 is unnecessary since map((ch) => _channelToString(ch)) already returns Iterable<String>, and toList() preserves that type.

🔎 Suggested simplification
  /// Convert channel value to string
  String _channelToString(Object channel) {
    if (channel is String) {
      return channel;
    }
    // Check if it's an Actionable instance (has channel getter)
    if (channel is Actionable) {
      return channel.channel;
    }
-   // Check if it's a ResolvedChannel (has toString() method)
-   if (channel is ResolvedChannel) {
-     return channel.toString();
-   }
    return channel.toString();
  }

  RealtimeSubscription subscribeTo(List<Object> channels) {
    StreamController<RealtimeMessage> controller = StreamController.broadcast();
-   final channelStrings = channels.map((ch) => _channelToString(ch)).toList().cast<String>();
+   final channelStrings = channels.map((ch) => _channelToString(ch)).toList();
templates/web/src/client.ts.twig (1)

585-601: Redundant type check for toString.

The check on line 597 typeof (ch as ResolvedChannel).toString === 'function' is always true since all JavaScript objects inherit toString from Object.prototype. This branch will handle all non-string, non-Actionable inputs, making the String(ch) fallback on line 600 unreachable.

Consider simplifying by removing the redundant check:

🔎 Suggested simplification
        const channelStrings = channelArray.map(ch => {
            if (typeof ch === 'string') {
                return ch;
            }
            // Check if it's an Actionable instance (has channel() method)
            if (ch && typeof (ch as Actionable).channel === 'function') {
                return (ch as Actionable).channel();
            }
-           // Check if it's a ResolvedChannel (has toString() method)
-           if (ch && typeof (ch as ResolvedChannel).toString === 'function') {
-               return (ch as ResolvedChannel).toString();
-           }
-           return String(ch);
+           return ch.toString();
        });
templates/web/src/services/realtime.ts.twig (1)

248-261: Same redundant toString check as in client.ts.

Lines 256-259 check for toString method existence, but all objects have this method. The check will always pass, making line 260's String(channel) unreachable.

🔎 Suggested simplification
    private channelToString(channel: string | Actionable | ResolvedChannel): string {
        if (typeof channel === 'string') {
            return channel;
        }
        // Check if it's an Actionable instance (has channel() method)
        if (channel && typeof (channel as Actionable).channel === 'function') {
            return (channel as Actionable).channel();
        }
-       // Check if it's a ResolvedChannel (has toString() method)
-       if (channel && typeof (channel as ResolvedChannel).toString === 'function') {
-           return (channel as ResolvedChannel).toString();
-       }
-       return String(channel);
+       return channel.toString();
    }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3e2402d and 4ff8dd6.

📒 Files selected for processing (13)
  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/android/library/src/main/java/io/package/services/Realtime.kt.twig
  • templates/apple/Sources/Channel.swift.twig
  • templates/apple/Sources/Services/Realtime.swift.twig
  • templates/dart/test/channel_test.dart.twig
  • templates/flutter/lib/channel.dart.twig
  • templates/flutter/lib/src/realtime.dart.twig
  • templates/flutter/lib/src/realtime_base.dart.twig
  • templates/flutter/lib/src/realtime_mixin.dart.twig
  • templates/react-native/src/channel.ts.twig
  • templates/web/src/channel.ts.twig
  • templates/web/src/client.ts.twig
  • templates/web/src/services/realtime.ts.twig
🚧 Files skipped from review as they are similar to previous changes (2)
  • templates/dart/test/channel_test.dart.twig
  • templates/android/library/src/main/java/io/package/services/Realtime.kt.twig
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.
📚 Learning: 2025-12-18T18:42:20.227Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.

Applied to files:

  • templates/flutter/lib/src/realtime_mixin.dart.twig
  • templates/flutter/lib/src/realtime_base.dart.twig
  • templates/web/src/client.ts.twig
  • templates/flutter/lib/src/realtime.dart.twig
  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/apple/Sources/Channel.swift.twig
  • templates/flutter/lib/channel.dart.twig
  • templates/react-native/src/channel.ts.twig
  • templates/apple/Sources/Services/Realtime.swift.twig
  • templates/web/src/services/realtime.ts.twig
  • templates/web/src/channel.ts.twig
📚 Learning: 2025-12-18T18:43:35.419Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.

Applied to files:

  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/apple/Sources/Channel.swift.twig
  • templates/flutter/lib/channel.dart.twig
  • templates/react-native/src/channel.ts.twig
  • templates/web/src/channel.ts.twig
🔇 Additional comments (18)
templates/apple/Sources/Services/Realtime.swift.twig (3)

6-17: LGTM! Clean abstraction for channel types.

The ChannelValue protocol provides a simple, extensible contract for channel representation. String conformance ensures backward compatibility, allowing existing code using String channels to work seamlessly with the new API.


19-20: ResolvedChannel and ActionChannel correctly implement the required ChannelValue protocol.

Both types already define toString() -> String methods in Channel.swift.twig (ResolvedChannel at lines 11-13, ActionChannel at lines 54-56), making the empty protocol conformance extensions valid.


155-199: LGTM! Well-structured API expansion with backward compatibility.

The subscribe overloads correctly accept ChannelValue types while maintaining internal Set<String> logic. Key strengths:

  • Backward compatible: Existing String-based calls continue to work seamlessly
  • Consistent pattern: All overloads convert ChannelValue → String via channelToString before forwarding
  • Complete API surface: New channels: [ChannelValue], payloadType: overload (lines 189-199) fills a gap in the generic API
  • Properly typed: Actionable protocol is correctly defined in Channel.swift.twig with the .channel property, ensuring type-safe conversions

The clean separation between the public ChannelValue-based interface and internal String-based storage ensures no breaking changes.

templates/flutter/lib/src/realtime.dart.twig (1)

45-53: LGTM!

The updated documentation with Channel builder examples is clear and helpful. The API change from List<String> to List<Object> maintains backward compatibility since strings are still accepted, while enabling the new Channel builder pattern.

templates/flutter/lib/src/realtime_base.dart.twig (1)

4-6: LGTM!

The signature change is consistent with the parent Realtime class interface.

templates/web/src/client.ts.twig (1)

575-582: Good documentation addition.

The Channel builder examples in the JSDoc are clear and demonstrate the various builder patterns available.

templates/android/library/src/main/java/io/package/Channel.kt.twig (1)

1-196: Well-structured Channel DSL for Android/Kotlin.

The implementation provides good type safety with separate classes for each hierarchy level. The ActionChannel base class effectively reduces duplication for action methods. The normalize helper correctly handles empty/whitespace IDs by defaulting to "*".

Note: The Flutter implementation (in channel.dart.twig) uses a single _ChannelBuilder class with interfaces, which is more compact. Both approaches are valid - this one trades more classes for clearer static typing at each builder step.

templates/flutter/lib/channel.dart.twig (2)

131-132: Consider edge case for whitespace-only userId.

The account method checks userId.isEmpty but if a user passes whitespace like " ", it won't be considered empty and will result in account.* (after normalization). This differs from other entry points where the ID is normalized first.

Is this the intended behavior? If consistency is desired:

🔎 Potential fix for consistency
  static String account([String userId = '']) => 
-     userId.isEmpty ? 'account' : 'account.${_normalize(userId)}';
+     userId.trim().isEmpty ? 'account' : 'account.${_normalize(userId)}';

58-108: Well-implemented consolidated builder pattern.

The _ChannelBuilder class implementing all interfaces via a segment list is clean and follows DRY principles. This addresses the "class explosion" concern raised in the past review discussion.

templates/web/src/services/realtime.ts.twig (1)

311-321: LGTM!

The subscribe implementation correctly normalizes mixed channel types to a Set<string> before registering with the subscription system.

templates/react-native/src/channel.ts.twig (1)

1-195: LGTM - consistent Channel builder implementation.

The React Native implementation follows the same consolidated builder pattern as Flutter, with a single ChannelBuilder class implementing all level interfaces. The account() method correctly handles whitespace with trim() check (line 173), which is consistent with how other platforms should behave.

Minor note: The file has unusual indentation (extra leading spaces on most lines), which may be a template formatting artifact.

templates/web/src/channel.ts.twig (3)

1-13: LGTM! Clean foundation utilities.

The ResolvedChannel wrapper and normalize helper are well-implemented. The immutable design of ResolvedChannel and the whitespace-to-wildcard normalization logic are both sound.


17-54: LGTM! Well-structured type-safe interfaces.

The interface hierarchy correctly models the resource relationships and ensures type-safe chaining. The separation of concerns between chainable interfaces and the Actionable interface for terminal operations is clean.


58-143: LGTM! Solid builder implementation.

The immutable builder pattern with next() creating new instances is a good design choice. The approach of having ChannelBuilder implement all interfaces works well in TypeScript's structural type system, keeping the implementation DRY while the interface types enforce correct usage at the API boundary.

templates/apple/Sources/Channel.swift.twig (4)

1-20: LGTM! Solid foundation matching TypeScript implementation.

The ResolvedChannel and normalize utilities correctly mirror the TypeScript implementation. The immutability of ResolvedChannel and the whitespace normalization logic are both sound.

Based on learnings, the lack of ID validation is intentional design for SDK helper utilities.


23-57: LGTM! Appropriate Swift design pattern.

The Actionable protocol with ActionChannel base class is a clean Swift pattern that provides code reuse while maintaining type safety. This is a good language-specific adaptation compared to the TypeScript interface-based approach.


59-199: LGTM! Consistent and well-structured channel hierarchy.

All resource-specific channel classes follow a consistent pattern:

  • Intermediate builders (CollectionChannel, TableChannel, BucketChannel, FunctionChannel) provide chaining methods
  • Terminal channels (DocumentChannel, RowChannel, FileChannel, ExecutionChannel, TeamChannel, MembershipChannel) extend ActionChannel for CRUD actions
  • Each class properly normalizes IDs and constructs correct dot-delimited channel paths

The verbose class-per-resource approach is appropriate for Swift's type system and provides excellent type safety. The channel strings generated match the TypeScript implementation (except for the account method, flagged separately).


236-239: LGTM! Global event constants match TypeScript implementation.

The static constants for global events (documents, rows, files, executions) are consistent with the TypeScript implementation and provide a clean API for subscribing to global resource events.

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 (1)
templates/flutter/lib/channel.dart.twig (1)

112-138: Clean public API with appropriate type guidance.

The Channel class provides intuitive static entry points that return typed interfaces guiding users through valid builder chains. The distinction between structured resources (database, buckets, etc.) returning builders and simpler resources (account, global events) returning strings directly makes sense given their different structures.

Optional: More idiomatic Dart pattern

While the abstract class with static members works correctly, a more idiomatic Dart pattern would use a private constructor to prevent instantiation:

-abstract class Channel {
+class Channel {
+  // Private constructor prevents instantiation
+  Channel._();
+
   static IDatabase database([String id = '*']) => 
       _ChannelBuilder._(['databases', _normalize(id)]);
   // ... rest of static methods

This is purely stylistic and doesn't affect functionality.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ff8dd6 and 61710ea.

📒 Files selected for processing (3)
  • templates/flutter/lib/channel.dart.twig
  • templates/flutter/lib/src/realtime_mixin.dart.twig
  • tests/Base.php
🚧 Files skipped from review as they are similar to previous changes (1)
  • templates/flutter/lib/src/realtime_mixin.dart.twig
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.
📚 Learning: 2025-12-18T18:42:20.227Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.

Applied to files:

  • templates/flutter/lib/channel.dart.twig
📚 Learning: 2025-12-18T18:43:35.419Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.

Applied to files:

  • templates/flutter/lib/channel.dart.twig
⏰ 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). (20)
  • GitHub Check: swift (server)
  • GitHub Check: dart (server)
  • GitHub Check: kotlin (server)
  • GitHub Check: react-native (client)
  • GitHub Check: php (server)
  • GitHub Check: node (server)
  • GitHub Check: android (client)
  • GitHub Check: build (8.3, CLINode18)
  • GitHub Check: build (8.3, WebChromium)
  • GitHub Check: build (8.3, Ruby31)
  • GitHub Check: build (8.3, FlutterBeta)
  • GitHub Check: build (8.3, CLINode20)
  • GitHub Check: build (8.3, AppleSwift56)
  • GitHub Check: build (8.3, DotNet60)
  • GitHub Check: build (8.3, Swift56)
  • GitHub Check: build (8.3, Ruby30)
  • GitHub Check: build (8.3, Android5Java17)
  • GitHub Check: build (8.3, KotlinJava11)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: build (8.3, Android14Java17)
🔇 Additional comments (4)
tests/Base.php (1)

166-187: LGTM! Comprehensive test data for channel helpers.

The CHANNEL_HELPER_RESPONSES constant provides thorough coverage of channel patterns across different resource types (databases, tablesdb, account, buckets, functions, teams, memberships) with wildcards, specific IDs, and action variants. The patterns correctly follow the dot-notation format expected by Appwrite realtime channels.

templates/flutter/lib/channel.dart.twig (3)

3-13: Well-designed value types and normalization.

The ResolvedChannel wrapper provides semantic distinction for channels with actions, and the _normalize helper correctly implements the minimal ID transformation pattern (empty → '*'). Based on learnings, the lack of ID validation or escaping is intentional across SDK helpers.


17-54: Excellent use of interfaces for type-safe fluent API.

The interface hierarchy enforces valid call sequences at compile time while avoiding class explosion. Each interface exposes only the appropriate next steps in the builder chain, with terminal types implementing Actionable to provide action methods and channel access. This directly addresses the previous review feedback.


58-108: Solid single-builder implementation addressing previous feedback.

The _ChannelBuilder class elegantly implements all interfaces while maintaining type safety through return types. The immutable segment list and _next() pattern prevent accidental mutations. This design directly implements the suggestion from the previous review to use "a single Builder and a system of interface constraints" rather than multiple classes, successfully avoiding class explosion while preserving strict typing.

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 (1)
templates/flutter/lib/src/realtime_mixin.dart.twig (1)

194-194: Remove redundant cast operation.

The .cast<String>() is unnecessary since map(_channelToString) already produces List<String> after .toList().

🔎 Simplified implementation
-    final channelStrings = channels.map((ch) => _channelToString(ch)).toList().cast<String>();
+    final channelStrings = channels.map((ch) => _channelToString(ch)).toList();
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 61710ea and 9252cc3.

📒 Files selected for processing (1)
  • templates/flutter/lib/src/realtime_mixin.dart.twig
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.
📚 Learning: 2025-12-18T18:42:20.227Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.

Applied to files:

  • templates/flutter/lib/src/realtime_mixin.dart.twig
⏰ 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). (18)
  • GitHub Check: build (8.3, WebChromium)
  • GitHub Check: build (8.3, KotlinJava11)
  • GitHub Check: build (8.3, Ruby31)
  • GitHub Check: build (8.3, Node16)
  • GitHub Check: build (8.3, AppleSwift56)
  • GitHub Check: build (8.3, Python39)
  • GitHub Check: build (8.3, Node20)
  • GitHub Check: build (8.3, FlutterBeta)
  • GitHub Check: build (8.3, KotlinJava17)
  • GitHub Check: build (8.3, DartBeta)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: build (8.3, Android14Java17)
  • GitHub Check: build (8.3, Android5Java17)
  • GitHub Check: flutter (client)
  • GitHub Check: kotlin (server)
  • GitHub Check: android (client)
  • GitHub Check: swift (server)
  • GitHub Check: apple (client)
🔇 Additional comments (3)
templates/flutter/lib/src/realtime_mixin.dart.twig (3)

171-190: LGTM! Dynamic channel conversion implementation is appropriate.

The three-tier approach (String → Actionable interface → toString fallback) correctly handles multiple channel representations. The dynamic property access avoids import conflicts with part file types, which is a pragmatic solution given Dart's part/library constraints.


192-192: LGTM! Backward-compatible signature change.

Broadening the parameter type from List<String> to List<Object> enables Channel helper objects while maintaining backward compatibility with existing string-based callers.


195-204: LGTM! Consistent use of normalized channel strings.

The normalized channelStrings are used consistently throughout the method, ensuring type safety for internal storage, subscription creation, and cleanup operations.

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

♻️ Duplicate comments (1)
templates/apple/Sources/Channel.swift.twig (1)

151-154: Cross-platform API inconsistency with TypeScript implementation remains.

This was flagged in a previous review: when userId is empty or whitespace (normalized to "*"), Swift returns "account" while TypeScript returns "account.*". Please coordinate with the Web SDK to align behavior.

🧹 Nitpick comments (1)
templates/apple/Sources/Channel.swift.twig (1)

4-14: Consider conforming to CustomStringConvertible for Swift idiom.

The toString() method works, but Swift convention prefers CustomStringConvertible conformance with a description property. This allows ResolvedChannel to work naturally with string interpolation and print statements.

🔎 Suggested enhancement (optional)
-public final class ResolvedChannel {
+public final class ResolvedChannel: CustomStringConvertible {
     private let value: String

     init(value: String) {
         self.value = value
     }

     public func toString() -> String {
         value
     }
+
+    public var description: String {
+        value
+    }
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9252cc3 and 4c3bef7.

📒 Files selected for processing (2)
  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/apple/Sources/Channel.swift.twig
🚧 Files skipped from review as they are similar to previous changes (1)
  • templates/android/library/src/main/java/io/package/Channel.kt.twig
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.
📚 Learning: 2025-12-18T18:42:20.227Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.

Applied to files:

  • templates/apple/Sources/Channel.swift.twig
📚 Learning: 2025-12-18T18:43:35.419Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.

Applied to files:

  • templates/apple/Sources/Channel.swift.twig
⏰ 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). (19)
  • GitHub Check: build (8.3, Go118)
  • GitHub Check: build (8.3, Python311)
  • GitHub Check: build (8.3, KotlinJava11)
  • GitHub Check: build (8.3, Ruby30)
  • GitHub Check: build (8.3, Python39)
  • GitHub Check: build (8.3, Python312)
  • GitHub Check: build (8.3, PHP83)
  • GitHub Check: build (8.3, AppleSwift56)
  • GitHub Check: build (8.3, Node16)
  • GitHub Check: build (8.3, DartBeta)
  • GitHub Check: build (8.3, Python313)
  • GitHub Check: build (8.3, Android5Java17)
  • GitHub Check: build (8.3, KotlinJava17)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: build (8.3, Android14Java17)
  • GitHub Check: build (8.3, FlutterBeta)
  • GitHub Check: apple (client)
  • GitHub Check: swift (server)
  • GitHub Check: android (client)
🔇 Additional comments (3)
templates/apple/Sources/Channel.swift.twig (3)

16-20: LGTM!

The normalize function correctly handles trimming and defaults empty/whitespace-only inputs to "*".


22-60: LGTM!

The protocol hierarchy is well-designed, enabling type-safe fluent chaining for channel construction. The separation between navigation protocols (IDatabase, ICollection, etc.) and terminal actionable protocols (IDocument, IRow, IFile, IExecution) is clean.


125-149: LGTM!

The public API is well-designed with appropriate return types that hide the internal ChannelBuilder implementation. The static factory methods provide a clean entry point for channel construction, and the global event constants are appropriately defined.

Also applies to: 156-161

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

♻️ Duplicate comments (1)
templates/apple/Sources/Channel.swift.twig (1)

208-211: Cross-platform inconsistency: account() behavior still differs from TypeScript.

The account() method continues to return "account" when the normalized input is "*", while the TypeScript implementation returns "account.*". This creates inconsistent channel strings across SDKs:

  • Swift: Channel.account("*")"account"
  • TypeScript: Channel.account("*")"account.*"

This was flagged as critical in a previous review and remains unresolved.

Based on the previous review thread, the expected fix is to return "account.*" when the normalized ID is "*", aligning with the Web SDK behavior.

🧹 Nitpick comments (2)
tests/AppleSwift56Test.php (2)

138-154: Consider adding error handling for filesystem operations.

The rmdirRecursive() implementation is correct, but filesystem operations like unlink(), rmdir(), and scandir() can fail due to permission issues or other errors. Consider wrapping these operations in a try-catch block and logging or throwing exceptions on failure to make debugging easier if cleanup fails.

🔎 Proposed enhancement with error handling
 private function rmdirRecursive($dir): void
 {
     if (!\is_dir($dir)) {
         return;
     }
-    foreach (\scandir($dir) as $file) {
-        if ('.' === $file || '..' === $file) {
-            continue;
-        }
-        if (\is_dir("$dir/$file")) {
-            $this->rmdirRecursive("$dir/$file");
-        } else {
-            \unlink("$dir/$file");
+    try {
+        foreach (\scandir($dir) as $file) {
+            if ('.' === $file || '..' === $file) {
+                continue;
+            }
+            if (\is_dir("$dir/$file")) {
+                $this->rmdirRecursive("$dir/$file");
+            } else {
+                \unlink("$dir/$file");
+            }
         }
+        \rmdir($dir);
+    } catch (\Exception $e) {
+        echo "Warning: Failed to remove directory $dir: " . $e->getMessage() . "\n";
     }
-    \rmdir($dir);
 }

38-136: Consider refactoring this method to improve maintainability.

The testHTTPSuccess() method handles multiple distinct responsibilities sequentially:

  1. SDK setup and configuration (lines 43-69)
  2. Directory cleanup and generation (lines 71-75)
  3. Building (lines 77-83)
  4. Test execution and output capture (lines 85-97)
  5. Output validation (lines 99-135)

While the enhanced logging for debugging is valuable, extracting helper methods like setupSdk(), buildSdk(), captureTestOutput(), and validateTestOutput() would improve readability and testability.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4c3bef7 and 05046c8.

📒 Files selected for processing (3)
  • templates/apple/Sources/Channel.swift.twig
  • templates/apple/Sources/Services/Realtime.swift.twig
  • tests/AppleSwift56Test.php
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.
📚 Learning: 2025-12-18T18:42:20.227Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.

Applied to files:

  • templates/apple/Sources/Services/Realtime.swift.twig
  • templates/apple/Sources/Channel.swift.twig
📚 Learning: 2025-12-18T18:43:35.419Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.

Applied to files:

  • templates/apple/Sources/Channel.swift.twig
⏰ 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). (17)
  • GitHub Check: build (8.3, Ruby31)
  • GitHub Check: node (server)
  • GitHub Check: swift (server)
  • GitHub Check: build (8.3, Python313)
  • GitHub Check: build (8.3, WebChromium)
  • GitHub Check: build (8.3, Swift56)
  • GitHub Check: build (8.3, AppleSwift56)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: build (8.3, Node18)
  • GitHub Check: cli (console)
  • GitHub Check: flutter (client)
  • GitHub Check: build (8.3, Python39)
  • GitHub Check: build (8.3, FlutterBeta)
  • GitHub Check: build (8.3, DotNet80)
  • GitHub Check: android (client)
  • GitHub Check: react-native (client)
  • GitHub Check: build (8.3, Android5Java17)
🔇 Additional comments (6)
tests/AppleSwift56Test.php (2)

19-19: LGTM! Improved error capture.

The addition of 2>&1 to capture both stdout and stderr is a good improvement for debugging compilation errors during SDK testing.


34-34: Addition of CHANNEL_HELPER_RESPONSES is correct and complete.

The constant Base::CHANNEL_HELPER_RESPONSES is properly defined in the Base test class, and AppleSwift56Test.php correctly includes it alongside other platform test files that support channel helpers (Web, Flutter, and Android). No additional updates are required.

templates/apple/Sources/Channel.swift.twig (1)

169-179: LGTM! The value: argument label fix has been applied.

The ResolvedChannel initializations now correctly include the value: argument label, resolving the compilation error flagged in the previous review.

templates/apple/Sources/Services/Realtime.swift.twig (3)

120-125: LGTM! Clean abstraction for channel conversion.

The channelToString helper provides a clear conversion point from the public ChannelValue protocol to the internal String representation used by the WebSocket layer.


161-171: Good addition: typed multi-channel subscription with ChannelValue.

This new overload completes the API surface by allowing subscriptions to multiple channels with a specific payload type using ChannelValue. The conversion from [ChannelValue] to Set<String> is consistent with other overloads and properly removes duplicates.


227-227: LGTM! Explicit NIO.Channel qualification avoids naming conflict.

The explicit NIO.Channel qualification prevents ambiguity with the new Channel helper enum for realtime subscriptions. This is the same type, just explicitly scoped.

Based on learnings, this qualification pattern is intentional to avoid naming collisions.

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

🤖 Fix all issues with AI agents
In @templates/web/src/client.ts.twig:
- Around line 588-601: The current type-guard in the channelArray ->
channelStrings mapping wrongly treats any object as ResolvedChannel because
every object has toString; update the checks so ResolvedChannel is detected
before generic toString and avoid relying on Object.prototype.toString:
specifically, in the map that builds channelStrings, check for ResolvedChannel
first using a robust test (e.g., instanceof ResolvedChannel or a unique
property/method that only ResolvedChannel exposes) and only then fall back to
Actionable duck-typing (checking typeof (ch as Actionable).channel ===
'function') and finally use String(ch) as the last fallback; this keeps the
channel() detection correct and prevents false positives from toString().
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 05046c8 and 9f5f635.

📒 Files selected for processing (12)
  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/apple/Sources/Channel.swift.twig
  • templates/dart/test/channel_test.dart.twig
  • templates/flutter/lib/channel.dart.twig
  • templates/flutter/test/src/channel_test.dart.twig
  • templates/web/src/channel.ts.twig
  • templates/web/src/client.ts.twig
  • tests/languages/android/Tests.kt
  • tests/languages/apple/Tests.swift
  • tests/languages/flutter/tests.dart
  • tests/languages/web/index.html
  • tests/languages/web/node.js
🚧 Files skipped from review as they are similar to previous changes (6)
  • tests/languages/flutter/tests.dart
  • tests/languages/web/node.js
  • templates/dart/test/channel_test.dart.twig
  • tests/languages/apple/Tests.swift
  • templates/flutter/test/src/channel_test.dart.twig
  • tests/languages/android/Tests.kt
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.
📚 Learning: 2025-12-18T18:42:20.227Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.

Applied to files:

  • tests/languages/web/index.html
  • templates/web/src/client.ts.twig
  • templates/flutter/lib/channel.dart.twig
  • templates/web/src/channel.ts.twig
  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/apple/Sources/Channel.swift.twig
📚 Learning: 2025-12-18T18:43:35.419Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.

Applied to files:

  • tests/languages/web/index.html
  • templates/flutter/lib/channel.dart.twig
  • templates/web/src/channel.ts.twig
  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/apple/Sources/Channel.swift.twig
⏰ 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). (14)
  • GitHub Check: build (8.3, Ruby30)
  • GitHub Check: build (8.3, PHP83)
  • GitHub Check: go (server)
  • GitHub Check: kotlin (server)
  • GitHub Check: android (client)
  • GitHub Check: build (8.3, Go118)
  • GitHub Check: build (8.3, KotlinJava17)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: build (8.3, DotNet90)
  • GitHub Check: build (8.3, FlutterBeta)
  • GitHub Check: build (8.3, KotlinJava11)
  • GitHub Check: build (8.3, DartBeta)
  • GitHub Check: swift (server)
  • GitHub Check: apple (client)
🔇 Additional comments (33)
tests/languages/web/index.html (2)

24-24: LGTM!

The Channel import is correctly added to the destructured Appwrite exports, consistent with other helper utilities like Query, Permission, and ID.


322-342: LGTM!

Channel helper tests are comprehensive, covering all major API surfaces: database, tablesdb, account, buckets, function, team, and membership. The tests correctly exercise both parameterless and parameterized calls, as well as CRUD actions.

templates/apple/Sources/Channel.swift.twig (9)

6-15: LGTM!

The ChannelValue protocol and String extension provide a clean abstraction for channel values. The String conformance enables seamless interoperability between plain strings and Channel builders.


18-28: LGTM!

ResolvedChannel is well-designed as a final value wrapper with internal initialization. This ensures instances are only created through the Channel DSL, maintaining API integrity.


31-34: LGTM!

The normalize helper appropriately converts empty or whitespace-only IDs to wildcards. As per learnings, the lack of additional validation or escaping is an intentional design pattern for these lightweight helper utilities.


37-42: LGTM!

The Actionable protocol cleanly defines the contract for resources that support CRUD actions. The design correctly returns ResolvedChannel for actions while exposing the raw channel string.


44-74: LGTM!

The resource protocol hierarchy accurately models the Appwrite resource structure. Leaf nodes correctly implement Actionable, while parent nodes provide typed navigation methods, ensuring type safety throughout the fluent API.


79-113: LGTM!

The protocol extensions correctly work around Swift's limitation on default parameters in protocol requirements. The overloads provide an ergonomic API where calls like collection() default to wildcards.


115-180: LGTM!

ChannelBuilder implements an immutable fluent builder pattern correctly. The implementation handles path construction, navigation, and CRUD actions appropriately, with segments joined by dots to form valid channel strings.


208-211: Verify the account() empty string default behavior.

The account() method treats an empty userId parameter as "account" (no suffix), whereas other Channel methods default to "". Line 209 normalizes an empty string to "", but Line 210 checks if the normalized result equals "*" and returns plain "account" in that case.

This appears intentional based on the test expectations, but please confirm this design choice is correct: should Channel.account() return "account" or "account.*"?

Based on learnings, the lack of validation is intentional. However, the different default behavior warrants confirmation.


182-207: LGTM!

The Channel factory methods and static constants are well-designed. Each method correctly normalizes IDs and returns the appropriate protocol type, enabling type-safe channel construction.

Also applies to: 212-218

templates/web/src/client.ts.twig (3)

2-2: LGTM!

The import correctly brings in Actionable and ResolvedChannel types needed for the enhanced subscribe() signature.


556-582: LGTM!

The documentation is thoroughly updated to reflect the new Channel builder support. Examples clearly demonstrate both traditional string channels and the new Channel DSL, providing good developer guidance.


602-617: LGTM (pending type guard fix)!

The subscription management correctly uses the normalized channel strings throughout. The flow from channel addition through subscription record creation to cleanup is well-structured and maintains consistency.

templates/flutter/lib/channel.dart.twig (6)

4-10: LGTM!

ResolvedChannel is well-designed as an immutable value object using Dart's const constructor. The toString() override provides clean string conversion.


13-13: LGTM!

The _normalize helper is concisely implemented and follows the same logic as other language implementations. Based on learnings, the lack of validation/escaping is intentional.


17-54: LGTM!

The interface definitions correctly model the resource hierarchy using Dart's abstract classes. The use of optional parameters with defaults provides an ergonomic API consistent with the design in other languages.


58-108: LGTM!

The _ChannelBuilder correctly implements an immutable fluent builder pattern. All navigation and action methods are properly implemented, maintaining the builder chain with each step returning a new instance.


131-132: Verify the account() empty string default behavior.

Similar to the Swift implementation, the Dart account() method treats an empty userId as "account" without a suffix, while other methods default to "*". Line 131-132 returns "account" when userId.isEmpty, otherwise "account.{normalized}".

This is consistent with the Swift version but differs from the wildcard pattern used elsewhere. Please confirm this is the intended behavior.

Based on learnings, the design choice should be consistent across languages, but the deviation from the "*" pattern warrants confirmation.


113-130: LGTM!

The Channel factory methods and static getters are well-designed and consistent with implementations in other languages. Each method properly normalizes IDs and returns the appropriate type.

Also applies to: 133-138

templates/web/src/channel.ts.twig (6)

2-7: LGTM!

ResolvedChannel is a clean value wrapper with appropriate visibility. The exported class enables type-safe channel resolution in the subscribe API.


10-13: LGTM!

The normalize helper correctly converts empty/whitespace IDs to wildcards. As per learnings, the minimal validation is intentional for these lightweight utilities.


17-54: LGTM!

The interface hierarchy correctly models the resource structure. The exported Actionable interface enables type-safe channel handling in the client, while internal resource interfaces maintain encapsulation.


58-143: LGTM!

ChannelBuilder correctly implements the immutable fluent builder pattern. All navigation and action methods properly create new instances, maintaining the builder chain while constructing valid channel paths.


172-176: Verify the account() empty string default behavior.

Consistent with Swift and Dart implementations, the TypeScript account() method returns "account" for empty/whitespace userId, while other methods default to "*". Lines 173-175 use a ternary to return "account" when userId is empty after trimming.

This design is consistent across languages but deviates from the wildcard pattern. Please confirm this is intentional.

The cross-language consistency suggests this is deliberate, but explicit confirmation would be valuable.


147-171: LGTM!

The Channel class provides a well-designed entry point with static factory methods returning appropriately typed builders. The implementation is consistent with other language templates, maintaining the same API surface.

Also applies to: 177-195

templates/android/library/src/main/java/io/package/Channel.kt.twig (7)

3-10: LGTM! Clean wrapper design.

The ResolvedChannel class provides a lightweight wrapper for the channel string with proper toString() support, enabling seamless integration with the DSL pattern.


12-18: LGTM! Proper normalization logic.

The normalize function correctly handles edge cases by trimming whitespace and defaulting empty strings to "*" wildcard. As per learnings, no additional validation or escaping is applied, which is the intentional design pattern for helper classes in this repository.


20-60: LGTM! Well-structured DSL interfaces.

The interface hierarchy provides a clean, type-safe foundation for the channel builder DSL. The use of default parameters (id: String = "*") offers flexibility, and the Actionable interface appropriately marks endpoints that support CRUD operations.


62-108: LGTM! Excellent immutable builder implementation.

The ChannelBuilder demonstrates solid design principles:

  • Immutability: next() creates new instances rather than mutating state
  • Encapsulation: Private class prevents external instantiation
  • Type safety: Multi-interface implementation enables fluent, type-safe chaining
  • Clean string building: Segments joined with "." and CRUD operations properly append action suffixes

110-131: LGTM! Consistent factory methods.

The Channel companion object provides a clean, consistent API surface. The private constructor enforces controlled instantiation, and all factory methods follow a uniform pattern: normalize the ID and start the builder chain with appropriate segments.


132-135: Note: Intentional API difference for account.

The account method returns a String directly (unlike other factory methods that return builders), and has distinct behavior:

  • account(), account(""), or account("*") all return "account" (current user)
  • account("userId") returns "account.userId" (specific user)

This design prevents constructing "account.*" which may not be a valid channel format. The API difference appears intentional to match account channel semantics.


137-142: LGTM! Clean global channel constants.

The global event channel constants (documents, rows, files, executions) provide convenient access to top-level channels and align with the overall channel naming conventions.

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

🤖 Fix all issues with AI agents
In @templates/react-native/src/channel.ts.twig:
- Around line 160-170: The public API on Channel uses plural method names
(functions, teams, memberships) which conflicts with Flutter/Dart's singular
names; rename these static methods to function(id = "*"), team(id = "*"), and
membership(id = "*") and update any internal references to call
ChannelBuilder.start(["functions", normalize(id)]) etc. (keep the path segments
as-is if the backend expects plural resource names) and update
exports/usages/tests to the new method names so consumers on React Native match
the Flutter/Dart naming.
🧹 Nitpick comments (2)
templates/web/src/client.ts.twig (1)

585-617: The ResolvedChannel detection relies on toString() which exists on all objects.

The check at line 597 (typeof (ch as ResolvedChannel).toString === 'function') will always be true for any non-null object since toString() is inherited from Object.prototype. However, since:

  1. The TypeScript type signature constrains valid inputs
  2. Actionable is checked first (line 593)
  3. The fallback String(ch) at line 600 produces the same result

This works correctly at runtime but could be more explicit. Consider checking for a discriminator property or using instanceof if ResolvedChannel is a class.

♻️ Optional: More explicit type discrimination
-            // Check if it's a ResolvedChannel (has toString() method)
-            if (ch && typeof (ch as ResolvedChannel).toString === 'function') {
-                return (ch as ResolvedChannel).toString();
-            }
-            return String(ch);
+            // For ResolvedChannel or any other object, convert to string
+            return String(ch);

Since ResolvedChannel.toString() returns the channel string, String(ch) will invoke it correctly. The explicit check is redundant.

templates/web/src/channel.ts.twig (1)

160-162: Minor: Inconsistent indentation.

The static function method is missing the leading indentation that other methods have.

🔧 Suggested fix
-  static function(id: string = "*"): IFunction {
+    static function(id: string = "*"): IFunction {
       return ChannelBuilder.start(["functions", normalize(id)]);
     }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f5f635 and 0b840f9.

📒 Files selected for processing (14)
  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/apple/Sources/Channel.swift.twig
  • templates/dart/test/channel_test.dart.twig
  • templates/flutter/lib/channel.dart.twig
  • templates/flutter/lib/src/realtime.dart.twig
  • templates/flutter/test/src/channel_test.dart.twig
  • templates/react-native/src/channel.ts.twig
  • templates/web/src/channel.ts.twig
  • templates/web/src/client.ts.twig
  • tests/languages/android/Tests.kt
  • tests/languages/apple/Tests.swift
  • tests/languages/flutter/tests.dart
  • tests/languages/web/index.html
  • tests/languages/web/node.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • templates/flutter/lib/src/realtime.dart.twig
  • templates/flutter/test/src/channel_test.dart.twig
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.
📚 Learning: 2025-12-18T18:43:35.419Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.

Applied to files:

  • tests/languages/flutter/tests.dart
  • tests/languages/web/node.js
  • templates/react-native/src/channel.ts.twig
  • tests/languages/web/index.html
  • tests/languages/apple/Tests.swift
  • templates/web/src/channel.ts.twig
  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • tests/languages/android/Tests.kt
  • templates/flutter/lib/channel.dart.twig
  • templates/apple/Sources/Channel.swift.twig
📚 Learning: 2025-12-18T18:42:20.227Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.

Applied to files:

  • templates/web/src/client.ts.twig
  • templates/react-native/src/channel.ts.twig
  • tests/languages/web/index.html
  • tests/languages/apple/Tests.swift
  • templates/web/src/channel.ts.twig
  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/flutter/lib/channel.dart.twig
  • templates/apple/Sources/Channel.swift.twig
🧬 Code graph analysis (2)
tests/languages/web/node.js (1)
tests/languages/node/test.js (3)
  • require (1-13)
  • require (14-14)
  • require (15-15)
tests/languages/android/Tests.kt (1)
tests/languages/kotlin/Tests.kt (1)
  • writeToFile (271-274)
⏰ 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). (9)
  • GitHub Check: build (8.3, Node16)
  • GitHub Check: build (8.3, AppleSwift56)
  • GitHub Check: build (8.3, FlutterBeta)
  • GitHub Check: build (8.3, KotlinJava8)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: build (8.3, KotlinJava11)
  • GitHub Check: swift (server)
  • GitHub Check: android (client)
  • GitHub Check: apple (client)
🔇 Additional comments (28)
tests/languages/flutter/tests.dart (1)

250-270: LGTM!

The Channel helper tests comprehensively cover the Channel DSL surface including database, tablesdb, account, bucket, function, team, and membership builders with their respective actions (create, update, delete). The test structure is consistent with other helper tests in this file.

templates/dart/test/channel_test.dart.twig (2)

1-6: LGTM!

The conditional import logic correctly distinguishes between pure Dart (package:test/test.dart) and Flutter (package:flutter_test/flutter_test.dart) test frameworks based on the package name.


9-157: LGTM!

The test template provides comprehensive coverage for the Channel DSL including:

  • Builder patterns with default wildcards and specific IDs
  • Nested path construction (database → collection → document, etc.)
  • Action methods (create, update, delete) returning ResolvedChannel
  • Global event getters returning plain strings
templates/web/src/client.ts.twig (1)

2-2: LGTM!

The import correctly brings in Actionable and ResolvedChannel types from the new channel module.

templates/react-native/src/channel.ts.twig (2)

58-143: LGTM!

The ChannelBuilder implementation is well-structured:

  • Immutable chaining via new instances in next()
  • Clean separation of segment building and action resolution
  • Proper implementation of all state interfaces

1-7: No action needed. The React Native implementation uses a different pattern than the web template. The subscribe method in React Native accepts raw string | string[] channels directly, not the Actionable or ResolvedChannel types. Since React Native doesn't use the Channel builder pattern and has no realtime service template that would import these types, exporting them is unnecessary.

Likely an incorrect or invalid review comment.

templates/flutter/lib/channel.dart.twig (2)

1-13: LGTM!

The file structure follows Dart conventions:

  • part of directive for library integration
  • Private helper _normalize with underscore prefix
  • Clean ResolvedChannel wrapper with proper toString() override

112-138: LGTM!

The Channel class is well-designed:

  • abstract class prevents instantiation (idiomatic Dart pattern for utility classes)
  • Consistent singular method naming (function, team, membership)
  • account() correctly handles the special case of returning plain "account" vs "account.{userId}"
  • Static getters for global events (documents, rows, files, executions) are clean
tests/languages/web/node.js (2)

1-1: LGTM!

The import correctly includes Channel in the destructured imports from the SDK.


253-273: LGTM!

The Channel helper tests are comprehensive and use singular method naming (Channel.function(), Channel.team(), Channel.membership()) consistent with the Flutter/Dart templates.

tests/languages/web/index.html (2)

24-24: LGTM!

The destructuring correctly includes Channel from the Appwrite global namespace.


322-342: LGTM!

The Channel helper tests are consistent with the Node.js test file and cover the complete Channel API surface.

tests/languages/apple/Tests.swift (1)

251-271: LGTM! Comprehensive test coverage for Channel helpers.

The tests exercise the full Channel DSL API surface including database, tablesdb, account, bucket, function, team, and membership builders with various operations (create, update, delete) and chained calls. The test structure follows the established patterns for Query and Permission helper tests in this file.

tests/languages/android/Tests.kt (2)

10-10: Import correctly added.


273-293: LGTM! Channel helper tests align with the Swift test suite.

The tests provide consistent coverage across the Channel DSL API, matching the patterns established in the Swift tests and following the existing writeToFile convention used throughout this test file.

templates/android/library/src/main/java/io/package/Channel.kt.twig (4)

1-10: Clean wrapper implementation.

ResolvedChannel provides a straightforward value wrapper with proper toString() override for seamless string conversion in realtime subscription contexts.


12-60: Well-structured interface hierarchy.

The normalize helper and interface definitions provide a clean, type-safe API. The Actionable interface correctly exposes channel for intermediate string access and action methods returning ResolvedChannel.


62-108: Solid builder implementation with immutable pattern.

The ChannelBuilder correctly uses an immutable approach (creating new instances via next()) and provides clean segment composition. The channel property and action methods properly construct the final path strings.


110-143: Clean public API design.

The Channel companion object provides intuitive factory methods. The account() method correctly handles the special case where an empty/whitespace userId should return just "account" rather than "account.*". Global event constants are appropriately exposed as const val.

templates/apple/Sources/Channel.swift.twig (5)

1-28: Well-designed protocol hierarchy for Swift interoperability.

The ChannelValue protocol with String extension enables seamless use of both raw strings and ResolvedChannel instances in APIs that accept channel values. This is a clean Swift-idiomatic approach.


30-74: Protocol hierarchy mirrors the Kotlin implementation.

The protocol definitions maintain cross-language consistency while being Swift-idiomatic. The inheritance structure (IDocument: Actionable, etc.) correctly models the channel path composition.


76-113: Correct Swift pattern for default parameter simulation.

Since Swift protocols don't support default parameter values, these extensions properly provide argument-less overloads that delegate to the wildcard "*". The inline comment at lines 76-78 helpfully documents this design decision.


115-180: Builder implementation is correctly encapsulated.

ChannelBuilder uses Swift's default internal access level, keeping it hidden from the public API while Channel remains the sole entry point. The immutable pattern and segment composition are consistent with the Kotlin implementation.


182-218: Public API is clean and consistent.

Using a Swift enum with only static members is the idiomatic way to create a namespace that can't be instantiated. The account() method correctly handles the empty userId case, and global event constants are properly exposed.

templates/web/src/channel.ts.twig (4)

1-13: Clean TypeScript implementation.

The ResolvedChannel wrapper and normalize helper follow the same patterns established in the Kotlin and Swift implementations.


15-54: Interface exports are appropriately scoped.

Only Actionable is exported (needed for typing in consumer code), while the intermediate interfaces remain internal. This keeps the public API surface minimal.


56-143: Builder implementation is consistent across languages.

The non-exported ChannelBuilder maintains the immutable builder pattern with segment composition via spread operator. The private resolve() helper cleanly abstracts action suffix appending.


145-195: Public API is well-designed.

The Channel class provides a clean entry point with static factory methods. Using getters for global event constants ensures they're accessed as properties (Channel.documents) rather than method calls, which is more idiomatic for TypeScript.

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 (2)
templates/web/src/client.ts.twig (1)

585-585: Consider broadening the type signature to allow mixed arrays.

The implementation (lines 588-602) correctly handles mixed arrays containing strings alongside Channel helpers, but the current type signature doesn't explicitly allow this pattern. Users might want to pass arrays like [Channel.account(), "documents", Channel.database('db')].

♻️ Proposed refinement to support mixed arrays
-subscribe<T extends unknown>(channels: string | string[] | Actionable | ResolvedChannel | (Actionable | ResolvedChannel)[], callback: (payload: RealtimeResponseEvent<T>) => void): () => void {
+subscribe<T extends unknown>(channels: string | Actionable | ResolvedChannel | (string | Actionable | ResolvedChannel)[], callback: (payload: RealtimeResponseEvent<T>) => void): () => void {
templates/react-native/src/channel.ts.twig (1)

145-194: LGTM! Clean public API with minor optimization opportunity.

The Channel entry point provides a clear, consistent API for constructing realtime channel subscriptions. Factory methods, special-case handling for account channels, and global event getters all work correctly.

♻️ Optional: Eliminate redundant trim in account() method

The account() method calls trim() twice—once for the conditional check and again inside normalize(). Caching the trimmed value would be slightly more efficient:

 static account(userId: string = ""): string {
-  return userId.trim()
-    ? `account.${normalize(userId)}`
-    : "account";
+  const trimmed = userId.trim();
+  return trimmed
+    ? `account.${normalize(userId)}`
+    : "account";
 }

Or more directly:

 static account(userId: string = ""): string {
-  return userId.trim()
-    ? `account.${normalize(userId)}`
-    : "account";
+  const normalized = normalize(userId);
+  return normalized !== "*"
+    ? `account.${normalized}`
+    : "account";
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0b840f9 and 2ed26c9.

📒 Files selected for processing (3)
  • templates/react-native/src/channel.ts.twig
  • templates/web/src/channel.ts.twig
  • templates/web/src/client.ts.twig
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.
📚 Learning: 2025-12-18T18:42:20.227Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.

Applied to files:

  • templates/web/src/client.ts.twig
  • templates/react-native/src/channel.ts.twig
  • templates/web/src/channel.ts.twig
📚 Learning: 2025-12-18T18:43:35.419Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.

Applied to files:

  • templates/react-native/src/channel.ts.twig
  • templates/web/src/channel.ts.twig
⏰ 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). (13)
  • GitHub Check: build (8.3, WebNode)
  • GitHub Check: build (8.3, Swift56)
  • GitHub Check: build (8.3, KotlinJava17)
  • GitHub Check: build (8.3, WebChromium)
  • GitHub Check: build (8.3, AppleSwift56)
  • GitHub Check: build (8.3, Node16)
  • GitHub Check: build (8.3, KotlinJava8)
  • GitHub Check: build (8.3, Android5Java17)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: build (8.3, Android14Java17)
  • GitHub Check: apple (client)
  • GitHub Check: swift (server)
  • GitHub Check: android (client)
🔇 Additional comments (12)
templates/web/src/client.ts.twig (4)

2-2: LGTM!

The imports are necessary for the Channel helper support in the subscribe method.


586-602: LGTM!

The normalization logic correctly handles all Channel helper types with appropriate fallbacks. The order of checks (ResolvedChannel, then Actionable, then string conversion) is correct since ChannelBuilder instances implement Actionable but not ResolvedChannel.


556-584: LGTM!

The documentation clearly explains both the legacy string-based channel syntax and the new Channel builder API with practical examples.


603-617: LGTM!

The normalized channel strings are consistently used throughout the subscription lifecycle (registration, storage, and cleanup), ensuring the realtime internals work exclusively with string channels.

templates/web/src/channel.ts.twig (5)

1-7: LGTM!

The ResolvedChannel class provides a clean wrapper for completed channel paths with actions, distinguishing them from in-progress builders.


9-13: LGTM!

The normalize function follows the SDK's intentional design pattern of accepting IDs without validation or escaping. The empty-to-wildcard conversion provides sensible default behavior for optional parameters.

Based on learnings, this lightweight approach is consistent across the SDK.


15-54: LGTM!

The interface design provides a clean, type-safe fluent API. Terminal interfaces appropriately extend Actionable to provide action methods, while intermediate interfaces guide users through the valid chaining paths.


58-143: LGTM!

The ChannelBuilder implementation correctly uses an immutable builder pattern with segment-based path construction. The use of default wildcard parameters provides sensible defaults for optional IDs.


147-195: LGTM!

The Channel factory methods provide appropriate entry points for different resource types. The design variations (account returning string, team/membership returning Actionable, and others returning chainable interfaces) align with the different API patterns for each resource type.

templates/react-native/src/channel.ts.twig (3)

1-13: LGTM! Clean helper types and normalization.

The ResolvedChannel wrapper and normalize function are well-implemented. The normalization logic correctly handles empty and whitespace-only IDs by converting them to wildcard "*". Per learnings, the intentional lack of validation or escaping aligns with the repository's design pattern for lightweight helper utilities.


15-92: LGTM! Well-structured builder pattern with strong type safety.

The interface hierarchy and ChannelBuilder implementation elegantly enforce valid channel construction paths through TypeScript's type system. The builder returns specific interfaces (IDatabase, ICollection, etc.) that constrain which methods can be called at each stage, preventing invalid channel structures. The immutable chaining via next() is a solid design choice.


94-143: LGTM! Consistent method implementations.

All builder methods follow a consistent pattern using next() for chaining and resolve() for terminal actions. The default parameter "*" provides a sensible wildcard for subscriptions. The implementation correctly satisfies the optional parameters in the interfaces.

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 (1)
templates/android/library/src/main/java/io/package/services/Realtime.kt.twig (1)

129-145: LGTM - Channel DSL overloads implemented correctly.

Both overloads properly convert DSL channel types to strings using channelToString and delegate to the existing String-based subscribe implementation. The vararg array handling and type conversions are correct.

The return type is correctly inferred as RealtimeSubscription from the delegate chain, maintaining consistency with the existing non-typed String overload pattern at lines 147-154.

Optional: Consider explicit return type for consistency

While the current implicit return type is correct, explicitly declaring it could improve readability and API documentation:

 fun subscribe(
     vararg channels: Actionable,
     callback: (RealtimeResponseEvent<Any>) -> Unit,
-) = subscribe(
+): RealtimeSubscription = subscribe(
     channels = channels.map { channelToString(it) }.toTypedArray(),
     Any::class.java,
     callback
 )

 fun subscribe(
     vararg channels: ResolvedChannel,
     callback: (RealtimeResponseEvent<Any>) -> Unit,
-) = subscribe(
+): RealtimeSubscription = subscribe(
     channels = channels.map { channelToString(it) }.toTypedArray(),
     Any::class.java,
     callback
 )

However, since the existing String overload at line 147 follows the same pattern without explicit return types, the current approach maintains consistency with the codebase style.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2ed26c9 and 6b30e48.

📒 Files selected for processing (2)
  • templates/android/library/src/main/java/io/package/services/Realtime.kt.twig
  • tests/AppleSwift56Test.php
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/AppleSwift56Test.php
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.
📚 Learning: 2025-12-18T18:42:20.227Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.

Applied to files:

  • templates/android/library/src/main/java/io/package/services/Realtime.kt.twig
🔇 Additional comments (3)
templates/android/library/src/main/java/io/package/services/Realtime.kt.twig (3)

5-6: LGTM - Imports correctly added for Channel DSL support.

The new imports for Actionable and ResolvedChannel are properly placed and necessary for the Channel helper functionality being added to the Realtime service.


114-127: LGTM - Well-designed channel normalization helper.

The channelToString function correctly handles the type hierarchy with appropriate precedence:

  1. Direct string pass-through
  2. Actionable → extracts .channel property
  3. ResolvedChannel → uses .toString()
  4. Fallback to .toString() for any unexpected types

The defensive design and clear documentation make this helper robust and maintainable.


156-178: LGTM - Generic typed overloads correctly preserve type safety.

The typed subscribe overloads properly:

  • Preserve the generic type parameter <T> through the delegation chain
  • Convert DSL channel types using channelToString and map operations
  • Explicitly declare RealtimeSubscription return type for clarity
  • Delegate to the existing typed String-based implementation at lines 180-208

The implementation maintains type safety while providing a cleaner API for Channel DSL users.

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 (3)
templates/web/src/channel.ts.twig (1)

1-14: Unused Root interface.

The Root interface is declared but never used in the code. If it's intended for future use, consider adding a comment; otherwise, it can be removed.

templates/web/src/client.ts.twig (1)

585-598: The toString function check is always true for objects.

The check typeof (ch as Channel<any>).toString === 'function' will always be true for any object since all objects inherit toString() from Object.prototype. The current logic works correctly but the condition is redundant.

Consider simplifying to check for non-null objects directly, or keep the current defensive approach if you prefer explicitness.

♻️ Optional simplification
     const channelStrings = channelArray.map(ch => {
       if (typeof ch === 'string') {
         return ch;
       }
-      // All Channel instances have toString() method
-      if (ch && typeof (ch as Channel<any>).toString === 'function') {
-        return (ch as Channel<any>).toString();
-      }
-      // Fallback to generic string conversion
-      return String(ch);
+      // Convert Channel instances to strings
+      return ch.toString();
     });
templates/web/src/services/realtime.ts.twig (1)

241-257: Same observation: the toString check is redundant.

Similar to client.ts.twig, the check typeof (channel as Channel<any>).toString === 'function' is always true for objects. Consider the same simplification for consistency.

♻️ Optional simplification
     private channelToString(channel: string | Channel<any> | ActionableChannel | ResolvedChannel): string {
         if (typeof channel === 'string') {
             return channel;
         }
-        // All Channel instances have toString() method
-        if (channel && typeof (channel as Channel<any>).toString === 'function') {
-            return (channel as Channel<any>).toString();
-        }
-        return String(channel);
+        // Convert Channel instances to strings
+        return channel.toString();
     }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 903bdb3 and 18152a1.

📒 Files selected for processing (4)
  • templates/react-native/src/channel.ts.twig
  • templates/web/src/channel.ts.twig
  • templates/web/src/client.ts.twig
  • templates/web/src/services/realtime.ts.twig
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.
📚 Learning: 2025-12-18T18:42:20.227Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.

Applied to files:

  • templates/web/src/client.ts.twig
  • templates/web/src/services/realtime.ts.twig
  • templates/web/src/channel.ts.twig
  • templates/react-native/src/channel.ts.twig
🔇 Additional comments (13)
templates/web/src/channel.ts.twig (6)

18-21: LGTM!

The normalize function correctly trims whitespace and converts empty strings to the wildcard *, which aligns with the channel path semantics.


23-38: LGTM!

The Channel<T> class is well-structured with a private constructor to enforce factory usage. The _type declaration provides phantom type information for type narrowing without runtime overhead.


40-68: LGTM!

The use of TypeScript's this parameter for type-safe method chaining is a clean pattern. Each navigation method correctly constrains the caller type and produces the appropriate next state type.


70-82: LGTM!

Terminal actions (create, update, delete) are correctly restricted to the Actionable union type, preventing invalid method chains at compile time.


109-112: Inconsistent return type for account() compared to other factories.

account() returns a string directly, while all other factory methods (database, bucket, etc.) return Channel<T> instances. This asymmetry may surprise users expecting uniform behavior.

If this is intentional (e.g., account doesn't support further chaining), consider documenting this distinction in a comment.


84-129: LGTM!

The static factory methods and global event getters are well-implemented. The factories correctly normalize IDs and construct the initial segment arrays.

templates/react-native/src/channel.ts.twig (1)

1-134: LGTM!

This file is a direct copy of the web Channel implementation, which is appropriate for the SDK generator template architecture. The same observations apply (unused Root interface, account() returning a string instead of a Channel instance), but these are consistent with the web template.

templates/web/src/client.ts.twig (3)

2-2: LGTM!

The import correctly brings in the Channel types needed for the extended subscribe signature.


556-584: LGTM!

The JSDoc has been updated with clear examples of Channel builder usage, which will help users understand the new API.


599-614: LGTM!

The normalized channelStrings array is correctly used for adding to the channels set, storing in subscriptions, and cleanup operations.

templates/web/src/services/realtime.ts.twig (3)

2-2: LGTM!

The import correctly brings in the Channel types for the Realtime service.


259-305: LGTM!

The subscribe overloads are well-documented with updated JSDoc comments reflecting the new Channel-compatible signatures. The type definitions properly support both single channel and array inputs with optional typed payloads.


307-348: LGTM!

The subscribe implementation correctly:

  1. Normalizes input to an array
  2. Converts all channels to strings via channelToString()
  3. Deduplicates using a Set
  4. Stores string-based channel references for subscription matching and cleanup

The implementation is consistent with the Client's subscribe method.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
templates/apple/Sources/Services/Realtime.swift.twig (1)

228-233: Change NIO.Channel to NIOCore.Channel in delegate method signatures to match the protocol.

The WebSocketClientDelegate protocol (line 7 & 10 in WebSocketClientDelegate.swift.twig) defines onOpen(channel: NIOCore.Channel) and onClose(channel: NIOCore.Channel, data: Data), but the Realtime implementation uses NIO.Channel at lines 228 and 248. This is a protocol conformance mismatch.

The ChannelValue protocol and String conformance are correctly defined and working as expected; no changes needed there.

🧹 Nitpick comments (11)
templates/web/src/client.ts.twig (1)

611-624: Consider simplifying the Channel-to-string conversion logic.

The toString method check on line 619 is redundant since all JavaScript objects inherit toString() from Object.prototype, making line 623 unreachable dead code.

♻️ Suggested simplification
     subscribe<T extends unknown>(channels: string | string[] | Channel<any> | ActionableChannel | ResolvedChannel | (Channel<any> | ActionableChannel | ResolvedChannel)[], callback: (payload: RealtimeResponseEvent<T>) => void): () => void {
         const channelArray = Array.isArray(channels) ? channels : [channels];
         // Convert Channel instances to strings
         const channelStrings = channelArray.map(ch => {
             if (typeof ch === 'string') {
                 return ch;
             }
-            // All Channel instances have toString() method
-            if (ch && typeof (ch as Channel<any>).toString === 'function') {
-                return (ch as Channel<any>).toString();
-            }
-            // Fallback to generic string conversion
-            return String(ch);
+            // Channel instances use toString() to return the channel string
+            return ch.toString();
         });
templates/apple/Sources/Services/Realtime.swift.twig (1)

120-172: Forward the passed payloadType instead of hardcoding T.self (avoids confusion).

Right now the generic overloads accept payloadType: T.Type but ignore the parameter (Line 150-171). It’s functionally equivalent, but reads like a bug.

Proposed diff
 public func subscribe<T : Codable>(
     channel: ChannelValue,
     payloadType: T.Type,
     callback: @escaping (RealtimeResponseEvent) -> Void
 ) async throws -> RealtimeSubscription {
     return try await subscribe(
         channels: Set([channelToString(channel)]),
-        payloadType: T.self,
+        payloadType: payloadType,
         callback: callback
     )
 }

 public func subscribe<T : Codable>(
     channels: [ChannelValue],
     payloadType: T.Type,
     callback: @escaping (RealtimeResponseEvent) -> Void
 ) async throws -> RealtimeSubscription {
     return try await subscribe(
         channels: Set(channels.map { channelToString($0) }),
-        payloadType: T.self,
+        payloadType: payloadType,
         callback: callback
     )
 }
templates/android/library/src/main/java/io/package/Channel.kt.twig (1)

134-243: Consider collapsing the duplicated create/update/delete extensions via a real Actionable marker interface.

typealias Actionable = Document doesn’t actually help here, and the current approach repeats the same three methods for each type.

(One possible direction: introduce interface Actionable, have Document/Row/File/Execution/Team/Membership : Actionable, then add fun <T : Actionable> Channel<T>.create()/update()/delete() once.)

templates/flutter/lib/channel.dart.twig (1)

19-70: Optional: make global events static const instead of getters (const-friendly).

Today:

  • static String get documents => 'documents';

Could be:

  • static const String documents = 'documents';
templates/flutter/lib/src/realtime_base.dart.twig (1)

4-6: The List<Object> parameter with the inline comment adequately documents the expected types. The implementation correctly normalizes both String and Channel objects to strings via the _channelToString() method:

String _channelToString(Object channel) {
  return channel is String ? channel : channel.toString();
}

A typedef alias to Object would not meaningfully improve type safety or discoverability in Dart—it's functionally equivalent to the current approach and provides no compile-time benefit over the inline comment. The current pattern is lightweight and works well for this helper API.

templates/flutter/lib/src/realtime_mixin.dart.twig (2)

171-175: _channelToString can be simplified (String special-case is redundant).

String.toString() already returns itself, so the type check doesn’t buy much.

Proposed simplification
-  String _channelToString(Object channel) {
-    return channel is String ? channel : channel.toString();
-  }
+  String _channelToString(Object channel) => channel.toString();

177-190: Remove redundant .cast<String>() when building channelStrings.

_channelToString already returns String, so the extra cast is unnecessary.

Proposed change
-    final channelStrings = channels.map((ch) => _channelToString(ch)).toList().cast<String>();
+    final channelStrings = channels.map(_channelToString).toList(growable: false);
templates/android/library/src/main/java/io/package/services/Realtime.kt.twig (2)

113-123: channelToString branches are redundant; simplify to channel.toString().

The String and Channel<*> branches don’t change behavior vs toString().

Proposed simplification
-    private fun channelToString(channel: Any): String {
-        return when {
-            channel is String -> channel
-            channel is Channel<*> -> channel.toString()
-            else -> channel.toString()
-        }
-    }
+    private fun channelToString(channel: Any): String = channel.toString()

125-133: Channel overloads look correct; consider explicit return type on the non-generic overload.

It should currently infer RealtimeSubscription, but making it explicit avoids accidental signature drift during future edits.

Optional tweak
-    fun subscribe(
+    fun subscribe(
         vararg channels: Channel<*>,
         callback: (RealtimeResponseEvent<Any>) -> Unit,
-    ) = subscribe(
+    ): RealtimeSubscription = subscribe(
         channels = channels.map { channelToString(it) }.toTypedArray(),
         Any::class.java,
         callback
     )

Also applies to: 143-153

templates/apple/Sources/Channel.swift.twig (2)

19-47: Consider struct for RealtimeChannel<T> (it’s immutable) + add CustomStringConvertible for smoother interop.

Right now consumers must remember to call .toString(); description helps logs/debugging and any String(describing:) usage.

Optional ergonomic additions
 public class RealtimeChannel<T> {
     private let segments: [String]
@@
     public func toString() -> String {
         return segments.joined(separator: ".")
     }
 }
+
+extension RealtimeChannel: CustomStringConvertible {
+    public var description: String { toString() }
+}

83-88: Use static let for global events (they’re constants).

Proposed change
-    public static var documents: String { "documents" }
-    public static var rows: String { "rows" }
-    public static var files: String { "files" }
-    public static var executions: String { "executions" }
+    public static let documents = "documents"
+    public static let rows = "rows"
+    public static let files = "files"
+    public static let executions = "executions"
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 18152a1 and 6d627f3.

📒 Files selected for processing (29)
  • src/SDK/Language/Android.php
  • src/SDK/Language/Apple.php
  • src/SDK/Language/Flutter.php
  • src/SDK/Language/ReactNative.php
  • src/SDK/Language/Web.php
  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/android/library/src/main/java/io/package/services/Realtime.kt.twig
  • templates/apple/Sources/Channel.swift.twig
  • templates/apple/Sources/Services/Realtime.swift.twig
  • templates/flutter/lib/channel.dart.twig
  • templates/flutter/lib/src/realtime.dart.twig
  • templates/flutter/lib/src/realtime_base.dart.twig
  • templates/flutter/lib/src/realtime_mixin.dart.twig
  • templates/web/src/client.ts.twig
  • tests/Android14Java11Test.php
  • tests/Android14Java17Test.php
  • tests/Android14Java8Test.php
  • tests/Android5Java17Test.php
  • tests/AppleSwift56Test.php
  • tests/Base.php
  • tests/FlutterBetaTest.php
  • tests/FlutterStableTest.php
  • tests/WebChromiumTest.php
  • tests/WebNodeTest.php
  • tests/languages/android/Tests.kt
  • tests/languages/apple/Tests.swift
  • tests/languages/flutter/tests.dart
  • tests/languages/web/index.html
  • tests/languages/web/node.js
🚧 Files skipped from review as they are similar to previous changes (6)
  • tests/WebNodeTest.php
  • tests/Base.php
  • src/SDK/Language/ReactNative.php
  • templates/flutter/lib/src/realtime.dart.twig
  • tests/languages/flutter/tests.dart
  • tests/Android5Java17Test.php
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.
📚 Learning: 2025-12-18T18:42:20.227Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.

Applied to files:

  • src/SDK/Language/Apple.php
  • templates/flutter/lib/src/realtime_base.dart.twig
  • tests/languages/apple/Tests.swift
  • tests/languages/web/index.html
  • templates/web/src/client.ts.twig
  • tests/languages/web/node.js
  • tests/AppleSwift56Test.php
  • templates/flutter/lib/src/realtime_mixin.dart.twig
  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/apple/Sources/Channel.swift.twig
  • templates/flutter/lib/channel.dart.twig
  • templates/android/library/src/main/java/io/package/services/Realtime.kt.twig
  • templates/apple/Sources/Services/Realtime.swift.twig
📚 Learning: 2025-12-18T18:43:35.419Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.

Applied to files:

  • tests/languages/apple/Tests.swift
  • tests/languages/web/index.html
  • tests/languages/web/node.js
  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/apple/Sources/Channel.swift.twig
  • templates/flutter/lib/channel.dart.twig
  • templates/android/library/src/main/java/io/package/services/Realtime.kt.twig
  • tests/languages/android/Tests.kt
🧬 Code graph analysis (8)
tests/FlutterStableTest.php (1)
tests/Base.php (1)
  • Base (17-369)
tests/languages/web/node.js (1)
tests/languages/node/test.js (3)
  • require (1-13)
  • require (14-14)
  • require (15-15)
tests/Android14Java8Test.php (1)
tests/Base.php (1)
  • Base (17-369)
tests/Android14Java11Test.php (1)
tests/Base.php (1)
  • Base (17-369)
tests/Android14Java17Test.php (1)
tests/Base.php (1)
  • Base (17-369)
tests/languages/android/Tests.kt (1)
tests/languages/kotlin/Tests.kt (1)
  • writeToFile (282-285)
tests/WebChromiumTest.php (1)
tests/Base.php (1)
  • Base (17-369)
tests/FlutterBetaTest.php (1)
tests/Base.php (1)
  • Base (17-369)
⏰ 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). (20)
  • GitHub Check: build (8.3, Node18)
  • GitHub Check: build (8.3, Python310)
  • GitHub Check: build (8.3, AppleSwift56)
  • GitHub Check: build (8.3, Go112)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: build (8.3, PHP80)
  • GitHub Check: build (8.3, KotlinJava8)
  • GitHub Check: build (8.3, DotNet90)
  • GitHub Check: build (8.3, WebChromium)
  • GitHub Check: build (8.3, Ruby31)
  • GitHub Check: build (8.3, KotlinJava17)
  • GitHub Check: build (8.3, KotlinJava11)
  • GitHub Check: build (8.3, DotNet80)
  • GitHub Check: build (8.3, DartBeta)
  • GitHub Check: build (8.3, Android5Java17)
  • GitHub Check: build (8.3, Android14Java17)
  • GitHub Check: android (client)
  • GitHub Check: kotlin (server)
  • GitHub Check: apple (client)
  • GitHub Check: swift (server)
🔇 Additional comments (23)
tests/WebChromiumTest.php (1)

40-40: LGTM!

The addition of CHANNEL_HELPER_RESPONSES follows the established pattern and is correctly placed in the expected output sequence.

tests/AppleSwift56Test.php (1)

35-35: LGTM!

Consistent with the broader test-suite updates for channel helper coverage.

tests/FlutterBetaTest.php (1)

35-35: LGTM!

Consistent addition of channel helper test coverage for Flutter beta.

tests/FlutterStableTest.php (1)

35-35: LGTM!

Consistent addition of channel helper test coverage for Flutter stable.

templates/web/src/client.ts.twig (3)

2-2: LGTM!

Import correctly brings in the new Channel types needed for the subscribe method signature.


582-610: LGTM!

Good documentation update with clear examples showing both traditional string channels and the new Channel builder API.


625-637: LGTM!

Correctly updates channel registration, subscription storage, and cleanup to use the converted string array.

src/SDK/Language/Android.php (1)

123-127: LGTM: generator now emits Channel.kt for Android.

tests/Android14Java8Test.php (1)

22-39: LGTM: adds Channel helper expectations to the Android Java 8 matrix.

src/SDK/Language/Web.php (1)

83-87: LGTM!

The new channel.ts file generation entry follows the established pattern for helper files (permission.ts, role.ts, id.ts). The scope, destination, and template path are consistent with the existing structure.

tests/Android14Java11Test.php (1)

37-37: LGTM!

The addition of CHANNEL_HELPER_RESPONSES to the expected output follows the established pattern and is consistently positioned between ID_HELPER_RESPONSES and OPERATOR_HELPER_RESPONSES.

tests/Android14Java17Test.php (1)

36-36: LGTM!

Consistent addition of CHANNEL_HELPER_RESPONSES matching the pattern in other Android test files.

src/SDK/Language/Apple.php (1)

73-77: LGTM!

The Channel.swift generation entry follows the established pattern for Apple SDK helper files. The template correctly uses the apple/ prefix for Apple-specific implementation, and the destination path is consistent with other helper files like ID.swift and Query.swift.

src/SDK/Language/Flutter.php (2)

83-87: LGTM!

The channel.dart generation entry follows the established pattern and is consistently positioned between id.dart and query.dart, matching the structure in other SDK languages.


303-307: LGTM!

Good practice adding the corresponding test file for the new channel helper. The test template path and destination follow the existing test file conventions.

tests/languages/apple/Tests.swift (1)

261-281: LGTM! Comprehensive Channel helper test coverage.

The tests exercise the Channel DSL thoroughly, covering all resource types (database, tablesdb, account, bucket, function, team, membership) with both default and custom parameters, as well as action methods (create, update, delete). The test structure follows the established patterns for other helpers like Query and Permission.

tests/languages/android/Tests.kt (2)

10-10: LGTM!

Import correctly added alongside other helper imports.


284-304: LGTM! Channel helper tests are consistent with other platforms.

The Android tests mirror the Apple/Swift Channel tests exactly, ensuring cross-platform API consistency. The test structure follows the existing writeToFile() pattern used throughout the test file.

tests/languages/web/node.js (2)

1-1: LGTM!

Channel correctly added to the SDK import.


278-298: LGTM! Channel helper tests match cross-platform consistency.

The Node.js tests follow the identical pattern as Android and Apple tests, ensuring the Channel DSL API is consistent across all client SDKs.

tests/languages/web/index.html (2)

24-24: LGTM!

Channel correctly added to the Appwrite destructuring for browser tests.


347-367: LGTM! Browser Channel tests consistent with Node.js tests.

The browser tests mirror the Node.js Channel helper tests, ensuring complete web SDK test coverage for both execution environments.

templates/apple/Sources/Channel.swift.twig (1)

234-249: ChannelValue compatibility layer looks good.

This keeps the API flexible while still allowing typed builder chaining.

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 (3)
templates/android/library/src/main/java/io/package/Channel.kt.twig (2)

19-20: Unused and misleading typealias.

The comment describes this as a "union type for actionable channels," but it's simply an alias to _Document. Additionally, this typealias doesn't appear to be used anywhere in the file. Consider removing it or clarifying its purpose if it's intended for external use.


40-50: Consider making next and resolve internal to preserve type safety.

These methods are marked public "for extension function access," but in Kotlin, internal visibility is sufficient for extension functions within the same module. Making them public allows SDK consumers to bypass the type-safe DSL by calling channel.next<AnyType>("arbitrary", "id") directly.

♻️ Proposed fix
-    fun <N> next(segment: String, id: String = "*"): Channel<N> {
+    internal fun <N> next(segment: String, id: String = "*"): Channel<N> {
         return Channel(segments + listOf(segment, normalize(id)))
     }

-    fun resolve(action: String): Channel<_Resolved> {
+    internal fun resolve(action: String): Channel<_Resolved> {
         return Channel(segments + listOf(action))
     }
templates/flutter/lib/channel.dart.twig (1)

60-63: Consider returning a Channel type for API consistency.

The account() method returns a String directly, while other factory methods (database(), bucket(), etc.) return Channel<T>. Although this works correctly since the string is the final channel path, it creates a slight API inconsistency.

If account is intentionally terminal with no further chaining, this is acceptable. Otherwise, consider returning a Channel<_Account> for uniform behavior.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6d627f3 and dde2c4b.

📒 Files selected for processing (2)
  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/flutter/lib/channel.dart.twig
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.
📚 Learning: 2025-12-18T18:42:20.227Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.

Applied to files:

  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/flutter/lib/channel.dart.twig
📚 Learning: 2025-12-18T18:43:35.419Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.

Applied to files:

  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/flutter/lib/channel.dart.twig
⏰ 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). (19)
  • GitHub Check: build (8.3, Ruby27)
  • GitHub Check: build (8.3, Ruby31)
  • GitHub Check: build (8.3, FlutterBeta)
  • GitHub Check: build (8.3, AppleSwift56)
  • GitHub Check: build (8.3, Go118)
  • GitHub Check: build (8.3, Node20)
  • GitHub Check: build (8.3, KotlinJava17)
  • GitHub Check: build (8.3, Python313)
  • GitHub Check: build (8.3, DotNet80)
  • GitHub Check: android (client)
  • GitHub Check: build (8.3, CLIBun10)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: build (8.3, DartStable)
  • GitHub Check: build (8.3, Android5Java17)
  • GitHub Check: build (8.3, DartBeta)
  • GitHub Check: build (8.3, Android14Java17)
  • GitHub Check: kotlin (server)
  • GitHub Check: swift (server)
  • GitHub Check: apple (client)
🔇 Additional comments (9)
templates/android/library/src/main/java/io/package/Channel.kt.twig (5)

1-17: LGTM!

The package templating and marker interfaces are well-structured. Using internal visibility for marker interfaces appropriately encapsulates the type-state pattern within the SDK.


22-28: LGTM!

The normalize helper correctly handles empty/whitespace IDs by defaulting to "*". Based on learnings, the intentional design pattern is to accept IDs without validation or escaping, which this implementation follows.


57-86: LGTM!

Factory methods are well-organized. The account() method intentionally returns a String directly (rather than Channel<T>) to match the expected output format where account doesn't have child routes. The global event constants provide convenient shortcuts.


89-132: LGTM!

The extension functions correctly implement type-safe method chaining. Each route transition is properly restricted by the receiver type, ensuring compile-time safety (e.g., document() is only callable on Channel<_Collection>).


134-243: LGTM!

Terminal actions are correctly implemented with proper type restrictions. Each actionable type (_Document, _Row, _File, _Execution, _Team, _Membership) has the expected create(), update(), and delete() operations returning Channel<_Resolved>.

Note: The comment on line 135 references "Actionable types" but the Actionable typealias on line 20 only maps to _Document. Consider updating the comment or typealias for consistency.

templates/flutter/lib/channel.dart.twig (4)

3-17: Well-designed phantom type markers for type-safe chaining.

The private marker classes effectively restrict method availability at compile time through the generic type parameter. This is a clean implementation of the phantom type pattern in Dart.


19-20: LGTM!

The _normalize function correctly handles empty and whitespace-only inputs by defaulting to the wildcard '*'.


72-109: LGTM!

The navigation extensions are correctly typed to their respective Channel<T> receivers, ensuring compile-time safety. The consistent use of _next<N> and default wildcard '*' provides a clean, predictable API.


111-154: LGTM!

The terminal action extensions correctly restrict create(), update(), and delete() methods to actionable resource types. The Channel<_Resolved> return type effectively prevents further invalid chaining. While the code is repetitive, this is acceptable given Dart's extension limitations.

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 (4)
templates/android/library/src/main/java/io/package/Channel.kt.twig (2)

3-17: _Root interface appears unused.

The _Root marker interface is declared but never referenced in the Channel DSL. If it's intended for future use, consider adding a comment; otherwise, remove it to avoid dead code.


19-20: Misleading and unused typealias.

The comment describes a "union type for actionable channels," but Kotlin doesn't support union types this way—this only aliases _Document. Additionally, Actionable is never used in the file. Consider removing this dead code or replacing it with a documentation comment listing the actionable types.

🔧 Suggested fix
-// Union type for actionable channels
-typealias Actionable = _Document
+// Actionable types (supporting create/update/delete):
+// _Document, _Row, _File, _Execution, _Team, _Membership
templates/apple/Sources/Channel.swift.twig (2)

25-47: Consider adding CustomStringConvertible conformance.

The toString() method works fine, but conforming to CustomStringConvertible would make RealtimeChannel more idiomatic in Swift, enabling direct use in string interpolation and print statements.

♻️ Optional enhancement
 /// Channel class with generic type parameter for type-safe method chaining
-public class RealtimeChannel<T> {
+public class RealtimeChannel<T>: CustomStringConvertible {
     private let segments: [String]
     
     internal init(_ segments: [String]) {
         self.segments = segments
     }
     
+    public var description: String {
+        return segments.joined(separator: ".")
+    }
+    
     /// Internal helper to transition to next state with segment and ID
     internal func next<N>(_ segment: String, _ id: String = "*") -> RealtimeChannel<N> {
         return RealtimeChannel<N>(segments + [segment, normalize(id)])
     }
     
     /// Internal helper for terminal actions (no ID segment)
     internal func resolve(_ action: String) -> RealtimeChannel<_Resolved> {
         return RealtimeChannel<_Resolved>(segments + [action])
     }
     
     /// Convert channel to string representation
     public func toString() -> String {
-        return segments.joined(separator: ".")
+        return description
     }
 }

3-17: Remove or document the unused _Root marker struct.

The _Root struct is defined but never referenced by any factory method or extension in this file. All Channel factory methods produce typed channels starting from _Database, _TablesDB, _Bucket, etc., never transitioning through _Root.

If reserved for future use, add a clarifying comment; otherwise, remove it.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dde2c4b and 8af59f4.

📒 Files selected for processing (2)
  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/apple/Sources/Channel.swift.twig
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.
📚 Learning: 2025-12-18T18:42:20.227Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/swift/Sources/WebSockets/WebSocketClient.swift.twig:140-140
Timestamp: 2025-12-18T18:42:20.227Z
Learning: In templates/swift/Sources/WebSockets/WebSocketClient.swift.twig, the Channel type from NIOCore is explicitly qualified as `NIOCore.Channel` to avoid naming conflicts with the new Channel helper class for realtime subscriptions. This is not a breaking change - it's the same type, just explicitly qualified in the source.

Applied to files:

  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/apple/Sources/Channel.swift.twig
📚 Learning: 2025-12-18T18:43:35.419Z
Learnt from: ArnabChatterjee20k
Repo: appwrite/sdk-generator PR: 1266
File: templates/apple/Sources/Channel.swift.twig:13-19
Timestamp: 2025-12-18T18:43:35.419Z
Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.

Applied to files:

  • templates/android/library/src/main/java/io/package/Channel.kt.twig
  • templates/apple/Sources/Channel.swift.twig
⏰ 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). (17)
  • GitHub Check: build (8.3, KotlinJava17)
  • GitHub Check: build (8.3, Node16)
  • GitHub Check: build (8.3, Go112)
  • GitHub Check: build (8.3, KotlinJava8)
  • GitHub Check: build (8.3, PHP80)
  • GitHub Check: build (8.3, DotNet90)
  • GitHub Check: build (8.3, FlutterStable)
  • GitHub Check: build (8.3, DotNet80)
  • GitHub Check: build (8.3, CLIBun11)
  • GitHub Check: build (8.3, DartStable)
  • GitHub Check: build (8.3, FlutterBeta)
  • GitHub Check: build (8.3, CLIBun13)
  • GitHub Check: build (8.3, Android14Java17)
  • GitHub Check: build (8.3, DartBeta)
  • GitHub Check: swift (server)
  • GitHub Check: apple (client)
  • GitHub Check: android (client)
🔇 Additional comments (9)
templates/android/library/src/main/java/io/package/Channel.kt.twig (4)

22-28: LGTM!

The normalize helper correctly handles whitespace trimming and defaults empty IDs to the wildcard *. Based on learnings, the intentional lack of validation/escaping aligns with the SDK's design pattern for lightweight helper utilities.


30-87: LGTM!

The Channel class design is well-structured:

  • Covariant out T is correctly applied since the type parameter is only used in output positions.
  • The next and resolve methods are intentionally public for extension function access, as documented.
  • The account method returning String directly (rather than Channel) is a reasonable design choice since accounts don't follow the same nested resource pattern.

89-133: LGTM!

The navigation extension functions provide clean, type-safe state transitions. The receiver type constraints ensure compile-time validation of valid channel paths (e.g., you can only call .document() on a Channel<_Collection>).


134-261: LGTM with a minor note.

The terminal action extensions are well-implemented with proper @JvmName annotations for JVM bytecode disambiguation. The type-restricted extension functions ensure only valid resource types can have terminal actions.

Note: The comment at line 135 references "Actionable types" which relates to the misleading typealias flagged earlier—consider updating both together.

templates/apple/Sources/Channel.swift.twig (5)

19-23: LGTM!

The normalize helper correctly handles empty/whitespace-only strings by defaulting to the wildcard "*". Based on learnings, the intentional omission of validation/escaping aligns with the SDK's design pattern.


49-88: LGTM!

The factory methods correctly construct channel paths. The account() method returning String directly (rather than RealtimeChannel) is a reasonable design choice since the account channel doesn't support further chaining or terminal actions.

The global event constants (documents, rows, files, executions) provide convenient shortcuts for broad subscriptions.


90-139: LGTM!

The constrained extensions correctly implement the type-state pattern, ensuring compile-time safety for valid channel paths. The routing hierarchy (Database→Collection→Document, TablesDB→Table→Row, etc.) is well-structured.


141-232: LGTM!

The terminal action extensions correctly restrict create(), update(), and delete() to actionable channel types. While the code is repetitive, this is a necessary trade-off in Swift to achieve compile-time type safety with phantom types.


234-249: LGTM!

The ChannelValue protocol provides a clean abstraction enabling subscribe APIs to accept both raw String channels and RealtimeChannel instances uniformly. The conformances are straightforward and correct.

@abnegate abnegate merged commit 3cf7f6f into master Jan 16, 2026
55 checks passed
@abnegate abnegate deleted the dat-971 branch January 16, 2026 06:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants