feat: omit nil optional fields on encode (IOS-4537)#23
Conversation
Add a KeyedEncodingContainer.encode(_:forKey:) overload that skips the key when wrappedValue is nil, matching Apple's default Codable behavior (nil optionals are omitted, not encoded as an explicit null). Mirrors the existing decode(_:forKey:) overload, so round-trips restore nil. Claude-Session: https://claude.ai/code/session_01BSXaRrJaLrjWQaiJSMy45Q
Add the nil-omission encoding overload for the lossy variant, consistent with OptionalPolymorphicValue. Claude-Session: https://claude.ai/code/session_01BSXaRrJaLrjWQaiJSMy45Q
Add the nil-omission encoding overload for the optional array wrapper. An empty array ([]) is non-nil and stays as []; only nil omits the key. Also update the now-stale 'encodes as null' doc. Claude-Session: https://claude.ai/code/session_01BSXaRrJaLrjWQaiJSMy45Q
Add the nil-omission encoding overload for the optional lossy array wrapper. Empty arrays are preserved; only nil omits the key. Also update the stale doc and wrap two over-long doc lines. Claude-Session: https://claude.ai/code/session_01BSXaRrJaLrjWQaiJSMy45Q
Add the nil-omission encoding overload for OptionalDateValue (BetterCodable), consistent with the polymorphic optional wrappers. Claude-Session: https://claude.ai/code/session_01BSXaRrJaLrjWQaiJSMy45Q
Add the nil-omission encoding overload for OptionalLosslessValue (BetterCodable). Claude-Session: https://claude.ai/code/session_01BSXaRrJaLrjWQaiJSMy45Q
Add the nil-omission encoding overload for LossyOptional. Constrained to DefaultCodable<DefaultNilStrategy> so other DefaultCodable wrappers (DefaultFalse, DefaultEmptyArray, ...) keep encoding their non-optional default value. Claude-Session: https://claude.ai/code/session_01BSXaRrJaLrjWQaiJSMy45Q
|
Warning Review limit reached
More reviews will be available in 41 minutes and 5 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughAdds keyed encoding overloads for several optional wrapper types so nil values are omitted from encoded keyed containers. Updates wrapper docs and debug-only plumbing for optional polymorphic arrays. Expands tests to cover nil omission, preserved empty arrays, and round-trip decoding. ChangesOptional nil-encoding behavior
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@Tests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueTests.swift`:
- Around line 209-227: The current tests cover keyed omission and empty-array
preservation, but they miss the unkeyed null behavior required by the PR
contract. Add a focused regression test around OptionalPolymorphicArrayValue’s
encode(to:) and decode paths in an unkeyed array context, verifying that a nil
element is encoded as null and round-trips back to nil. Use the existing
OptionalPolymorphicArrayDummyResponse / OptionalPolymorphicArrayValue test area
to keep the new case close to the current polymorphic array coverage.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: ASSERTIVE
Plan: Pro
Run ID: a7d93427-cfd1-4a68-9b1d-f7603459ce43
📒 Files selected for processing (16)
Sources/KarrotCodableKit/BetterCodable/DateValue/KeyedEncodingContainer+OptionalDateValue.swiftSources/KarrotCodableKit/BetterCodable/LosslessValue/KeyedEncodingContainer+OptionalLosslessValue.swiftSources/KarrotCodableKit/BetterCodable/LossyValue/KeyedEncodingContainer+LossyOptional.swiftSources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedEncodingContainer+LossyOptionalPolymorphicValue.swiftSources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedEncodingContainer+OptionalPolymorphicArrayValue.swiftSources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedEncodingContainer+OptionalPolymorphicLossyArrayValue.swiftSources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedEncodingContainer+OptionalPolymorphicValue.swiftSources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicArrayValue.swiftSources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicLossyArrayValue.swiftTests/KarrotCodableKitTests/BetterCodable/DateValue/OptionalDateValueOmitNilTests.swiftTests/KarrotCodableKitTests/BetterCodable/LosslessValue/OptionalLosslessValueOmitNilTests.swiftTests/KarrotCodableKitTests/BetterCodable/LossyValue/LossyOptionalOmitNilTests.swiftTests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicArrayValueTests.swiftTests/KarrotCodableKitTests/PolymorphicCodable/OptionalPolymorphicLossyArrayValueTests.swiftTests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/LossyOptionalPolymorphicValueTests.swiftTests/KarrotCodableKitTests/PolymorphicCodable/OptionalValue/OptionalPolymorphicValueTests.swift
…Value In an unkeyed container (array element) there is no key to omit, so a nil wrapper is encoded as an explicit null, matching Apple's [T?] behavior. This locks down the wrapper's encode(to:) path that the keyed encode(_:forKey:) overload does not exercise. Claude-Session: https://claude.ai/code/session_01BSXaRrJaLrjWQaiJSMy45Q
Overview
Changes the encoding behavior of Optional property wrappers/macros so that a
nilvalue omits the key entirely, matching Apple's defaultCodablebehavior — instead of writing an explicitnullviaencodeNil().Background
Swift's synthesized
Codableomits the key forniloptional properties (viaencodeIfPresent). KarrotCodableKit's Optional wrappers instead emitted an explicitnullthroughsingleValueContainer().encodeNil(), diverging from that behavior.Changes
Each Optional wrapper gains a
KeyedEncodingContainer.encode(_:forKey:)overload that skips the key whenwrappedValue == nil. This is exactly symmetric with the existingdecode(_:forKey:)overload (contains(key)check). Macros (@CustomCodablesynthesis /@UnnestedPolymorphic) call this container overload automatically, so no macro changes are required.Covered 7 wrappers (split into one commit per wrapper):
DefaultCodable<DefaultNilStrategy>so otherDefaultCodablewrappers (@DefaultFalse,@DefaultEmptyArray, ...) are unaffectedBehavior
nil→ key omitted[]→ preserved (distinct from nil)null(same as Apple's[T?])nilEncoded output now omits the key for
niloptional fields instead of writing"key": null. Consumers that distinguish explicit null from key absence (e.g. PATCH semantics where null = "delete field") may be affected.Tests
[]) preservation regression testshttps://claude.ai/code/session_01BSXaRrJaLrjWQaiJSMy45Q
Summary by CodeRabbit
New Features
nullfor several supported value types.Bug Fixes
nilcorrectly.Tests