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
124 changes: 74 additions & 50 deletions .github/workflows/VerifyChanges.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
name: Verify Changes

on:
merge_group:
pull_request:
Expand All @@ -8,115 +7,140 @@ 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

build-and-test:
name: Build and Test (${{ matrix.platform }})
needs: lint
runs-on: macos-15
runs-on: macos-26
strategy:
fail-fast: false
matrix:
include:
# - platform: iOS
# xcode_destination: "platform=iOS Simulator,name=iPhone SE (3rd generation)"
- platform: iOS
xcode_destination: "platform=iOS Simulator,name=iPhone 17 Pro"
- platform: macOS
xcode_destination: "platform=macOS,arch=arm64"
# - platform: tvOS
# xcode_destination: "platform=tvOS Simulator,name=Apple TV 4K (3rd generation) (at 1080p)"
- platform: tvOS
xcode_destination: "platform=tvOS Simulator,name=Apple TV 4K (3rd generation)"

env:
DEV_BUILDS: DevBuilds/Sources
OTHER_XCODE_FLAGS: ${{ matrix.other_xcode_flags }}
OTHER_XCBEAUTIFY_FLAGS: --renderer github-actions
XCCOV_PRETTY_VERSION: 1.2.0
XCODE_SCHEME: DevColorExtraction
XCODE_DESTINATION: ${{ matrix.xcode_destination }}
XCODE_TEST_PRODUCTS_PATH: .build/DevColorExtraction.xctestproducts

steps:
- name: Select Xcode ${{ env.XCODE_VERSION }}
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: Download xccovPretty

- name: Restore XCTestProducts
if: github.event_name != 'push'
run: |
gh release download ${{ env.XCCOV_PRETTY_VERSION }} \
--repo DevKitOrganization/xccovPretty \
--pattern "xccovPretty-macos.tar.gz" \
-O - | tar -xz
chmod +x xccovPretty
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
id: cache-xctestproducts-restore
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: Package.resolved
verbose: true
- name: Select Xcode ${{ env.XCODE_VERSION }}
run: sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app

- name: Build for Testing
run: |
"$DEV_BUILDS"/build_and_test.sh --action build-for-testing
- name: Test
id: build-for-testing
if: steps.cache-xctestproducts-restore.outputs.cache-hit != 'true'
run: ${{ env.DEV_BUILDS }}/build_and_test.sh --action build-for-testing

- name: Test Without Building
id: test-without-building
if: github.event_name != 'push'
run: |
"$DEV_BUILDS"/build_and_test.sh --action test
run: ${{ env.DEV_BUILDS }}/build_and_test.sh --action test-without-building

- name: Save XCTestProducts
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 }}

- name: Log Code Coverage
if: github.event_name != 'push'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
xcrun xccov view --report .build/DevColorExtraction_test.xcresult --json \
| ./xccovPretty --github-comment \
> .build/xccovPretty-${{ matrix.platform }}.output
- name: Upload Logs
uses: actions/upload-artifact@v4
if: success() || failure()
with:
name: Logs-${{ matrix.platform }}
path: .build/*.log
include-hidden-files: true
- name: Upload XCResults
uses: actions/upload-artifact@v4
gh release download ${{ env.XCCOV_PRETTY_VERSION }} \
--repo DevKitOrganization/xccovPretty \
--pattern "xccovPretty-macos.tar.gz" \
-O - \
| tar -xz
chmod +x xccovPretty

xcrun xccov view --report .build/${XCODE_SCHEME}_test-without-building.xcresult --json \
| ./xccovPretty --github-comment \
> .build/xccovPretty-${{ matrix.platform }}.output

- name: Upload Logs and XCResults
if: success() || failure()
uses: actions/upload-artifact@v7
with:
name: XCResults-${{ matrix.platform }}
path: .build/*.xcresult
name: Logs_and_XCResults-${{ matrix.platform }}
path: |
.build/*.log
.build/*.xcresult
include-hidden-files: true

- 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
include-hidden-files: true

post-pr-comments:
name: Post PR Comments
needs: build-and-test
if: ${{ github.event_name == 'pull_request' }}
permissions:
pull-requests: write
runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }}

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

- name: Post Code Coverage Comment
uses: thollander/actions-comment-pull-request@v3
with:
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
105 changes: 105 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# 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**: `swift build`
- **Test all**: `swift test`
- **Test specific target**: `swift test --filter DevColorExtractionTests`

### Code Quality

- **Lint**: `Scripts/lint` (uses `swift format lint --recursive --strict`)
- **Format**: `Scripts/format` (uses `swift format --in-place`)

### GitHub Actions

The repository uses GitHub Actions for CI/CD with the workflow in
`.github/workflows/VerifyChanges.yaml`. The workflow:

- Lints code on PRs using `swift format`
- Builds and tests on iOS, macOS, and tvOS
- Generates code coverage reports using xccovPretty
- Uses Xcode 26.3 and macOS 26 runners


## Architecture Overview

DevColorExtraction is a Swift package that extracts the most common colors from images using
k-means clustering. It provides efficient color analysis with support for edge-based extraction.

### Source Structure

Sources/DevColorExtraction/
├── MostCommonColors.swift # Core color extraction via CGImage extensions
├── EdgeSet.swift # Edge set type for edge-based extraction
└── Documentation.docc/ # DocC documentation

### Key APIs

- `CGImage.mostCommonColors(count:passes:)` — extract most common colors from an image
- `CGImage.mostCommonColors(count:passes:edges:)` — extract colors from specific edges
- `EdgeSet` — option set for specifying image edges (top, left, bottom, right)


## Dependencies

External dependencies managed via Swift Package Manager:

- **swift-numerics**: Numeric utilities (RealModule, used in tests)


## Testing

The codebase maintains >97% test coverage. Tests are in `Tests/DevColorExtractionTests/` with
test image resources in `Tests/DevColorExtractionTests/Resources/`.


## Platform Support

- iOS 18+
- macOS 15+
- tvOS 18+


## 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


## Documentation Style

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

- **Line Length**: 100 characters maximum
- **Code Blocks**: Use 4-space indentation instead of fenced blocks
- **Lists**: Use `-` for bullets with proper indentation alignment
- **Spacing**: 2 blank lines between major sections, 1 blank line after headers
- **Terminology**: Use "function" over "method", "type" over "class"


## Development Notes

- Follows Swift API Design Guidelines
- Uses Swift 6.2 with `ExistentialAny` and `MemberImportVisibility` features enabled
- All public APIs are documented and tested
- Test coverage target: >97%
4 changes: 2 additions & 2 deletions Documentation/DependencyInjection.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Dependency Injection

This document outlines the dependency injection patterns and conventions that I like to use in my
Swift code.
This document outlines the dependency injection patterns and conventions used throughout this
project.

After reading this doc, take a look at the [Test Mocks guide](TestMocks.md) for specific guidance
about how to write mocks.
Expand Down
3 changes: 1 addition & 2 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 Down
Loading
Loading