Skip to content

fix(ios): properly serialize arrays of Codable objects in widget config#377

Draft
galacticgibbon wants to merge 2 commits into
ABausG:mainfrom
galacticgibbon:main
Draft

fix(ios): properly serialize arrays of Codable objects in widget config#377
galacticgibbon wants to merge 2 commits into
ABausG:mainfrom
galacticgibbon:main

Conversation

@galacticgibbon

Copy link
Copy Markdown

Description

Fixes a serialization bug in HomeWidgetPlugin.swift where arrays of Codable objects (e.g., [EntryTypeEntity]) were not being properly encoded when extracting widget configuration via getInstalledWidgets().

Problem

When a widget configuration contains an array parameter (like entryTypes: [EntryTypeEntity]), the existing code would match the generic case let arrayValue as [Any]: case before reaching the Codable case. This caused raw Swift objects to be passed through the Flutter method channel, resulting in a "Unsupported value for standard codec" crash.

Solution

Reordered the type checking in getInstalledWidgets() to handle arrays of Codable objects before the generic array case. The fix:

  1. Attempts to encode each array element as Codable
  2. Converts each element to a JSON dictionary
  3. Returns the array of dictionaries if all elements encode successfully
  4. Falls back to string conversion if encoding fails

This allows arrays of AppEntity types (which conform to Codable) to be properly serialized and accessible in Flutter.

Example

Before: Crash when accessing widget.configuration['entryTypes']

After: Returns properly serialized array:

[
  {id: "task", displayString: "Task"},
  {id: "event", displayString: "Event"},
  {id: "note", displayString: "Note"}
]

Checklist

  • I have updated/added tests for ALL new/updated/fixed functionality.
  • I have updated/added relevant documentation and added code (documentation) comments where necessary.
  • I have updated/added relevant examples in example or documentation.

Breaking Change?

  • Yes, this PR is a breaking change.
  • No, this PR is not a breaking change.

Related Issues

Fixes #376

@docs-page

docs-page Bot commented Oct 18, 2025

Copy link
Copy Markdown

To view this pull requests documentation preview, visit the following URL:

docs.page/abausg/home_widget~377

Documentation is deployed and generated using docs.page.

@coderabbitai

coderabbitai Bot commented Oct 18, 2025

Copy link
Copy Markdown

Walkthrough

Adds special-case encoding for arrays of Codable elements in the iOS widget plugin, encoding each element to JSON dictionaries with a string fallback. Separately, Android background work now supports an expedited flag propagated through intent, HomeWidgetBackgroundReceiver, HomeWidgetIntent, and HomeWidgetBackgroundWorker.

Changes

Cohort / File(s) Summary
iOS: Widget configuration array encoding
packages/home_widget/ios/Classes/HomeWidgetPlugin.swift
Detects arrays whose elements conform to Codable and attempts per-element JSON encoding → dictionary conversion; if any element fails, falls back to converting elements to strings. This handling is applied before the generic array case.
Android: Background work expedited flag
packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundReceiver.kt, packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundWorker.kt, packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetIntent.kt
Adds EXTRA_IS_EXPEDITED intent extra and propagates an expedited: Boolean parameter through HomeWidgetIntent.getBroadcast → HomeWidgetBackgroundReceiver → HomeWidgetBackgroundWorker.enqueueWork; enqueueWork optionally applies an expedited OutOfQuotaPolicy when building the WorkRequest.

Sequence Diagram(s)

sequenceDiagram
    participant Plugin as iOS HomeWidgetPlugin
    participant Param as Parameter Value (Array)
    participant Element as Element (Any)
    participant Encoder as JSONEncoder
    participant Dict as Dictionary
    participant Fallback as String Conversion
    participant Output as Widget Config Value

    Plugin->>Param: receive array parameter
    Param->>Element: iterate elements
    Element->>Encoder: try encode (if Codable)
    alt encode succeeds for all elements
        Encoder->>Dict: decode JSON -> Dictionary
        Dict->>Output: store array of dictionaries
    else any encode fails
        Element->>Fallback: convert each element to string
        Fallback->>Output: store array of strings
    end
Loading
sequenceDiagram
    participant Intent as HomeWidgetIntent.getBroadcast
    participant Receiver as HomeWidgetBackgroundReceiver
    participant Worker as HomeWidgetBackgroundWorker
    participant WorkManager as Android WorkManager

    Intent->>Receiver: send PendingIntent (includes EXTRA_IS_EXPEDITED)
    Receiver->>Worker: call enqueueWork(context, intent, expedited)
    Worker->>Worker: build OneTimeWorkRequestBuilder
    alt expedited == true
        Worker->>Worker: set OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST (expedited)
    end
    Worker->>WorkManager: enqueue UniqueWork with APPEND policy
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Points to review:

  • HomeWidgetPlugin.swift: confirm Codable detection logic, proper JSON encoding/decoding to dictionary, edge cases (empty arrays, mixed element types), and performance/memory implications.
  • Android files: verify intent extra naming/consistency, default behavior when expedited omitted, WorkRequest builder usage, and compatibility with WorkManager versions.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The pull request contains significant out-of-scope changes in three Android files (HomeWidgetBackgroundReceiver.kt, HomeWidgetBackgroundWorker.kt, and HomeWidgetIntent.kt) that implement expedited work-queueing functionality, which is entirely unrelated to the stated PR objectives addressing iOS array serialization in issue #376. These Android changes introduce new public APIs (EXTRA_IS_EXPEDITED constant, expedited parameter to multiple methods) and alter method signatures but are not mentioned in the PR description, title, or linked objectives. This represents a substantial scope creep that combines two separate features into a single PR.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title "fix(ios): properly serialize arrays of Codable objects in widget config" correctly uses the conventional commit prefix "fix:" and clearly describes the primary iOS-specific change being made. It's concise, specific enough that a teammate scanning history would understand this addresses serialization of Codable arrays, and directly relates to the main problem being solved (arrays of AppEntity objects not serializing properly). The title accurately captures the essence of the iOS fix described in the PR without being vague or generic.

[pre_merge_check_pass]

The pull request description comprehensively follows the provided template with all required sections present. It includes a clear Description section explaining the bug and solution, a Problem subsection detailing the root cause, a Solution subsection with implementation details, and an Example showing the before/after behavior. The Checklist, Breaking Change, and Related Issues sections are all included as required. While the checkboxes for tests and documentation updates are unchecked, the description itself is detailed and complete rather than vague or incomplete.

[pre_merge_check_pass]

The iOS changes in HomeWidgetPlugin.swift directly address the primary objective from linked issue #376, which requires handling arrays of Codable objects (specifically AppEntity types like EntryTypeEntity) to prevent crashes when accessing widget configuration via getInstalledWidgets(). The fix implements the expected behavior by attempting to encode each array element as Codable, converting to JSON dictionaries, and falling back to string conversion if encoding fails, which matches the stated requirements to return standard-codec-compatible representations accessible in Flutter. |

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6fa4ab7 and 5883ae8.

📒 Files selected for processing (3)
  • packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundReceiver.kt (1 hunks)
  • packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundWorker.kt (2 hunks)
  • packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetIntent.kt (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). (2)
  • GitHub Check: iOS Integration Tests
  • GitHub Check: iOS Integration Tests
🔇 Additional comments (3)
packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundReceiver.kt (1)

13-14: LGTM! Expedited flag propagation is correct.

The extraction of the expedited flag from the intent and its propagation to enqueueWork is implemented correctly. The use of getBooleanExtra with a false default ensures safe handling when the extra is not present.

packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundWorker.kt (2)

12-12: Import is correct for expedited work support.

The OutOfQuotaPolicy import is necessary for configuring expedited work requests.


115-125: Expedited work implementation is correct and verified.

The refactor to a builder pattern with conditional expedited policy application is clean and correct. The choice of OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST provides appropriate graceful degradation when the expedited work quota is exhausted.

Verification confirms that the project's WorkManager dependency (androidx.work:work-runtime-ktx:2.+ in the main library and 2.8.1 in the example app) meets the 2.7.0+ requirement for expedited work support.

Comment on lines +56 to +62
const val EXTRA_IS_EXPEDITED = "es.antonborri.home_widget.extra.IS_EXPEDITED"

fun getBroadcast(context: Context, uri: Uri? = null): PendingIntent {
fun getBroadcast(context: Context, uri: Uri? = null, expedited: Boolean = false): PendingIntent {
val intent = Intent(context, HomeWidgetBackgroundReceiver::class.java)
intent.data = uri
intent.action = HOME_WIDGET_BACKGROUND_ACTION
intent.putExtra(EXTRA_IS_EXPEDITED, expedited)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Expedited flag integration is well-implemented.

The constant definition, parameter addition with a sensible default (false), and intent storage are all correct and maintain backward compatibility.

Verify that the Flutter/Dart API has been updated to expose this expedited parameter to plugin users. The Android native side is ready, but users need a way to set this flag from Dart.


🏁 Script executed:

#!/bin/bash
# Search for Flutter/Dart API that exposes the expedited parameter
rg -n --type dart -C3 'expedited'

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Find Dart/Flutter files in the plugin structure
fd -e dart -type f | head -20

Length of output: 170


🏁 Script executed:

#!/bin/bash
# Look for files that might define the Flutter API
rg -n 'getBroadcast' --type dart -C2

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Search for any reference to expedited with case-insensitive matching
rg -ni 'expedited' --type dart --type kotlin --type java

Length of output: 1404


🏁 Script executed:

#!/bin/bash
# Check the lib directory structure for plugin API files
find . -path '*/lib/*.dart' -o -path '*/lib/*/*.dart' 2>/dev/null | grep -E '(home_widget|plugin)' | head -20

Length of output: 257


🏁 Script executed:

#!/bin/bash
# Search for broadcast-related methods in Dart files
rg -n 'broadcast|update|enqueue' packages/home_widget/lib/ -C2

Length of output: 1361


🏁 Script executed:

#!/bin/bash
# Inspect the main Dart plugin API file
cat packages/home_widget/lib/src/home_widget.dart

Length of output: 10005


🏁 Script executed:

#!/bin/bash
# Check for TODO or FIXME comments related to expedited or broadcast
rg -n 'TODO|FIXME|todo' packages/home_widget/lib/ | grep -i 'expedited\|broadcast'

Length of output: 0


Add the expedited parameter to the Dart API.

The Android implementation is complete and ready to use the expedited flag, but the Flutter/Dart API has no way to expose this feature to users. The updateWidget() method and related broadcast trigger methods need the expedited parameter added to allow developers to enable expedited work requests from Dart.

Verify that changes are made to packages/home_widget/lib/src/home_widget.dart to add expedited: bool = false parameter to methods that trigger background work on Android (likely updateWidget()), and that this parameter is passed through the MethodChannel to the native Android layer.

🤖 Prompt for AI Agents
In packages/home_widget/lib/src/home_widget.dart (methods that trigger Android
background work, e.g., updateWidget) add an optional parameter expedited: bool =
false to the public API and to any internal helper methods that build the
platform call; include expedited in the arguments map passed to
MethodChannel.invokeMethod (key name "expedited" or match the Android
EXTRA_IS_EXPEDITED naming convention) so the boolean is forwarded to the native
side, and ensure the default remains false so existing callers are unaffected.

@ABausG

ABausG commented Nov 9, 2025

Copy link
Copy Markdown
Owner

Hi. Thanks for this. Can you please remove the changes in the Android implementation? They don't seem to be related to the fix you are describing in the title/description.
If you see a need for this please explain in an issue/new pr why that change on Android may be necessary

@ABausG ABausG marked this pull request as draft January 4, 2026 23:04
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.

"getInstalledWidgets" WidgetConfigurationIntent error - can't parse list of AppEntity

2 participants