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
14 changes: 14 additions & 0 deletions .github/workflows/VerifyChanges.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,22 @@ on:
branches: [ $default-branch ]

jobs:
lint:
name: Lint
runs-on: macos-15
if: ${{ github.event_name == 'pull_request' }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Select Xcode 16.4
run: |
sudo xcode-select -s /Applications/Xcode_16.4.0.app
- name: Lint
run: |
Scripts/lint
build-and-test:
name: Build and Test (${{ matrix.platform }})
needs: lint
runs-on: macos-15
strategy:
fail-fast: false
Expand Down
75 changes: 75 additions & 0 deletions .swift-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{
"fileScopedDeclarationPrivacy": {
"accessLevel": "private"
},
"indentConditionalCompilationBlocks": false,
"indentSwitchCaseLabels": false,
"indentation": {
"spaces": 4
},
"lineBreakAroundMultilineExpressionChainComponents": false,
"lineBreakBeforeControlFlowKeywords": false,
"lineBreakBeforeEachArgument": false,
"lineBreakBeforeEachGenericRequirement": false,
"lineBreakBetweenDeclarationAttributes": false,
"lineLength": 120,
"maximumBlankLines": 2,
"multiElementCollectionTrailingCommas": true,
"noAssignmentInExpressions": {
"allowedFunctions": []
},
"prioritizeKeepingFunctionOutputTogether": true,
"reflowMultilineStringLiterals": {
"never": {}
},
"respectsExistingLineBreaks": true,
"rules": {
"AllPublicDeclarationsHaveDocumentation": false,
"AlwaysUseLiteralForEmptyCollectionInit": true,
"AlwaysUseLowerCamelCase": true,
"AmbiguousTrailingClosureOverload": true,
"AvoidRetroactiveConformances": false,
"BeginDocumentationCommentWithOneLineSummary": true,
"DoNotUseSemicolons": true,
"DontRepeatTypeInStaticProperties": true,
"FileScopedDeclarationPrivacy": true,
"FullyIndirectEnum": true,
"GroupNumericLiterals": true,
"IdentifiersMustBeASCII": true,
"NeverForceUnwrap": false,
"NeverUseForceTry": false,
"NeverUseImplicitlyUnwrappedOptionals": false,
"NoAccessLevelOnExtensionDeclaration": true,
"NoAssignmentInExpressions": true,
"NoBlockComments": true,
"NoCasesWithOnlyFallthrough": true,
"NoEmptyLinesOpeningClosingBraces": false,
"NoEmptyTrailingClosureParentheses": true,
"NoLabelsInCasePatterns": true,
"NoLeadingUnderscores": false,
"NoParensAroundConditions": true,
"NoPlaygroundLiterals": true,
"NoVoidReturnOnFunctionSignature": true,
"OmitExplicitReturns": false,
"OneCasePerLine": true,
"OneVariableDeclarationPerLine": true,
"OnlyOneTrailingClosureArgument": true,
"OrderedImports": true,
"ReplaceForEachWithForLoop": true,
"ReturnVoidInsteadOfEmptyTuple": true,
"TypeNamesShouldBeCapitalized": true,
"UseEarlyExits": true,
"UseExplicitNilCheckInConditions": true,
"UseLetInEveryBoundCaseVariable": true,
"UseShorthandTypeNames": true,
"UseSingleLinePropertyGetter": true,
"UseSynthesizedInitializer": true,
"UseTripleSlashForDocumentationComments": true,
"UseWhereClausesInForLoops": true,
"ValidateDocumentationComments": false
},
"spacesAroundRangeFormationOperators": true,
"spacesBeforeEndOfLineComments": 4,
"tabWidth": 4,
"version": 1
}
88 changes: 88 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this
repository.


## Development Commands

### Building and Testing

- **Build for testing**: `swift build --package-path . --disable-automatic-resolution`
- **Run tests**: `swift test --package-path . --disable-automatic-resolution`
- **Run tests with code coverage**: Tests are run through the GitHub Actions workflow using
`build_and_test.sh` from DevBuilds
- **Test specific platform**: Use `xcodebuild` with appropriate destination (see
`.github/workflows/VerifyChanges.yaml` for examples)

### Code Formatting and Linting

- **Lint check**: `Scripts/lint` (runs `swift format lint --recursive --strict` from anywhere)
- **Format code**: `swift format --in-place --recursive Sources/ Tests/`
- **Manual lint**: `swift format lint --recursive --strict Sources/ Tests/`
- **Swift format configuration**: Uses `.swift-format` file with 4-space indentation and 120
character line length

### Git Hooks

- **Install git hooks**: `Scripts/install-git-hooks` (installs pre-commit hook that runs lint
check)
- **Pre-commit hook**: Automatically runs `Scripts/lint` before each commit

### Documentation

- **Build documentation**: `swift package generate-documentation`
- **Documentation source**: `Sources/DevTesting/Documentation.docc/`


## Code Architecture

### Core Components

**RandomValueGenerating Protocol**: The central protocol that test suites conform to for generating
repeatable random test data. Uses `SeedableRandomNumberGenerator` and logs seeds for test
reproducibility.

**Three Main Feature Areas**:

1. **Random Value Generation** (`Sources/DevTesting/Random Value Generation/`): Extensions for
generating random values of various types (String, Data, URL, UUID, etc.) with seeded
randomness
2. **Collection Generation** (`Sources/DevTesting/Collection Generation/`): Array extensions for
generating collections of random data
3. **Stubbing** (`Sources/DevTesting/Stubbing/`): `Stub` and `ThrowingStub` classes for mocking
and spying in tests

### Key Design Patterns

- **Seeded Randomness**: All random generation uses `SeedableRandomNumberGenerator` to ensure
test reproducibility
- **Logging Integration**: Random seeds are logged using `os.Logger` (subsystem: "DevTesting",
category: "randomization")
- **Swift Testing Integration**: Designed specifically for Swift Testing framework, not XCTest
- **Type Safety**: Extensive use of Swift 6 features including typed throws and existential
types

### Dependencies

- **Swift Numerics**: Used for `RealModule` in tests
- **Platform Requirements**: iOS 18+, macOS 15+, tvOS 18+, visionOS 2+, watchOS 11+
- **Swift Version**: Requires Swift 6.1 toolchain

### Testing Strategy

- **Test Plans**: Uses `DevTesting.xctestplan` for organized test execution
- **Coverage Target**: Aims for 99%+ test coverage
- **Platform Testing**: Tests run on iOS, macOS, tvOS, and watchOS simulators
- **CI/CD**: GitHub Actions workflow with matrix strategy across platforms

### Documentation Standards

- **Full Documentation**: All public APIs are documented with Swift DocC
- **API Guidelines**: Follows Swift API Design Guidelines
- **Documentation Comments**: Uses triple-slash comments (`///`)
- **Markdown Style**: Follow `Documentation/MarkdownStyleGuide.md` for consistent formatting:
- 100 character line length
- 4-space indented code blocks (no fenced blocks)
- 2-space indented bullet lists with alignment
- Consistent terminology (function vs method, type vs class)
190 changes: 190 additions & 0 deletions Documentation/MarkdownStyleGuide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# Markdown Style Guide

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


## General Formatting

### Line Length

Keep all lines under **100 characters**. Break long sentences and paragraphs at natural points
to stay within this limit.

✅ Good:
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.


### Spacing

Use consistent spacing for visual hierarchy:

- **Between major sections**: 2 blank lines
- **After code blocks**: 1 blank line
- **Before subsections**: 1 blank line
- **After section headers**: 1 blank line

Example:

## Major Section

Content here.


## Another Major Section

### Subsection

Content after subsection header.

code block here

Content after code block.


## Headers

### Structure

Use consistent header hierarchy:

- `#` for document title
- `##` for major sections
- `###` for subsections
- `####` for sub-subsections (use sparingly)

### Numbering

Number subsections when they represent sequential steps or ordered items:

## Usage Patterns

### 1. Basic Setup
### 2. Advanced Configuration
### 3. Verification


## Code Blocks

### Indented Code Blocks

Use **4-space indentation** for all code blocks instead of fenced blocks:

✅ Good:
import DevTesting

final class MockService: ServiceProtocol {
nonisolated(unsafe) var performActionStub: Stub<String, Bool>!
}

❌ Bad:
```swift
import DevTesting

final class MockService: ServiceProtocol {
nonisolated(unsafe) var performActionStub: Stub<String, Bool>!
}
```


## Lists

### Unordered Lists

Use `-` as the bullet character for unordered lists. Place the hyphen 2 spaces from current
indentation level, followed by a space, then your text. When a bullet point spans multiple lines,
align continuation lines with the start of the text (not the hyphen). This ensures all text within a
bullet aligns vertically and makes proper indentation on continuation lines a matter of pressing tab
one or more times.

- Turpis cubilia sit urna dis ultricies consequat massa hendrerit enim natoque.
- Consectetur sapien posuere sit arcu finibus mattis dictumst dis, lectus ipsum in dictum
lobortis bibendum enim, suscipit aliquet nulla porta erat id class purus.
- Scelerisque massa rutrum dapibus placerat aenean tellus arcu cursus.
- Iaculis, cubilia tristique efficitur bibendum urna imperdiet facilisis turpis hac,
platea est habitant auctor quisque nec pulvinar fermentum sociosqu.
- Parturient justo, venenatis nunc lobortis senectus tortor orci elementum consequat.
- In nibh nisl venenatis bibendum neque mattis habitant tempor proin, tincidunt lobortis
vulputate commodo.

Blank lines can be placed between bullets if it aids in readability.

### Ordered Lists

Use standard numbered lists for sequential items. Follow similar indentation rules as for unordered
lists. Note that the `.` characters in the bullets leads to some strange indentation, but this is
unavoidable.

1. Turpis cubilia sit urna dis ultricies consequat massa hendrerit enim natoque.

2. Consectetur sapien posuere sit arcu finibus mattis dictumst dis, lectus ipsum in dictum
lobortis bibendum enim, suscipit aliquet nulla porta erat id class purus.

1. Scelerisque massa rutrum dapibus placerat aenean tellus arcu cursus.
2. Iaculis, cubilia tristique efficitur bibendum urna imperdiet facilisis turpis hac,
platea est habitant auctor quisque nec pulvinar fermentum sociosqu.
3. Parturient justo, venenatis nunc lobortis senectus tortor orci elementum consequat.

4. In nibh nisl venenatis bibendum neque mattis habitant tempor proin, tincidunt lobortis
vulputate commodo.


## Text Formatting

### Bold Text

Use bold for emphasis on key terms:

- **File names**: `Mock[ProtocolName].swift`
- **Type names**: `Mock[ProtocolName]`

### Code Spans

Use backticks for:

- Type names: `Stub<Input, Output>`
- Function names: `performAction(_:)`
- File names: `MockAppServices.swift`
- Keywords: `nonisolated(unsafe)`

### Terminology Consistency

Use consistent terminology throughout documents:

- Prefer "function" over "method" when referring to Swift functions
- Use "type" instead of "class" when referring generically to classes/structs/enums
- Use "property" for stored and computed properties


## File Structure Examples

Use indented text blocks for file structure diagrams:

Tests/
├── Folder 1/
│ └── Folder 2/
│ ├── File1.swift
│ └── File2.swift
└── Folder 3/
└── Folder 4/
├── File3.swift
└── File4.swift


## Links and References

### Internal References

Use relative paths for internal documentation:

See [Test Mock Documentation](TestMocks.md) for details.

### Code References

Reference specific locations using this pattern:

The main implementation is in `Stub.swift:42-68`.
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ let package = Package(
.library(
name: "DevTesting",
targets: ["DevTesting"]
),
)
],
dependencies: [
.package(url: "https://github.com/apple/swift-numerics", from: "1.0.0"),
.package(url: "https://github.com/apple/swift-numerics", from: "1.0.0")
],
targets: [
.target(
Expand Down
Loading
Loading