Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"permissions": {
"allow": [
"Bash(git:*)"
]
}
}
63 changes: 27 additions & 36 deletions .github/workflows/VerifyChanges.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ on:
branches: ["main"]

env:
XCODE_VERSION: 26.0.1
XCODE_VERSION: 26.3

jobs:
lint:
name: Lint
runs-on: macos-15
runs-on: macos-26
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Select Xcode ${{ env.XCODE_VERSION }}
run: sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app
run: sudo xcode-select -s /Applications/Xcode_"$XCODE_VERSION".app
- name: Lint
run: Scripts/lint

Expand All @@ -29,63 +29,54 @@ jobs:
fail-fast: false
matrix:
include:
# - platform: iOS
# xcode_destination: "platform=iOS Simulator,name=iPhone 16 Pro"
# xcode_project: "DevKeychain.xcodeproj"
# xcode_scheme: "DevKeychainApp"
- platform: iOS
xcode_destination: "platform=iOS Simulator,name=iPhone 17 Pro"
- platform: macOS
xcode_destination: "platform=macOS,arch=arm64"
xcode_project: ""
xcode_scheme: "DevKeychain"
# - platform: tvOS
# xcode_destination: "platform=tvOS Simulator,name=Apple TV 4K (3rd generation)"
# xcode_project: ""
# xcode_scheme: "DevKeychain"
# - platform: watchOS
# xcode_destination: "platform=watchOS Simulator,name=Apple Watch Series 10 (46mm)"
# xcode_project: ""
# xcode_scheme: "DevKeychain"
- platform: tvOS
xcode_destination: "platform=tvOS Simulator,name=Apple TV 4K (3rd generation)"
- platform: watchOS
xcode_destination: "platform=watchOS Simulator,name=Apple Watch Series 11 (46mm)"

env:
DEV_BUILDS: DevBuilds/Sources
OTHER_XCBEAUTIFY_FLAGS: --renderer github-actions
XCCOV_PRETTY_VERSION: 1.2.0
XCODE_PROJECT: ${{ matrix.xcode_project }}
XCODE_SCHEME: ${{ matrix.xcode_scheme }}
XCODE_SCHEME: DevKeychain
XCODE_DESTINATION: ${{ matrix.xcode_destination }}
XCODE_TEST_PRODUCTS_PATH: .build/DevFoundation.xctestproducts
XCODE_TEST_PRODUCTS_PATH: .build/DevKeychain.xctestproducts

steps:
- name: Select Xcode ${{ env.XCODE_VERSION }}
run: sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app
run: sudo xcode-select -s /Applications/Xcode_"$XCODE_VERSION".app

- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Checkout DevBuilds
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
repository: DevKitOrganization/DevBuilds
path: DevBuilds

- name: Restore XCTestProducts
if: github.event_name != 'push'
id: cache-xctestproducts-restore
uses: actions/cache/restore@v4
uses: actions/cache/restore@v5
with:
path: ${{ env.XCODE_TEST_PRODUCTS_PATH }}
key: cache-xctestproducts-${{ github.workflow }}-${{ matrix.platform }}-${{ github.sha }}

- uses: irgaly/xcode-cache@v1
if: steps.cache-xctestproducts-restore.outputs.cache-hit != 'true'
with:
key: xcode-cache-deriveddata-${{ github.workflow }}-${{ matrix.platform }}-${{ github.sha }}
key: xcode-cache-deriveddata-${{ env.XCODE_VERSION }}-${{ github.workflow }}-${{ matrix.platform }}-${{ github.sha }}
restore-keys: |
xcode-cache-deriveddata-${{ github.workflow }}-${{ matrix.platform }}-
xcode-cache-deriveddata-
xcode-cache-deriveddata-${{ env.XCODE_VERSION }}-${{ github.workflow }}-${{ matrix.platform }}-
xcode-cache-deriveddata-${{ env.XCODE_VERSION }}
deriveddata-directory: .build/DerivedData
sourcepackages-directory: .build/DerivedData/SourcePackages
swiftpm-package-resolved-file: |
**/*.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
Package.resolved
swiftpm-package-resolved-file: Package.resolved

- name: Build for Testing
id: build-for-testing
Expand All @@ -98,8 +89,8 @@ jobs:
run: ${{ env.DEV_BUILDS }}/build_and_test.sh --action test-without-building

- name: Save XCTestProducts
if: failure() && steps.cache-xctestproducts-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
if: (cancelled() || failure()) && steps.cache-xctestproducts-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v5
with:
path: ${{ env.XCODE_TEST_PRODUCTS_PATH }}
key: ${{ steps.cache-xctestproducts-restore.outputs.cache-primary-key }}
Expand All @@ -122,7 +113,7 @@ jobs:

- name: Upload Logs and XCResults
if: success() || failure()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: Logs_and_XCResults-${{ matrix.platform }}
path: |
Expand All @@ -132,7 +123,7 @@ jobs:

- name: Upload xccovPretty output
if: github.event_name != 'push'
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: xccovPrettyOutput-${{ matrix.platform }}
path: .build/xccovPretty-${{ matrix.platform }}.output
Expand All @@ -148,7 +139,7 @@ jobs:

steps:
- name: Download xccovPretty output
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: xccovPrettyOutput-macOS

Expand Down
4 changes: 2 additions & 2 deletions .swift-format
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,14 @@
"ReplaceForEachWithForLoop": true,
"ReturnVoidInsteadOfEmptyTuple": true,
"TypeNamesShouldBeCapitalized": true,
"UseEarlyExits": true,
"UseEarlyExits": false,
"UseExplicitNilCheckInConditions": true,
"UseLetInEveryBoundCaseVariable": true,
"UseShorthandTypeNames": true,
"UseSingleLinePropertyGetter": true,
"UseSynthesizedInitializer": true,
"UseTripleSlashForDocumentationComments": true,
"UseWhereClausesInForLoops": true,
"UseWhereClausesInForLoops": false,
"ValidateDocumentationComments": false
},
"spacesAroundRangeFormationOperators": true,
Expand Down
137 changes: 81 additions & 56 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,23 @@ DevKeychain is a Swift package providing a modern, type-safe interface to Apple'
services. It supports version 26 of Apple's OSes and requires a Swift 6.2+ toolchain.


## Common Development Commands
## Development Commands

### Building and Testing

# Build the package
swift build

# Run all tests
swift test

# Run specific test
swift test --filter <TestName>

# Build and test with code coverage
swift test --enable-code-coverage
- **Build**: `swift build`
- **Test all**: `swift test`
- **Test specific**: `swift test --filter <TestName>`
- **Test with coverage**: `swift test --enable-code-coverage`

### Code Quality

# Format code (requires swift-format)
swift-format --in-place --recursive Sources/ Tests/

# Check formatting
swift-format --recursive Sources/ Tests/
- **Lint**: `Scripts/lint` (uses `swift format lint --recursive --strict`)
- **Format**: `Scripts/format` (uses `swift format --in-place --recursive`)

### Git Hooks

# Install pre-commit hooks (if Scripts directory exists)
Scripts/install-git-hooks

# Run linting manually
Scripts/lint
- **Install**: `Scripts/install-git-hooks` (installs pre-push hook that runs lint)


## Architecture Overview
Expand Down Expand Up @@ -123,61 +109,100 @@ Uses **Swift Testing** framework with comprehensive mocking:
The project uses GitHub Actions for continuous integration:

- **Linting**: Automatically checks code formatting on all pull requests using `swift format`
- **Testing**: Runs tests on macOS (iOS, tvOS, and watchOS testing are disabled in CI due to
reliability issues)
- **Testing**: Builds and tests on iOS, macOS, tvOS, and watchOS
- **Coverage**: Generates code coverage reports using xccovPretty

For comprehensive cross-platform testing, developers should run `Scripts/test-all-platforms`
locally or rely on the pre-push git hook which automatically runs all platform tests before
pushing changes.
- Uses Xcode 26.3 and macOS 26 runners


## Platform Requirements

- Swift 6.2+ toolchain required
- Xcode 26.0 for CI/CD
- Xcode 26.3 for CI/CD
- Apple platforms only (iOS/macOS/tvOS/visionOS/watchOS version 26)
- Uses modern Swift concurrency features


## Testing and Mocking Standards

### Test Mock Architecture

The codebase uses a consistent stub-based mocking pattern built on the DevTesting framework:

#### Core Mock Patterns

- **Stub-based mocks**: All mocks use `Stub<Input, Output>` or
`ThrowingStub<Input, Output, Error>`
- **Force-unwrapped stubs**: Stub properties are declared with `!` — tests must configure them
- **Swift 6 concurrency**: All stub properties marked `nonisolated(unsafe)`
- **Argument structures**: Complex parameters use dedicated structures (e.g.,
`LogErrorArguments`)

#### Mock Organization

- **File naming**: `Mock[ProtocolName].swift`
- **Type naming**: `Mock[ProtocolName]`
- **Stub properties**: `[functionName]Stub`
- **Location**: `Tests/DevKeychainTests/Testing Helpers/`

#### Test Patterns

- Use `@Test` with Swift Testing framework
- Use `#expect()` and `#require()` for assertions
- Always configure stubs before use to avoid crashes
- Leverage DevTesting's call tracking for verification


## Documentation Standards

Follow the project's Markdown Style Guide:

- **Line length**: 100 characters max
- **Code blocks**: Use 4-space indentation, not fenced blocks
- **Lists**: Use `-` for bullets, align continuation lines with text
- **Spacing**: 2 blank lines between major sections, 1 after headers
- **Terminology**: Use "function" over "method", "type" over "class"

When writing Markdown documentation, reference `@Documentation/MarkdownStyleGuide.md` to
ensure consistent formatting, structure, and style across all project documentation.


## Code Formatting and Spacing

The project follows strict spacing conventions for readability and consistency:

- **2 blank lines between major sections** including:
- Between the last property declaration and first function declaration
- Between all function/computed property implementations at the same scope level
- Between top-level type declarations (class, struct, enum, protocol, extension)
- Before MARK comments that separate major sections
- **1 blank line** for minor separations:
- Between property declarations and nested type definitions
- Between all function definitions in protocols
- After headers in documentation
- After MARK comments that separate major sections
- **File endings**: All Swift files must end with exactly one blank line


## Scripts Directory

The `Scripts/` directory contains utility scripts for development workflow automation:

### Available Scripts

**`Scripts/install-git-hooks`**:
- Installs pre-commit and pre-push git hooks for automated quality checks
- Creates `.git/hooks/pre-commit` that calls `Scripts/lint` for formatting validation
- Creates `.git/hooks/pre-push` that calls `Scripts/test-all-platforms` for comprehensive testing
- Prevents commits with formatting issues and pushes with test failures
- Run once per repository to set up automated code quality and testing workflow
- Installs a pre-push git hook that runs `Scripts/lint` for formatting validation
- Works correctly with git worktrees
- Run once per repository to set up automated code quality workflow

**`Scripts/lint`**:
- Runs swift-format lint validation with strict mode enabled
- Runs `swift format lint` validation with strict mode enabled
- Checks `App/`, `Sources/`, and `Tests/` directories recursively
- Returns non-zero exit code if formatting issues are found
- Used by pre-commit hooks and can be run manually for code quality verification

**`Scripts/test-all-platforms`**:
- Runs comprehensive tests across all supported Apple platforms
- Tests on iOS Simulator (iPhone 16 Pro), macOS, tvOS Simulator (Apple TV 4K), and watchOS
Simulator (Apple Watch Series 10)
- Uses different project configurations: DevKeychainApp scheme for iOS, DevKeychain scheme for
other platforms
- Provides colored output with timestamps and clear success/failure indicators
- Returns non-zero exit code if any platform tests fail
- Essential for local cross-platform validation since CI only tests macOS

### Usage Patterns

- Run `Scripts/install-git-hooks` after cloning the repository for complete automation
- Pre-commit hooks automatically run `Scripts/lint` before each commit
- Pre-push hooks automatically run `Scripts/test-all-platforms` before each push
- Manual operations:
- Code formatting check: `Scripts/lint`
- Cross-platform testing: `Scripts/test-all-platforms`
- All scripts work from any directory by calculating repository root path
- Used by pre-push hook and can be run manually for code quality verification

**`Scripts/format`**:
- Runs `swift format --in-place` to automatically fix formatting issues
- Formats `App/`, `Sources/`, and `Tests/` directories recursively


## Key Files for Understanding
Expand Down
7 changes: 3 additions & 4 deletions Documentation/MarkdownStyleGuide.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Markdown Style Guide

This document defines the Markdown formatting standards for documentation in the Shopper iOS
codebase.
This document defines the Markdown formatting standards for documentation in this project.


## General Formatting
Expand All @@ -12,9 +11,9 @@ Keep all lines under **100 characters**. Break long sentences and paragraphs at
to stay within this limit.

✅ Good:
Faucibus consectetur lacinia nostra eros conubia nibh inceptos hendrerit, ante blandit
Faucibus consectetur lacinia nostra eros conubia nibh inceptos hendrerit, ante blandit
vulputate imperdiet amet porttitor torquent mattis.

❌ Bad:
Faucibus consectetur lacinia nostra eros conubia nibh inceptos hendrerit, ante blandit vulputate imperdiet amet porttitor torquent mattis.

Expand Down
Loading
Loading