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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
31 changes: 31 additions & 0 deletions .github/workflows/beta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Beta

on:
workflow_dispatch:

jobs:
beta:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4

- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
bundler-cache: true

- name: Write App Store Connect API key
env:
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
APP_STORE_CONNECT_API_KEY_CONTENT: ${{ secrets.APP_STORE_CONNECT_API_KEY_CONTENT }}
run: |
mkdir -p ~/.appstoreconnect/private_keys
echo "$APP_STORE_CONNECT_API_KEY_CONTENT" | base64 -d > ~/.appstoreconnect/private_keys/AuthKey_${APP_STORE_CONNECT_API_KEY_ID}.p8

- name: Upload to TestFlight
env:
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }}
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
run: bundle exec fastlane beta
37 changes: 37 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
test:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4

- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
bundler-cache: true

- name: Run tests
run: bundle exec fastlane test

periphery:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4

- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
bundler-cache: true

- name: Install periphery
run: brew install peripheryapp/periphery/periphery

- name: Scan for dead code
run: bundle exec fastlane periphery
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,11 @@ Carthage/Build
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/test_output

.DS_Store
*.xcuserstate

# Bundler
.bundle/
vendor/
16 changes: 16 additions & 0 deletions .periphery.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
project: PcVolumeControl.xcodeproj
schemes:
- PcVolumeControl
# Wire-protocol DTOs are only encoded to JSON for the server; their properties
# are written but never read back, which is not dead code.
retain_codable_properties: true
# Widget Live Activity / Control / AppIntent are intentional scaffolding kept for
# planned widget work (see WIDGET_IMPLEMENTATION_GUIDE.md). Exclude from reports.
# SnapshotHelper.swift is vendored verbatim from fastlane (regenerated by
# `fastlane snapshot`); its unused legacy overloads are part of fastlane's public
# API and must not be hand-edited.
report_exclude:
- "**/PcVolumeControlWidget/AppIntent.swift"
- "**/PcVolumeControlWidget/PcVolumeControlWidgetControl.swift"
- "**/PcVolumeControlWidget/PcVolumeControlWidgetLiveActivity.swift"
- "**/PcVolumeControlUITests/SnapshotHelper.swift"
98 changes: 98 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# CLAUDE.md

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

## Project Overview

PCVolumeControl is an iOS application that allows users to remotely control application audio volumes on a Windows PC. The app connects to a server running on the PC through a TCP connection and provides a UI to adjust volume levels for individual applications.

## Architecture

- **Platform**: iOS 17+ Swift app using SwiftUI
- **Architecture Pattern**: MVVM (Model-View-ViewModel)
- **Networking**: TCP socket communication using Apple's Network framework
- **Protocol**: JSON-based protocol (version 7) for client-server communication

## Key Components

1. **Connection Management**:
- `MainViewModel.swift`: Handles TCP socket connection, error handling, and data processing
- Connection states: ready, preparing, failed, waiting, cancelled

2. **Data Models**:
- `FullState`: Server response with complete state information
- `Session`: Represents an application with volume and mute state
- Various update models for sending changes to the server

3. **UI Components**:
- `MainView`: Initial connection screen
- `SliderView`: Volume controls for applications
- `ConnectingView`: Connection status display

## Development Workflow

### Building and Running the App

1. Open the project in Xcode:
```
open PcVolumeControl.xcodeproj
```

2. Select a target device or simulator

3. Build and run the app using Xcode's run button or:
```
⌘+R
```
## Running Tests
**unit tests**

Run this to execute all unit tests: `xcodebuild test -scheme PcVolumeControl -destination 'platform=iOS Simulator,name=iPhone 17 Pro Max,OS=26.0'`

## Dead Code Analysis

The project uses [periphery](https://github.com/peripheryapp/periphery) to detect unused code.

1. Install (one-time): `brew install peripheryapp/periphery/periphery`
2. Run the scan from the repo root: `periphery scan`

In CI this runs as the `periphery` fastlane lane (`bundle exec fastlane periphery`),
which adds `--strict` so the scan exits non-zero on findings. The
`.github/workflows/ci.yml` workflow runs it on every push and pull request.

Configuration lives in the committed `.periphery.yml`; `periphery scan` reads it
automatically. A clean run reports `No unused code detected.`. Notes:

- `retain_codable_properties: true` keeps the encode-only wire DTOs in
`models/VolumeUpdateDTOs.swift` from being flagged (their properties are written
to JSON but never read back, which is not dead code).
- `report_exclude` covers the widget scaffolding (`AppIntent.swift`,
`PcVolumeControlWidgetControl.swift`, `PcVolumeControlWidgetLiveActivity.swift`)
kept for planned widget work, and `SnapshotHelper.swift`, which is vendored
verbatim from fastlane and must not be hand-edited.
- The scan requires a single shared `PcVolumeControl` scheme. A stale per-user
scheme under `xcodeproj/xcuserdata/.../xcschemes/` creates a duplicate that
makes periphery fail with "Scheme ... does not exist"; delete it (it is
git-ignored and regenerated by Xcode).

### Development Notes

- The project is undergoing refactoring (branch: bill/refactor)
- Recently migrated to SwiftUI from an older architecture
- Pods dependencies have been removed
- NEVER use emojis in the code, including in comments.

## Protocol

The app communicates with the PC server using a JSON protocol:

- **Server → Client**: Sends `FullState` with all devices and sessions information
- **Client → Server**: Sends updates for:
- Default device changes
- Master volume/mute changes
- Individual application volume/mute changes

## Related Resources

- Demo Video: https://youtu.be/OgueVzguP3w
- Wiki: https://github.com/PcVolumeControl/PcVolumeControliOS/wiki
83 changes: 83 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Contributing to PCVolumeControl

Thanks for contributing! This guide covers the local development workflow.

## Prerequisites

- Xcode 16+ with the iOS 17+ SDK
- [Homebrew](https://brew.sh)

## Building and Running

```sh
open PcVolumeControl.xcodeproj
```

Select a simulator or device and build/run with the Xcode run button (`Cmd+R`).

## Running Tests

```sh
xcodebuild test -scheme PcVolumeControl \
-destination 'platform=iOS Simulator,name=iPhone 17 Pro Max,OS=26.0'
```

## Dead Code Analysis

We use [periphery](https://github.com/peripheryapp/periphery) to find unused code.
Run a scan before opening a pull request and resolve anything it reports.

1. Install (one-time):

```sh
brew install peripheryapp/periphery/periphery
```

2. From the repository root, run:

```sh
periphery scan
```

`periphery scan` reads the committed `.periphery.yml`; a clean run prints
`No unused code detected.`.

Alternatively, run the same check CI runs via fastlane:

```sh
bundle exec fastlane periphery
```

The `periphery` lane runs `periphery scan --strict`, so it exits non-zero when
unused code is found. CI (`.github/workflows/ci.yml`) runs this lane on every push
and pull request, so a scan failure will fail the build.

### Handling findings

Prefer deleting genuinely unused code. When periphery flags code that is *not*
actually dead, do not silence it blindly:

- Code reachable only through reflection, SwiftUI SPI, or the wire protocol can be
retained via `.periphery.yml` settings (e.g. `retain_codable_properties`) or a
targeted `// periphery:ignore` comment with an explanatory note.
- Vendored or generated files (e.g. fastlane's `SnapshotHelper.swift`) and
intentional scaffolding are listed under `report_exclude` in `.periphery.yml`.

Always add a comment explaining why an exclusion or ignore is justified.

### Troubleshooting

If periphery fails with `Scheme 'PcVolumeControl' does not exist`, you likely have
a stale per-user scheme shadowing the shared one. Remove it (it is git-ignored and
Xcode regenerates it):

```sh
rm PcVolumeControl.xcodeproj/xcuserdata/*.xcuserdatad/xcschemes/PcVolumeControl.xcscheme
```

## Code Style

- Target iOS 17+ with SwiftUI and the MVVM pattern.
- NEVER use emojis in code, including comments and log output.
</content>
</invoke>
5 changes: 5 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
source "https://rubygems.org"

gem "fastlane"
gem "abbrev"
gem "ostruct"
Loading