Skip to content

Conversation

@ElonPark
Copy link
Member

@ElonPark ElonPark commented Sep 23, 2025

Background (Required)

  • IOS-1332
  • Optional property wrappers (OptionalDateValue, OptionalPolymorphicValue, LossyOptionalPolymorphicValue) couldn't clearly track the cause of decoding failures
  • Unable to distinguish between missing keys and null values, making debugging difficult
  • Improved to track decoding failure causes through the outcome property

Changes (Required)

Added Outcome Logic

  • OptionalDateValue: Improved to distinguish and set outcome for keyNotFound and valueWasNil cases
    • Also modified KeyedDecodingContainer extension methods for consistent behavior
  • OptionalPolymorphicValue: Added handling logic for keyNotFound and valueWasNil cases
  • LossyOptionalPolymorphicValue: Added handling logic for keyNotFound and valueWasNil cases

Code Structure Improvements

  • KeyedDecodingContainer+DefaultCodable.swift: Extracted KeyedDecodingContainer extension from DefaultCodable.swift into separate file
    • Improved code structure and readability

Reverted Previous Changes

  • DefaultEmptyPolymorphicArrayValue: Reverted error handling improvements for non-optional types
    • Determined that the previous implementation was more appropriate

Testing Approach (Required)

Unit Tests Added

  • OptionalDateValueResilientTests: Added tests for OptionalDateValue outcome logic
    • keyNotFound case: Tests when key is missing from JSON
    • valueWasNil case: Tests when value is null
    • decodedSuccessfully case: Tests successful decoding cases
    • Type mismatch error case: Tests that errors are thrown for incorrect types
    • Implemented tests for both ISO8601Strategy and TimestampStrategy

Impact on Existing Tests

  • DefaultEmptyPolymorphicArrayValueResilientTests: Adjusted test cases due to revert
  • Confirmed all existing tests pass successfully

Review Notes

  • The outcome property is designed to be accessible only in DEBUG mode, so there's no impact on production builds
  • Optional property wrappers now handle decoding failures in a consistent manner
  • The KeyedDecodingContainer extension separation is a refactoring to improve code structure
  • The revert of DefaultEmptyPolymorphicArrayValue was done after determining the existing behavior was intended

Summary by CodeRabbit

  • New Features

    • More resilient decoding: optional dates and polymorphic values now explicitly handle missing keys and nulls without throwing, exposing clear outcome states.
    • Enhanced default-value decoding: improved handling for Bool and RawRepresentable defaults with graceful fallbacks.
  • Refactor

    • decodeIfPresent behaviors/signatures adjusted to better represent optional outcome semantics.
  • Tests

    • Added comprehensive optional-date resilience tests and updated default/polymorphic tests to assert outcome-based resilience.

…onal DefaultCodable properties"

This reverts commit 89ccc72.

# Conflicts:
#	Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 23, 2025

📝 Walkthrough

Walkthrough

Resilient decoding was extended across date, default, and polymorphic wrappers: missing keys and explicit nulls are treated as non-fatal outcomes (e.g., keyNotFound, valueWasNil); DefaultCodable decoding helpers were refactored/moved and expanded (including a Bool strategy and RawRepresentable support); tests updated to reflect outcome-based semantics.

Changes

Cohort / File(s) Summary
OptionalDateValue & tests
Sources/.../DateValue/OptionalDateValue.swift, Tests/.../DateValue/OptionalDateValueResilientTests.swift
Add explicit handling for DecodingError.keyNotFound and DecodingError.valueNotFound in OptionalDateValue decoding; change KeyedDecodingContainer.decodeIfPresent signature/behavior to return OptionalDateValue<T>? with explicit outcomes; add resilient tests for ISO8601/Timestamp strategies.
DefaultCodable — API relocation & enhancements
Sources/.../Defaults/DefaultCodable.swift, Sources/.../Defaults/Extenstions/KeyedDecodingContainer+DefaultCodable.swift, Tests/.../Defaults/DefaultCodableResilientTests.swift
Remove public BoolCodableStrategy and old KeyedDecodingContainer extension from DefaultCodable.swift; add new KeyedDecodingContainer+DefaultCodable.swift implementing public decode methods for DefaultCodable (including Bool coercions and RawRepresentable handling) plus a private helper for unknown raw values; tests updated to expect outcome semantics (missing/null no longer produce errors).
Polymorphic: DefaultEmptyPolymorphicArrayValue signatures & tests
Sources/.../PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift, Tests/.../PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift
Adjust internal parameter naming for decode / decodeIfPresent; remove DEBUG-only error reporting for missing/null and consistently return standardized outcomes (keyNotFound / valueWasNil); tests updated accordingly.
Polymorphic optional & lossy values
Sources/.../PolymorphicCodable/LossyOptionalPolymorphicValue.swift, Sources/.../PolymorphicCodable/OptionalPolymorphicValue.swift
Add explicit handling for DecodingError.keyNotFound and DecodingError.valueNotFound in Decodable initializers to set wrappedValue = nil and appropriate outcome enum values; other errors rethrown.
Tests: resilient semantics adjustments
Tests/.../BetterCodable/*, Tests/.../PolymorphicCodable/*
Update many tests to assert outcome enums and that no error is produced for missing/null cases; remove or simplify explicit resilient error-reporting where no longer needed.

Sequence Diagram(s)

OptionalDateValue Decodable initializer (high-level)

sequenceDiagram
    participant Decoder
    participant Container as SingleValueContainer
    participant OptionalDateValue

    Note right of OptionalDateValue: init(from: Decoder)
    Decoder->>Decoder: makeSingleValueContainer()
    Decoder->>Container: container.decode(Formatter.RawValue.self)
    alt decode succeeds
        Container-->OptionalDateValue: rawValue
        OptionalDateValue->>OptionalDateValue: T.decode(rawValue) -> date / throw
        alt decode success
            OptionalDateValue-->Decoder: wrappedValue=date, outcome=decodedSuccessfully
        else decode failure
            OptionalDateValue-->Decoder: rethrow (report in DEBUG)
        end
    else DecodingError.keyNotFound
        Container-->OptionalDateValue: catch keyNotFound
        OptionalDateValue-->Decoder: wrappedValue=nil, outcome=keyNotFound
    else DecodingError.valueNotFound (raw type match)
        Container-->OptionalDateValue: catch valueNotFound
        OptionalDateValue-->Decoder: wrappedValue=nil, outcome=valueWasNil
    else other errors
        Container-->OptionalDateValue: rethrow
    end
Loading

KeyedDecodingContainer.decodeIfPresent for OptionalDateValue (high-level)

sequenceDiagram
    participant Caller
    participant KDC as KeyedDecodingContainer
    participant OptionalDateValue

    Caller->>KDC: decodeIfPresent(OptionalDateValue<T>.self, forKey:key)
    alt key missing
        KDC-->Caller: nil
    else key present but null
        KDC-->Caller: OptionalDateValue(wrappedValue=nil, outcome=valueWasNil)
    else value present
        KDC->>KDC: decode raw string/RawValue via singleValueContainer
        alt decode + T.decode success
            KDC-->Caller: OptionalDateValue(wrappedValue=date, outcome=decodedSuccessfully)
        else decode throws (format/type)
            KDC-->Caller: rethrow (or report in DEBUG)
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

Feature, Breaking Changes

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The PR adds many changes that extend beyond the narrow objective of adding outcome logic to optional wrappers: the KeyedDecodingContainer extension was extracted into a new KeyedDecodingContainer+DefaultCodable.swift file and reintroduces public API (including a new BoolCodableStrategy protocol and several public decode methods), and OptionalDateValue.decodeIfPresent had its public signature and generic constraints changed; these are public API/refactor changes that should be justified or split out because they affect the library surface and backward compatibility. Split non-essential refactors and public API surface changes (the DefaultCodable extension move, addition of BoolCodableStrategy, and the decodeIfPresent signature change) into a separate PR or clearly document and justify these breaking/behavioral changes in the PR description and changelog and include a compatibility/migration note and version bump as appropriate.
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Linked Issues Check ❓ Inconclusive The linked_issues entry only contains a terse title ("IOS-1332: 프로필 화면 UI 최신화") with no coding acceptance criteria, while the PR and pr_objectives claim IOS-1332 maps to adding outcome tracking; because the linked issue content in the provided data is incomplete and appears semantically different, I cannot conclusively verify that the code changes satisfy the issue as listed. Please attach the full IOS-1332 issue (or a link) and explicit acceptance criteria that map to the expected behavior (for example: "absent JSON keys and null values for optional/defaulted properties must be treated as non-errors and recorded via outcome") so reviewers can verify the implementation against the issue.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title "feat: Add outcome logic for optional property wrappers" is concise, follows conventional commit style, and correctly summarizes the primary change (adding outcome tracking for optional property wrappers), so it gives reviewers a clear sense of the main intent of the PR.
Description Check ✅ Passed The PR description closely follows the repository template and includes Background, Changes, Testing (detailed unit tests and impacts), and Review Notes, describing the outcome logic additions, the DefaultCodable refactor, reverted behavior, and test coverage; sections are populated with concrete details and testing approach so reviewers can understand scope and verification steps.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch task/elon/IOS-1332-update-outcome-logic

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

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bebc686 and d5cb51e.

📒 Files selected for processing (9)
  • Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (2 hunks)
  • Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (0 hunks)
  • Sources/KarrotCodableKit/BetterCodable/Defaults/Extenstions/KeyedDecodingContainer+DefaultCodable.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift (2 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/DateValue/OptionalDateValueResilientTests.swift (1 hunks)
  • Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift (4 hunks)
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift (2 hunks)
💤 Files with no reviewable changes (1)
  • Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift
🧰 Additional context used
📓 Path-based instructions (2)
Tests/KarrotCodableKitTests/**

⚙️ CodeRabbit configuration file

Tests/KarrotCodableKitTests/**: You are a senior Swift/iOS engineer reviewing runtime functionality tests for KarrotCodableKit's Codable extensions.

1. Runtime Behavior Testing [HIGH]

  • Verify JSON encoding/decoding scenarios cover real-world use cases
  • Check polymorphic type resolution testing across different strategies
  • Assess property wrapper testing (@DATEvalue, @DefaultFalse, etc.)
  • Validate error handling and edge case coverage

2. Test Data Quality [HIGH]

  • Review test JSON structures for realistic complexity
  • Check that TestDoubles provide comprehensive mock scenarios
  • Verify test data covers various data types and edge cases
  • Assess polymorphic test data represents actual usage patterns

3. Integration Testing [MEDIUM]

  • Check integration between macro-generated code and runtime functionality
  • Verify end-to-end scenarios combining multiple KarrotCodableKit features
  • Assess performance testing for large data structures
  • Review memory usage testing for complex polymorphic scenarios

Review Focus

  • Prioritize real-world usage scenario coverage
  • Focus on Codable correctness and performance implications
  • Mark comments with priority: [HIGH], [MEDIUM], or [LOW]

Files:

  • Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift
  • Tests/KarrotCodableKitTests/BetterCodable/DateValue/OptionalDateValueResilientTests.swift
  • Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift
Sources/KarrotCodableKit/**

⚙️ CodeRabbit configuration file

Sources/KarrotCodableKit/**: You are a senior Swift/iOS engineer reviewing runtime library code for KarrotCodableKit, a comprehensive Codable extension framework.

1. Codable Performance [HIGH]

  • Check encoding/decoding efficiency and memory usage
  • Verify proper handling of large JSON structures
  • Assess polymorphic type resolution performance
  • Review property wrapper overhead and optimization

2. Type Safety & Polymorphism [HIGH]

  • Validate PolymorphicCodable identifier-based type resolution
  • Check AnyCodable type erasure implementation for edge cases
  • Verify UnnestedPolymorphic macro integration with runtime components
  • Assess error handling in polymorphic decoding scenarios

3. API Design [HIGH]

  • Evaluate public interface consistency across modules
  • Check property wrapper ergonomics (@DefaultFalse, @DATEvalue, etc.)
  • Verify protocol design follows Swift API guidelines
  • Assess extensibility for new Codable patterns

4. BetterCodable Integration [MEDIUM]

  • Review property wrapper implementations for common patterns
  • Check date strategy implementations (ISO8601, RFC3339, etc.)
  • Verify default value and lossy conversion handling
  • Assess data encoding strategies (Base64, etc.)

5. Error Handling [MEDIUM]

  • Verify comprehensive DecodingError and EncodingError usage
  • Check PolymorphicCodableError provides sufficient context
  • Assess graceful fallback handling in polymorphic scenarios

Review Focus

  • Prioritize runtime correctness and performance over style
  • Focus on real-world JSON processing scenarios
  • Mark comments with priority: [HIGH], [MEDIUM], or [LOW]

Files:

  • Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/BetterCodable/Defaults/Extenstions/KeyedDecodingContainer+DefaultCodable.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift
🧬 Code graph analysis (4)
Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift (9)
Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (1)
  • decode (101-119)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift (1)
  • decode (12-29)
Sources/KarrotCodableKit/BetterCodable/LosslessValue/OptionalLosslessValue.swift (1)
  • decode (169-182)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift (1)
  • decode (12-21)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicArrayValue.swift (1)
  • decode (12-21)
Sources/KarrotCodableKit/BetterCodable/LossyValue/LossyDictionary.swift (1)
  • decode (273-295)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+PolymorphicLossyArrayValue.swift (1)
  • decode (12-72)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift (1)
  • decode (12-21)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • decode (56-71)
Sources/KarrotCodableKit/BetterCodable/Defaults/Extenstions/KeyedDecodingContainer+DefaultCodable.swift (2)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (154-166)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (46-46)
Tests/KarrotCodableKitTests/BetterCodable/DateValue/OptionalDateValueResilientTests.swift (1)
Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (1)
  • decode (101-119)
Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (5)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift (2)
  • decode (12-29)
  • decodeIfPresent (31-48)
Sources/KarrotCodableKit/BetterCodable/LosslessValue/OptionalLosslessValue.swift (1)
  • decode (169-182)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+LossyOptionalPolymorphicValue.swift (2)
  • decode (12-21)
  • decodeIfPresent (23-52)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicArrayValue.swift (2)
  • decode (12-21)
  • decodeIfPresent (23-57)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+OptionalPolymorphicValue.swift (1)
  • decodeIfPresent (23-46)
⏰ 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: Check Macro Compatibility
  • GitHub Check: Build and Test (16.4, release)
🔇 Additional comments (14)
Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift (2)

13-15: Signature tweak looks good and consistent with other wrappers [LOW]

Renamed internal parameter improves readability and matches the broader refactor.


31-48: Confirmed: keep decodeIfPresent behavior (null → non‑nil wrapper with .valueWasNil)

This is intentional and consistent with other helpers (see Sources/KarrotCodableKit/PolymorphicCodable/Extensions/KeyedDecodingContainer+DefaultEmptyPolymorphicArrayValue.swift and the OptionalPolymorphic*/DefaultCodable helpers) and covered by tests that assert .valueWasNil (Tests/...DefaultEmptyPolymorphicArrayValueResilientTests.swift). No call sites were found that rely on “nil means null” for this wrapper.

Tests/KarrotCodableKitTests/PolymorphicCodable/ArrayValue/DefaultEmptyPolymorphicArrayValueResilientTests.swift (2)

119-121: Good: missing key expects .keyNotFound and no error [LOW]

Matches the new resilience model.


140-142: Good: null value expects .valueWasNil and no error [LOW]

Consistent with the decoding extension behavior.

Tests/KarrotCodableKitTests/BetterCodable/Defaults/DefaultCodableResilientTests.swift (3)

81-87: Correct: missing keys should not surface errors under DEBUG [LOW]

Assertions align with the new DefaultCodable extension semantics.


345-356: Great: RawRepresentable missing keys assert .keyNotFound and no errors [LOW]

Outcome checks look right for both enums.


372-383: Great: RawRepresentable null values assert .valueWasNil and no errors [LOW]

Matches the updated decode path for DefaultCodable + RawRepresentable.

Sources/KarrotCodableKit/BetterCodable/DateValue/OptionalDateValue.swift (1)

100-119: OptionalDateValue keyed decoding looks solid. [MEDIUM]

Key checks for contains and decodeNil avoid throwing and set precise outcomes; decoding then happens via T.RawValue followed by T.decode. Good balance of resilience and performance.

Please confirm that all OptionalDateValue strategies have RawValue: Decodable and that no call sites rely on the previous decodeIfPresent specialization.

Tests/KarrotCodableKitTests/BetterCodable/DateValue/OptionalDateValueResilientTests.swift (5)

24-40: Great coverage for missing-key/null ISO8601 paths. [LOW]

The tests validate both outcome and no-error behavior cleanly.


64-85: Valid ISO8601 success-path checks. [LOW]

Asserting the exact epoch aligns with the strategy. Looks good.


87-119: Error-path assertions are appropriate. [LOW]

Type/format mismatch tests ensure non-resilient errors still surface.


163-207: Timestamp strategy tests cover double and integer inputs. [LOW]

Both numeric forms are validated; outcomes match expectations.


246-256: Direct single-value decoding negative case is correct. [LOW]

Decoding from {} should throw; good guard test.

Sources/KarrotCodableKit/BetterCodable/Defaults/Extenstions/KeyedDecodingContainer+DefaultCodable.swift (1)

120-175: RawRepresentable handling is robust. [MEDIUM]

Unknown raw values convert to UnknownNovelValueError unless frozen; error reporting is DEBUG-only with recovery outcome. Looks good.

@ElonPark ElonPark force-pushed the task/elon/IOS-1332-update-outcome-logic branch from d5cb51e to b03a4d5 Compare September 23, 2025 11:37
@ElonPark ElonPark force-pushed the task/elon/IOS-1332-update-outcome-logic branch from b03a4d5 to ca1dd3c Compare September 23, 2025 11:45
@ElonPark ElonPark force-pushed the task/elon/IOS-1332-update-outcome-logic branch from ca1dd3c to 80347a9 Compare September 23, 2025 11:54
@ElonPark ElonPark self-assigned this Sep 23, 2025
@ElonPark ElonPark requested a review from OhKanghoon September 23, 2025 11:57
@ElonPark ElonPark added the Update Update feature label Sep 23, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ca1dd3c and 80347a9.

📒 Files selected for processing (4)
  • Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift (0 hunks)
  • Sources/KarrotCodableKit/BetterCodable/Defaults/Extenstions/KeyedDecodingContainer+DefaultCodable.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift (1 hunks)
  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift (2 hunks)
💤 Files with no reviewable changes (1)
  • Sources/KarrotCodableKit/BetterCodable/Defaults/DefaultCodable.swift
🧰 Additional context used
📓 Path-based instructions (1)
Sources/KarrotCodableKit/**

⚙️ CodeRabbit configuration file

Sources/KarrotCodableKit/**: You are a senior Swift/iOS engineer reviewing runtime library code for KarrotCodableKit, a comprehensive Codable extension framework.

1. Codable Performance [HIGH]

  • Check encoding/decoding efficiency and memory usage
  • Verify proper handling of large JSON structures
  • Assess polymorphic type resolution performance
  • Review property wrapper overhead and optimization

2. Type Safety & Polymorphism [HIGH]

  • Validate PolymorphicCodable identifier-based type resolution
  • Check AnyCodable type erasure implementation for edge cases
  • Verify UnnestedPolymorphic macro integration with runtime components
  • Assess error handling in polymorphic decoding scenarios

3. API Design [HIGH]

  • Evaluate public interface consistency across modules
  • Check property wrapper ergonomics (@DefaultFalse, @DATEvalue, etc.)
  • Verify protocol design follows Swift API guidelines
  • Assess extensibility for new Codable patterns

4. BetterCodable Integration [MEDIUM]

  • Review property wrapper implementations for common patterns
  • Check date strategy implementations (ISO8601, RFC3339, etc.)
  • Verify default value and lossy conversion handling
  • Assess data encoding strategies (Base64, etc.)

5. Error Handling [MEDIUM]

  • Verify comprehensive DecodingError and EncodingError usage
  • Check PolymorphicCodableError provides sufficient context
  • Assess graceful fallback handling in polymorphic scenarios

Review Focus

  • Prioritize runtime correctness and performance over style
  • Focus on real-world JSON processing scenarios
  • Mark comments with priority: [HIGH], [MEDIUM], or [LOW]

Files:

  • Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift
  • Sources/KarrotCodableKit/BetterCodable/Defaults/Extenstions/KeyedDecodingContainer+DefaultCodable.swift
🧬 Code graph analysis (1)
Sources/KarrotCodableKit/BetterCodable/Defaults/Extenstions/KeyedDecodingContainer+DefaultCodable.swift (2)
Sources/KarrotCodableKit/Resilient/ErrorReporting.swift (1)
  • reportError (154-166)
Sources/KarrotCodableKit/Resilient/ResilientDecodingOutcome.swift (1)
  • recoveredFrom (46-46)
⏰ 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: Build and Test (16.4, release)
  • GitHub Check: Check Macro Compatibility
🔇 Additional comments (9)
Sources/KarrotCodableKit/PolymorphicCodable/OptionalPolymorphicValue.swift (2)

11-19: Documentation accurately reflects the resilient handling behavior [HIGH]

The updated documentation correctly describes the new behavior where keyNotFound and valueNotFound (when type matches) are handled gracefully by setting nil with appropriate outcomes, while other errors are rethrown. This addresses the previous concern about documentation-code divergence.


66-73: Outcome tracking implementation is correct and consistent [HIGH]

The implementation properly distinguishes between keyNotFound and valueWasNil cases, setting appropriate outcomes. The type check in the valueNotFound handler correctly ensures we only handle cases where the missing value is of the expected polymorphic type.

Sources/KarrotCodableKit/PolymorphicCodable/LossyOptionalPolymorphicValue.swift (2)

15-22: Update documentation to reflect decoder.reportError usage [MEDIUM]

The documentation still mentions "logs the encountered error using print" (line 21), but the implementation now uses decoder.reportError for error reporting. Please update this line to accurately describe the current error reporting mechanism.

Apply this diff to fix the documentation:

 /// Unlike `@PolymorphicValue`, if the `PolymorphicType.decode(from:)` method throws *any* error during decoding
 /// (e.g., missing identifier key, unknown identifier value, invalid data for the concrete type, or even a missing key for the value itself),
 /// this wrapper catches the error and assigns `nil` to the `wrappedValue`.
-/// It logs the encountered error using `print`.
+/// In DEBUG builds, it reports the encountered error via the decoder's error reporter.

68-75: Outcome handling for resilient decoding is properly implemented [HIGH]

The new error handling branches correctly set specific outcomes for keyNotFound and valueWasNil cases before falling through to the general error recovery path. This provides better granularity for debugging while maintaining the lossy behavior expected from this wrapper.

Sources/KarrotCodableKit/BetterCodable/Defaults/Extenstions/KeyedDecodingContainer+DefaultCodable.swift (5)

10-10: Well-designed protocol for Bool strategies. [LOW]

Good use of protocol composition to create a specialized marker protocol for Bool-specific default strategies. This provides clear intent and enables targeted method overloading.


121-188: Excellent RawRepresentable handling with frozen/unfrozen distinction. [MEDIUM]

The implementation properly differentiates between frozen and unfrozen enums, providing appropriate error types for each case. The DEBUG-only outcome tracking maintains production performance while aiding development debugging.


36-44: Simplify control flow - else branch is unreachable. [LOW]

After checking contains(key) and decodeNil(forKey:) != true, the decodeIfPresent call will either return a non-nil value or throw an error - it won't return nil. The else branch (lines 38-44) is dead code.

Refactor to use a do-catch block for cleaner error handling:

-    // Try to decode normally
-    if let value = try decodeIfPresent(DefaultCodable<P>.self, forKey: key) {
-      return value
-    } else {
-      #if DEBUG
-      return DefaultCodable(wrappedValue: P.defaultValue, outcome: .keyNotFound)
-      #else
-      return DefaultCodable(wrappedValue: P.defaultValue)
-      #endif
-    }
+    // Try to decode normally
+    do {
+      return try decode(DefaultCodable<P>.self, forKey: key)
+    } catch let error as DecodingError {
+      // Handle decoding errors by falling back to default
+      #if DEBUG
+      let outcome: ResilientDecodingOutcome = (error.isKeyNotFound) ? .keyNotFound : .recoveredFrom(error, wasReported: false)
+      return DefaultCodable(wrappedValue: P.defaultValue, outcome: outcome)
+      #else
+      return DefaultCodable(wrappedValue: P.defaultValue)
+      #endif
+    }

93-98: Fix double-optional issue with decodeIfPresent. [HIGH]

The current code has a compilation issue: try? decodeIfPresent(Int.self, forKey: key) returns Int?? (double optional), but NSNumber(value:) expects a non-optional Int. Since you've already verified the key exists and isn't nil, use decode directly.

Apply this fix to resolve the compilation error:

       if
-        let intValue = try? decodeIfPresent(Int.self, forKey: key),
+        let intValue = try? decode(Int.self, forKey: key),
         let bool = Bool(exactly: NSNumber(value: intValue))
       {
         return DefaultCodable(wrappedValue: bool)
       }

100-105: Bool(stringValue) initializer doesn't exist in Swift. [HIGH]

Swift's standard library doesn't provide a Bool initializer that accepts a String. This will cause a compilation error.

Implement explicit string-to-bool parsing:

       if
-        let stringValue = try? decodeIfPresent(String.self, forKey: key),
-        let bool = Bool(stringValue)
+        let stringValue = try? decode(String.self, forKey: key)
       {
-        return DefaultCodable(wrappedValue: bool)
+        let trimmed = stringValue.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
+        switch trimmed {
+        case "true", "1", "yes":
+          return DefaultCodable(wrappedValue: true)
+        case "false", "0", "no":
+          return DefaultCodable(wrappedValue: false)
+        default:
+          break // Fall through to error handling
+        }
       }

Alternatively, add a private Bool extension:

private extension Bool {
  init?(from string: String) {
    let trimmed = string.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
    switch trimmed {
    case "true", "1", "yes":
      self = true
    case "false", "0", "no":
      self = false
    default:
      return nil
    }
  }
}

Then use it as:

if
  let stringValue = try? decode(String.self, forKey: key),
  let bool = Bool(from: stringValue)
{
  return DefaultCodable(wrappedValue: bool)
}

@ElonPark ElonPark merged commit c82af3c into main Sep 23, 2025
4 checks passed
@ElonPark ElonPark deleted the task/elon/IOS-1332-update-outcome-logic branch September 23, 2025 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Update Update feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants