Skip to content

Add AppStorage support for Date and Date? types (fixes #34)#36

Merged
leogdion merged 2 commits intomainfrom
34-appstorage-optional-date
Jan 7, 2026
Merged

Add AppStorage support for Date and Date? types (fixes #34)#36
leogdion merged 2 commits intomainfrom
34-appstorage-optional-date

Conversation

@leogdion
Copy link
Member

@leogdion leogdion commented Jan 6, 2026

Summary

  • Added AppStorage initializers for Date and Date? types
  • Bumped minimum deployment targets to iOS 18/macOS 15 to support native Date storage
  • Added comprehensive test coverage
  • Updated linting script to use hardcoded 2025 year

Changes

AppStorage Extensions

  • Added Date initializer in AppStorage+AppStored.swift for non-optional Date storage with wrapped values
  • Added Date? initializer in AppStorage+ExpressibleByNilLiteral.swift for optional Date storage

Deployment Target Updates

Updated minimum platform versions in Package.swift:

  • iOS 15.0 → 18.0
  • macOS 12.0 → 15.0
  • tvOS 15.0 → 18.0
  • watchOS 8.0 → 11.0
  • visionOS 1.0 → 2.0
  • Mac Catalyst 15.0 → 18.0

Tests

Added AppStorageDateTests.swift with 8 comprehensive tests:

  • Non-optional Date storage and updates
  • Optional Date? storage with values and nil
  • Persistence across instances
  • Both .describing and .reflecting key types
  • Edge cases (distantPast, distantFuture, now)

Cleanup

  • Removed obsolete @available(iOS 15.0, *) annotation from ValueTextBubble.swift
  • Updated lint script to pass -y 2025 parameter for consistent copyright years
  • Updated CLAUDE.md documentation with new platform requirements

Breaking Changes

⚠️ This is a breaking change - bumping minimum iOS from 15.0 to 18.0 and macOS from 12.0 to 15.0 will require users to update their apps' deployment targets.

Test Plan

Example Usage

// Non-optional Date
enum LastSync: AppStored {
  typealias Value = Date
  static let keyType: KeyType = .describing
}

@AppStorage(wrappedValue: .now, for: LastSync.self) private var lastSyncDate

// Optional Date
enum OnboardingComplete: AppStored {
  typealias Value = Date?
  static let keyType: KeyType = .reflecting
}

@AppStorage(for: OnboardingComplete.self) private var onboardedAt

Fixes #34

🤖 Generated with Claude Code


Perform an AI-assisted review on CodePeer.com

Summary by CodeRabbit

  • Platform Support Changes

    • Lowered macOS minimum deployment target from 15 to 12.
  • New Features / Improvements

    • Added AppStorage initializers to better support Date and Date? values.
    • Removed iOS 15 availability restriction from ValueTextBubble.
  • Tests

    • Added comprehensive AppStorage Date tests; removed an old test scaffold.
  • Chores

    • Updated lint header generation to include 2025 year flag.

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

@leogdion leogdion linked an issue Jan 6, 2026 that may be closed by this pull request
@coderabbitai
Copy link

coderabbitai bot commented Jan 6, 2026

📝 Walkthrough

Walkthrough

Adds AppStorage initializers for Date and Date?, updates Package.swift macOS target to 12.0, removes an iOS availability annotation from ValueTextBubble, updates lint header year flags to 2025, adds CLAUDE.md, adds comprehensive AppStorage date tests and deletes a placeholder test file.

Changes

Cohort / File(s) Summary
Documentation & Manifest
CLAUDE.md, Package.swift
Adds CLAUDE.md; changes macOS deployment target in Package.swift from .v15 to .v12.
Scripts
Scripts/lint.sh
Adds -y 2025 flag to header.sh invocations for Sources and Tests.
AppStorage — Date initializers
Sources/RadiantKit/PropertyWrappers/AppStorage/...
Sources/RadiantKit/PropertyWrappers/AppStorage/AppStorage+AppStored.swift, Sources/RadiantKit/PropertyWrappers/AppStorage/AppStorage+ExpressibleByNilLiteral.swift
Adds public initializers for AppStorage supporting Date and optional Date? when used with AppStored types (new overloads constrained to AppStoredType.Value == Date and Value == Date?).
Views
Sources/RadiantKit/Views/ValueTextBubble.swift
Removes @available(iOS 15.0, *) availability attribute from ValueTextBubble type.
Tests
Tests/RadiantKitTests/AppStorageDateTests.swift, Tests/RadiantKitTests/RadiantKitTests.swift
Adds AppStorageDateTests.swift with extensive Date/Date? coverage; removes RadiantKitTests.swift placeholder test file.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Poem

🐇 In burrows of code I quietly hop,
Dates tucked in storage, no thump nor drop.
New tests spring like clover by dawn,
Targets adjusted, old guards withdrawn.
🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes several out-of-scope changes beyond the #34 requirements: platform target version bumps, ValueTextBubble annotation removal, and lint script updates that were not part of the issue scope. Review whether the platform target bumps (iOS 12→18, macOS 15, etc.) were necessary for Date support or should be separate. Consider if ValueTextBubble annotation removal and lint script updates belong in this PR.
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding AppStorage support for Date and Date? types, with a reference to the fixed issue.
Linked Issues check ✅ Passed The PR implements the required AppStorage initializer for Date? as specified in #34, adds Date support for non-optional values, includes comprehensive tests, and resolves the compilation error.
✨ Finishing touches
  • 📝 Generate docstrings

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.

@codecov
Copy link

codecov bot commented Jan 6, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 3.59%. Comparing base (51bcc14) to head (d29f5e8).

Additional details and impacted files
@@           Coverage Diff            @@
##            main     #36      +/-   ##
========================================
+ Coverage   0.00%   3.59%   +3.59%     
========================================
  Files         24      24              
  Lines        351     362      +11     
========================================
+ Hits           0      13      +13     
+ Misses       351     349       -2     
Flag Coverage Δ
6.2 0.00% <ø> (ø)
ios18.6 4.31% <100.00%> (+4.31%) ⬆️
ios26.2 3.54% <0.00%> (?)
macos 0.00% <0.00%> (ø)
nightly-6.3 0.00% <ø> (ø)
spm 3.74% <100.00%> (+3.74%) ⬆️
tvos26.2 ?
ubuntu 0.00% <ø> (ø)
watchos11.2 0.00% <0.00%> (?)
watchos11.5 ?
watchos26.2 ?

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

@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

🤖 Fix all issues with AI Agents
In @CLAUDE.md:
- Line 7: The opening platform-version sentence ("iOS 15+, macOS 12+, tvOS 15+,
watchOS 8+, visionOS 1+") is out of sync with the Platform Support section and
Package.swift; update that sentence to exactly match the deployment target
versions declared in the Platform Support section and Package.swift (use the
exact iOS, macOS, tvOS, watchOS, and visionOS version numbers from those
sources) so the README and package metadata are consistent.

In @Package.swift:
- Around line 32-39: Remove the now-redundant @available(macOS 14.0, iOS 17.0,
watchOS 10.0, tvOS 17.0, *) annotations in SetupPublishers.swift (there are
three occurrences) because Package.swift raises the package minimums to iOS 18 /
macOS 15 / tvOS 18 / watchOS 11 / visionOS 2; delete those attribute lines from
the relevant functions/types (the three annotated declarations) and ensure no
follow-up conditional code depends on those availability checks.
🧹 Nitpick comments (2)
CLAUDE.md (1)

124-132: Consider adding a language identifier to the code block.

The ASCII diagram could use a language identifier like text or plaintext to satisfy markdown linting (MD040).

🔎 Suggested fix
-```
+```text
 RadiantKit (foundation)
    ↑         ↑
    |         |
    |         |
 RadiantDocs  RadiantPaging

 RadiantProgress (independent)
 ```
Tests/RadiantKitTests/AppStorageDateTests.swift (1)

66-70: Consider using #require for cleaner early exit.

The guard/Issue.record/return pattern can be simplified with Swift Testing's #require macro, which throws on failure and removes the need for explicit early return.

🔎 Suggested fix
-    guard let store = UserDefaults(suiteName: #function) else {
-      Issue.record("Failed to create UserDefaults")
-      return
-    }
+    let store = try #require(UserDefaults(suiteName: #function))

This pattern can be applied to all test methods in this file.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c18df55 and d5fd58b.

📒 Files selected for processing (7)
  • CLAUDE.md
  • Package.swift
  • Scripts/lint.sh
  • Sources/RadiantKit/PropertyWrappers/AppStorage/AppStorage+AppStored.swift
  • Sources/RadiantKit/PropertyWrappers/AppStorage/AppStorage+ExpressibleByNilLiteral.swift
  • Sources/RadiantKit/Views/ValueTextBubble.swift
  • Tests/RadiantKitTests/AppStorageDateTests.swift
💤 Files with no reviewable changes (1)
  • Sources/RadiantKit/Views/ValueTextBubble.swift
🧰 Additional context used
🧬 Code graph analysis (1)
Package.swift (1)
Sources/RadiantProgress/SetupPublishers.swift (3)
  • macOS (45-75)
  • macOS (84-103)
  • macOS (112-137)
🪛 LanguageTool
CLAUDE.md

[uncategorized] ~197-~197: The official name of this software platform is spelled with a capital “H”.
Context: ...Add corresponding test target 5. Update .github/workflows/RadiantKit.yml if platform-s...

(GITHUB)

🪛 markdownlint-cli2 (0.18.1)
CLAUDE.md

98-98: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


103-103: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


108-108: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


113-113: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


118-118: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


124-124: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (4)
Scripts/lint.sh (1)

47-48: LGTM — hardcoded year appears intentional.

The -y 2025 flag hardcodes the copyright year. If dynamic year generation is preferred in the future, consider using $(date +%Y).

Sources/RadiantKit/PropertyWrappers/AppStorage/AppStorage+ExpressibleByNilLiteral.swift (1)

139-155: LGTM — Consistent with existing optional type initializers.

The new Date? initializer follows the established pattern used for Bool?, Int?, Double?, String?, URL?, and Data?. The constraints and delegation are correct.

Tests/RadiantKitTests/AppStorageDateTests.swift (1)

61-220: LGTM — Comprehensive test coverage for Date and Date? storage.

The test suite covers:

  • Non-optional and optional Date storage
  • Value updates and persistence verification
  • Both .describing and .reflecting key types
  • Setting optional values to nil

Good use of isolated UserDefaults suites with proper cleanup via defer.

Sources/RadiantKit/PropertyWrappers/AppStorage/AppStorage+AppStored.swift (1)

145-161: LGTM — Consistent with existing non-optional type initializers.

The new Date initializer follows the established pattern used for Bool, Int, Double, String, URL, and Data. The constraints, delegation, and documentation are correct.

Comment on lines 32 to 39
platforms: [
.iOS(.v15),
.macCatalyst(.v15),
.macOS(.v12),
.tvOS(.v15),
.visionOS(.v1),
.watchOS(.v8)
.iOS(.v18),
.macCatalyst(.v18),
.macOS(.v15),
.tvOS(.v18),
.visionOS(.v2),
.watchOS(.v11)
],
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

AppStorage Date value support SwiftUI iOS macOS version requirements

💡 Result:

Short answer:

  • Native Date support in @AppStorage was added in SwiftUI for iOS 18 / macOS 15 (and corresponding tvOS/watchOS/visionOS versions). Use @available(iOS 18.0, macOS 15.0, ...) when using @AppStorage with Date. [1]

Before those OS SDKs:

  • @AppStorage does NOT natively accept Date; typical workarounds are: store a TimeInterval/Double shadow value, make Date conform to RawRepresentable (String/Int) or encode to Data/String, or use a custom Converter wrapper. [2][3]

Sources

  • Swift forums thread noting the new Date initializer and availability (iOS 18 / macOS 15). [1]
  • Community guides showing pre-iOS‑18 workarounds (shadow Double, RawRepresentable, string/ISO8601 or JSON encoding). [2][3]

🏁 Script executed:

# Check SetupPublishers.swift for the @available annotations mentioned
fd "SetupPublishers.swift" --type f

Repository: brightdigit/RadiantKit

Length of output: 111


🏁 Script executed:

wc -l "Sources/RadiantProgress/SetupPublishers.swift"

Repository: brightdigit/RadiantKit

Length of output: 115


🏁 Script executed:

sed -n '40,50p;80,90p;108,118p' "Sources/RadiantProgress/SetupPublishers.swift"

Repository: brightdigit/RadiantKit

Length of output: 1464


Breaking change: Platform minimums significantly raised to support native Date storage in AppStorage.

Apple added native Date support to AppStorage in iOS 18, macOS 15 (and corresponding versions for tvOS 18, watchOS 11, visionOS 2). The platform version bumps in Package.swift are required to use this feature.

The @available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) annotations in Sources/RadiantProgress/SetupPublishers.swift (lines 44, 83, 111) are now redundant since the package minimum exceeds those versions and can be safely removed.

🤖 Prompt for AI Agents
In @Package.swift around lines 32 - 39, Remove the now-redundant
@available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) annotations in
SetupPublishers.swift (there are three occurrences) because Package.swift raises
the package minimums to iOS 18 / macOS 15 / tvOS 18 / watchOS 11 / visionOS 2;
delete those attribute lines from the relevant functions/types (the three
annotated declarations) and ensure no follow-up conditional code depends on
those availability checks.

Implements issue #34 by adding native Date support to AppStorage
extensions. Bumps minimum deployment targets to iOS 18/macOS 15
to leverage SwiftUI's native Date storage capabilities.

- Add Date initializer to AppStorage+AppStored
- Add Date? initializer to AppStorage+ExpressibleByNilLiteral
- Update minimum deployment targets across all platforms
- Remove obsolete iOS 15.0 availability annotation
- Add comprehensive test coverage for Date storage
- Update lint script to use hardcoded 2025 year

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@leogdion leogdion force-pushed the 34-appstorage-optional-date branch from d5fd58b to d29f5e8 Compare January 6, 2026 23:44
@claude
Copy link

claude bot commented Jan 6, 2026

Pull Request Review

This PR adds native Date and Date? support to the @AppStorage property wrapper system. The implementation is well-structured and follows the existing codebase patterns. Here's my detailed feedback:


✅ Strengths

Code Quality

  • Consistent patterns: The new Date initializers follow the exact same pattern as existing types (Bool, Int, Double, String, URL, Data), maintaining excellent consistency
  • Type safety: Generic constraints properly enforce type matching between AppStoredType.Value and Value
  • Documentation: Doc comments are clear and follow the established format
  • Copyright headers: All files have proper 2025 headers managed by the lint script

Test Coverage

  • Comprehensive testing: 8 well-designed tests cover both optional and non-optional Date storage
  • Edge cases: Tests include distantPast, distantFuture, and .now
  • Key types: Both .describing and .reflecting key types are tested
  • Persistence: Tests verify data persists across AppStorage instances
  • Proper cleanup: Using UserDefaults(suiteName:) with defer { removePersistentDomain } prevents test pollution
  • MainActor isolation: Correctly marked @MainActor for SwiftUI compatibility

Architecture

  • Platform awareness: Correctly conditioned with #if canImport(SwiftUI)
  • Module separation: New code properly placed in RadiantKit foundation module

⚠️ Potential Issues

1. Breaking Change - Deployment Target Bump

The PR description acknowledges this is breaking (iOS 15→18, macOS 12→15), which is significant:

Concern: The PR body states this change was made "to support native Date storage," but AppStorage has supported Date natively since iOS 15/macOS 12. According to Apple's documentation, the init(_:wrappedValue:store:) initializer for Date was available in iOS 15.0+.

Recommendation:

  • Verify if the deployment target bump is actually necessary for this feature
  • If not required, consider reverting to the original deployment targets (iOS 15/macOS 12) to avoid breaking changes
  • If there's a specific iOS 18/macOS 15 feature being used, document what it is

2. Lint Script Year Hardcoding

-    $PACKAGE_DIR/scripts/header.sh -d  $PACKAGE_DIR/Sources -c "Leo Dion" -o "BrightDigit" -p "RadiantKit"
+    $PACKAGE_DIR/scripts/header.sh -d  $PACKAGE_DIR/Sources -c "Leo Dion" -o "BrightDigit" -p "RadiantKit" -y 2025

Issue: Hardcoding the year to 2025 will require manual updates every year.

Recommendation: Consider using dynamic year generation in the header script itself, or have the lint script calculate the current year automatically.

3. Missing Test for Persistence Verification

The tests verify that values can be set and retrieved within the same test, but there's no explicit test that verifies the underlying UserDefaults actually contains the correct data format.

Suggestion: Consider adding a test that directly inspects store.object(forKey:) after setting a Date to verify the storage format is correct (should be a Date object, not a string or timestamp).


🔍 Minor Observations

Test Naming

The test type names are very explicit:

  • TestDateStored, TestOptionalDateStored
  • TestReflectingDateStored, TestReflectingOptionalDateStored

These are clear and follow good testing conventions. No changes needed.

Code Duplication

The two new initializers (non-optional in AppStorage+AppStored.swift and optional in AppStorage+ExpressibleByNilLiteral.swift) are essentially duplicating the pattern for 6+ other types. This is consistent with the existing architecture, so while there might be opportunities for generic abstraction, that would be a separate refactoring effort.


🔒 Security & Performance

  • Security: No concerns. Date values in UserDefaults pose no security risks beyond what already exists
  • Performance: Native Date storage in AppStorage is efficient (Apple's implementation)
  • Concurrency: Proper @MainActor isolation in tests ensures thread safety

📝 Documentation

The PR description is excellent:


Final Recommendation

Approve with minor revisions:

  1. Critical: Verify if the deployment target bump is actually necessary. If AppStorage Date support exists in iOS 15, consider reverting the deployment targets to avoid breaking changes.
  2. Recommended: Fix the hardcoded year in lint.sh to use dynamic date or handle it in the header script.
  3. Optional: Add a test that verifies the UserDefaults storage format directly.

The core implementation is solid and follows all the project's architectural patterns. The test coverage is comprehensive. The main concern is whether the breaking change is justified.


Code Quality: ⭐⭐⭐⭐⭐ (5/5)
Test Coverage: ⭐⭐⭐⭐⭐ (5/5)
Architecture: ⭐⭐⭐⭐⭐ (5/5)
Breaking Change Justification: ⭐⭐⭐ (3/5 - needs clarification)

Great work overall! 🚀

Lower minimum macOS deployment target from v15 to v12 while maintaining
support for newer features through conditional availability.

Changes:
- Package.swift: Set macOS minimum to v12 (was v15)
- CLAUDE.md: Update platform support documentation
- AppStorage Date support: Add @available guards for macOS 15+ features
  - AppStorage+AppStored.swift: Guard Date initializer
  - AppStorage+ExpressibleByNilLiteral.swift: Guard Date? initializer
- AppStorageDateTests.swift: Restructure with runtime availability checks
  - Tests now record issues on unsupported platforms instead of failing
  - Each test body includes #if canImport(SwiftUI) and #available checks
- Remove placeholder RadiantKitTests.swift file

All existing @available guards from commit c18df55 remain in place for
macOS 13+ and macOS 14+ APIs. Package compiles and tests pass.

[skip ci]

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Copy link

@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)
Package.swift (1)

32-39: Critical: macOS deployment target must be v15, not v12.

Line 35 sets the macOS minimum to v12, but the PR objectives, coding guidelines, learnings, and the technical requirement for native Date support in AppStorage all mandate macOS 15.0. Native Date storage in AppStorage was introduced in iOS 18 / macOS 15, and setting macOS to v12 will cause compilation failures when using the new AppStorage initializers for Date and Date? added in this PR.

🔎 Proposed fix
   platforms: [
     .iOS(.v18),
     .macCatalyst(.v18),
-    .macOS(.v12),
+    .macOS(.v15),
     .tvOS(.v18),
     .visionOS(.v2),
     .watchOS(.v11)
   ],

Based on coding guidelines and learnings.

🧹 Nitpick comments (1)
Tests/RadiantKitTests/AppStorageDateTests.swift (1)

62-62: Optional: Consider extracting helper methods to address SwiftLint length warnings.

SwiftLint reports warnings for file length (278>250), type body length (180>125), and function body length (26>25 in testOptionalDateSetToNil). These are due to necessary platform guards and error handling boilerplate across 8 tests.

The current structure is clear and maintainable. Extracting the platform guard pattern into a helper could reduce duplication, but may add complexity. Given that all tests pass and the code is readable, this refactor is low priority.

Also applies to: 214-214

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d5fd58b and 6c552d7.

📒 Files selected for processing (8)
  • CLAUDE.md
  • Package.swift
  • Scripts/lint.sh
  • Sources/RadiantKit/PropertyWrappers/AppStorage/AppStorage+AppStored.swift
  • Sources/RadiantKit/PropertyWrappers/AppStorage/AppStorage+ExpressibleByNilLiteral.swift
  • Sources/RadiantKit/Views/ValueTextBubble.swift
  • Tests/RadiantKitTests/AppStorageDateTests.swift
  • Tests/RadiantKitTests/RadiantKitTests.swift
💤 Files with no reviewable changes (2)
  • Tests/RadiantKitTests/RadiantKitTests.swift
  • Sources/RadiantKit/Views/ValueTextBubble.swift
🚧 Files skipped from review as they are similar to previous changes (3)
  • CLAUDE.md
  • Scripts/lint.sh
  • Sources/RadiantKit/PropertyWrappers/AppStorage/AppStorage+ExpressibleByNilLiteral.swift
🧰 Additional context used
📓 Path-based instructions (3)
**/*.swift

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.swift: All source files must include copyright headers (managed by Scripts/header.sh)
Use swift-format (v600.0.0) with configuration in .swift-format for code formatting
Use SwiftLint (v0.58.0) with rules in .swiftlint.yml for Swift style validation
All UI components must be @mainactor isolated for thread safety
Closures crossing actor boundaries must be @sendable
Use nonisolated sparingly and only when necessary in @MainActor-isolated types
All code must be compatible with Swift 6.0 strict concurrency checking and Sendable requirements
Protocols should use descriptive nouns (e.g., CodablePackage, ProgressOperation) for naming
Generic type parameters should use full words (e.g., FileType, ValueType) instead of single letters
Environment keys should follow the pattern Key (e.g., NextPageKey, PreviousPageKey)
Platform-specific code should use #if canImport() or #if os() directives
Use AppStored protocol for key-based storage contracts in property wrappers
Use DefaultWrapped for property wrappers that extend AppStored with default value support
Use UserDefaults extensions for type-safe retrieval in AppStored implementations
Use DocumentFile for type-safe file handling where FileType conforms to FileTypeSpecification
Use ProgressOperation protocol for progress-reporting operations where ValueType conforms to BinaryInteger & Sendable
Use @observable macro for reactive state (requires iOS 17+)
Inject page navigation actions (NextPageAction, PreviousPageAction, CancelPageAction) via SwiftUI environment
Use interface segregation with small, focused protocols rather than large monolithic protocols
Use protocol extensions to provide shared functionality across conforming types
Use generic constraints for type safety to prevent runtime errors

Files:

  • Sources/RadiantKit/PropertyWrappers/AppStorage/AppStorage+AppStored.swift
  • Package.swift
  • Tests/RadiantKitTests/AppStorageDateTests.swift
Package.swift

📄 CodeRabbit inference engine (CLAUDE.md)

Package.swift: Minimum deployment target for iOS is 18.0 / iPadOS 18.0
Minimum deployment target for macOS is 15.0
Minimum deployment target for tvOS is 18.0
Minimum deployment target for watchOS is 11.0
Minimum deployment target for visionOS is 2.0
Minimum deployment target for Mac Catalyst is 18.0
New modules must be added to Package.swift products and targets arrays
New modules must apply swiftSettings for experimental features in Package.swift
New modules must have corresponding test targets defined in Package.swift
Enable AccessLevelOnImport experimental feature in swiftSettings
Enable BitwiseCopyable experimental feature in swiftSettings
Enable IsolatedAny experimental feature in swiftSettings
Enable MoveOnlyPartialConsumption experimental feature in swiftSettings
Enable NestedProtocols experimental feature in swiftSettings
Enable NoncopyableGenerics experimental feature in swiftSettings
Enable TransferringArgsAndResults experimental feature in swiftSettings
Enable VariadicGenerics experimental feature in swiftSettings

Files:

  • Package.swift
Tests/**/*.swift

📄 CodeRabbit inference engine (CLAUDE.md)

Tests must pass on both macOS and Linux platforms (note: RadiantProgress uses swift-log on Linux)

Files:

  • Tests/RadiantKitTests/AppStorageDateTests.swift
🧠 Learnings (16)
📓 Common learnings
Learnt from: CR
Repo: brightdigit/RadiantKit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T23:25:31.843Z
Learning: Applies to **/*.swift : Use UserDefaults extensions for type-safe retrieval in AppStored implementations
📚 Learning: 2026-01-06T23:25:31.843Z
Learnt from: CR
Repo: brightdigit/RadiantKit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T23:25:31.843Z
Learning: Applies to **/*.swift : Use UserDefaults extensions for type-safe retrieval in AppStored implementations

Applied to files:

  • Sources/RadiantKit/PropertyWrappers/AppStorage/AppStorage+AppStored.swift
  • Package.swift
  • Tests/RadiantKitTests/AppStorageDateTests.swift
📚 Learning: 2026-01-06T23:25:31.843Z
Learnt from: CR
Repo: brightdigit/RadiantKit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T23:25:31.843Z
Learning: Applies to **/*.swift : Use DefaultWrapped for property wrappers that extend AppStored with default value support

Applied to files:

  • Sources/RadiantKit/PropertyWrappers/AppStorage/AppStorage+AppStored.swift
  • Package.swift
  • Tests/RadiantKitTests/AppStorageDateTests.swift
📚 Learning: 2026-01-06T23:25:31.843Z
Learnt from: CR
Repo: brightdigit/RadiantKit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T23:25:31.843Z
Learning: Applies to **/*.swift : Use AppStored protocol for key-based storage contracts in property wrappers

Applied to files:

  • Sources/RadiantKit/PropertyWrappers/AppStorage/AppStorage+AppStored.swift
  • Package.swift
  • Tests/RadiantKitTests/AppStorageDateTests.swift
📚 Learning: 2026-01-06T23:25:31.843Z
Learnt from: CR
Repo: brightdigit/RadiantKit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T23:25:31.843Z
Learning: Applies to Package.swift : Minimum deployment target for macOS is 15.0

Applied to files:

  • Package.swift
📚 Learning: 2026-01-06T23:25:31.843Z
Learnt from: CR
Repo: brightdigit/RadiantKit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T23:25:31.843Z
Learning: Applies to Package.swift : Minimum deployment target for visionOS is 2.0

Applied to files:

  • Package.swift
📚 Learning: 2026-01-06T23:25:31.843Z
Learnt from: CR
Repo: brightdigit/RadiantKit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T23:25:31.843Z
Learning: Applies to Package.swift : Minimum deployment target for Mac Catalyst is 18.0

Applied to files:

  • Package.swift
📚 Learning: 2026-01-06T23:25:31.843Z
Learnt from: CR
Repo: brightdigit/RadiantKit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T23:25:31.843Z
Learning: Applies to Package.swift : Minimum deployment target for iOS is 18.0 / iPadOS 18.0

Applied to files:

  • Package.swift
📚 Learning: 2026-01-06T23:25:31.843Z
Learnt from: CR
Repo: brightdigit/RadiantKit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T23:25:31.843Z
Learning: Applies to Package.swift : Minimum deployment target for watchOS is 11.0

Applied to files:

  • Package.swift
📚 Learning: 2026-01-06T23:25:31.843Z
Learnt from: CR
Repo: brightdigit/RadiantKit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T23:25:31.843Z
Learning: Applies to Package.swift : Minimum deployment target for tvOS is 18.0

Applied to files:

  • Package.swift
📚 Learning: 2026-01-06T23:25:31.843Z
Learnt from: CR
Repo: brightdigit/RadiantKit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T23:25:31.843Z
Learning: Applies to Package.swift : New modules must be added to Package.swift products and targets arrays

Applied to files:

  • Package.swift
📚 Learning: 2026-01-06T23:25:31.843Z
Learnt from: CR
Repo: brightdigit/RadiantKit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T23:25:31.843Z
Learning: Applies to Package.swift : New modules must have corresponding test targets defined in Package.swift

Applied to files:

  • Package.swift
  • Tests/RadiantKitTests/AppStorageDateTests.swift
📚 Learning: 2026-01-06T23:25:31.843Z
Learnt from: CR
Repo: brightdigit/RadiantKit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T23:25:31.843Z
Learning: Applies to Package.swift : Enable VariadicGenerics experimental feature in swiftSettings

Applied to files:

  • Package.swift
📚 Learning: 2026-01-06T23:25:31.843Z
Learnt from: CR
Repo: brightdigit/RadiantKit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T23:25:31.843Z
Learning: Applies to Package.swift : Enable AccessLevelOnImport experimental feature in swiftSettings

Applied to files:

  • Package.swift
📚 Learning: 2026-01-06T23:25:31.843Z
Learnt from: CR
Repo: brightdigit/RadiantKit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T23:25:31.843Z
Learning: Applies to **/*.swift : Use Observable macro for reactive state (requires iOS 17+)

Applied to files:

  • Package.swift
📚 Learning: 2026-01-06T23:25:31.843Z
Learnt from: CR
Repo: brightdigit/RadiantKit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T23:25:31.843Z
Learning: Applies to Tests/**/*.swift : Tests must pass on both macOS and Linux platforms (note: RadiantProgress uses swift-log on Linux)

Applied to files:

  • Tests/RadiantKitTests/AppStorageDateTests.swift
🪛 GitHub Check: CodeFactor
Tests/RadiantKitTests/AppStorageDateTests.swift

[notice] 214-214: Tests/RadiantKitTests/AppStorageDateTests.swift#L214
Function body should span 25 lines or less excluding comments and whitespace: currently spans 26 lines (function_body_length)


[notice] 62-62: Tests/RadiantKitTests/AppStorageDateTests.swift#L62
Struct body should span 125 lines or less excluding comments and whitespace: currently spans 180 lines (type_body_length)


[notice] 278-278: Tests/RadiantKitTests/AppStorageDateTests.swift#L278
File should contain 250 lines or less: currently contains 278 (file_length)

🪛 SwiftLint (0.57.0)
Tests/RadiantKitTests/AppStorageDateTests.swift

[Warning] 278-278: File should contain 250 lines or less: currently contains 278

(file_length)


[Warning] 214-214: Function body should span 25 lines or less excluding comments and whitespace: currently spans 26 lines

(function_body_length)


[Warning] 62-62: Type body should span 125 lines or less excluding comments and whitespace: currently spans 180 lines

(type_body_length)

🔇 Additional comments (3)
Tests/RadiantKitTests/AppStorageDateTests.swift (2)

39-57: LGTM! Test types provide comprehensive coverage.

The test storage types correctly conform to AppStored and cover all necessary combinations: non-optional/optional Date with describing/reflecting key types.


66-275: Comprehensive test coverage with proper isolation.

The test suite thoroughly validates Date and Date? AppStorage behavior with:

  • Proper test isolation via unique UserDefaults suites
  • Correct platform guards and version checks
  • Cleanup using defer to prevent test pollution
  • Both .describing and .reflecting key types
  • Nil handling for optional Date

The repetitive platform guard boilerplate is necessary for backward compatibility.

Sources/RadiantKit/PropertyWrappers/AppStorage/AppStorage+AppStored.swift (1)

145-162: Looks good! The @available annotation is correct.

The initializer properly follows the established pattern for type-specific AppStorage initializers and correctly delegates to the base initializer via type.key. The platform availability annotation (macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, visionOS 2.0) matches the actual minimum versions required for native Date storage in AppStorage.

@leogdion leogdion merged commit 4903492 into main Jan 7, 2026
2 of 3 checks passed
@leogdion leogdion deleted the 34-appstorage-optional-date branch January 7, 2026 01:07
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.

Add AppStorage initializer for Date? (optional Date)

1 participant