This guide covers local setup, build/test commands, coding standards, CI expectations, and common troubleshooting.
- macOS with current Xcode support for the repo's configured Xcode version.
- Xcode version from
.github/xcode-version(26.4at the time of writing). - iOS Simulator runtime for iOS 26.
- Swift 6.2 toolchain.
- Homebrew for optional tools such as SwiftLint and actionlint.
The CI simulator device is iPhone 17 Pro.
From the repository root:
xcodebuild -version
xcodebuild -project Hackers.xcodeproj -scheme Hackers -destination 'platform=iOS Simulator,name=iPhone 17 Pro' buildIf the simulator is missing or stale, compare your environment with the CI setup script:
scripts/ci/setup-xcode-simulator.shThat script selects/prints Xcode, runs first launch setup, verifies the iOS simulator platform, and creates/boots the expected simulator when needed.
Run all xcodebuild commands from the repository root.
xcodebuild -project Hackers.xcodeproj -scheme Hackers -destination 'platform=iOS Simulator,name=iPhone 17 Pro' buildClean build:
xcodebuild clean build -project Hackers.xcodeproj -scheme Hackers -destination 'platform=iOS Simulator,name=iPhone 17 Pro'Quick build status check:
xcodebuild build -project Hackers.xcodeproj -scheme Hackers -destination 'platform=iOS Simulator,name=iPhone 17 Pro' 2>&1 | grep "BUILD"Use the project test runner:
./run_tests.shVerbose:
./run_tests.sh -vSpecific modules:
./run_tests.sh Domain
./run_tests.sh Feed Comments SettingsDo not use swift test as the project validation command. Packages target iOS and use iOS-only APIs, so tests must run through Xcode with an iOS Simulator destination.
Tests live beside their package:
Domain/Tests/Data/Tests/Networking/Tests/Shared/Tests/DesignSystem/Tests/Features/*/Tests/
Use Swift Testing (import Testing, @Suite, @Test) for new tests unless an existing target clearly uses a different local pattern.
Focus tests on:
- Domain models and parser behavior
- Repository parsing and persistence behavior
- ViewModel state transitions
- Shared services
- Regression cases for Hacker News markup changes
Avoid view snapshot or full UI tests unless the behavior cannot be covered at a lower level.
Keep changes consistent with the existing Swift 6.2 codebase:
- Prefer small, focused types over broad utility objects.
- Use
async/awaitfor new asynchronous code. - Keep UI-facing types and mutations on the main actor.
- Prefer initializer injection for ViewModels and services.
- Keep
DependencyContainer.shareduse near app/feature composition. - Keep Domain models and protocols free of unnecessary UI dependencies.
- Use existing DesignSystem components before adding new visual primitives.
- Do not introduce new package dependencies unless the benefit is clear and the scope is narrow.
- Keep comments sparse and useful; explain non-obvious decisions, not mechanics.
CI runs SwiftLint. Locally:
swiftlint lint --config .swiftlint.ymlInstall when needed:
brew install swiftlintSome existing warnings are non-blocking; do not expand unrelated lint cleanup into feature work unless asked.
Primary workflows:
.github/workflows/pr.yml- Runs on PRs and pushes to
master. - Required jobs:
lint,build,test.
- Runs on PRs and pushes to
.github/workflows/nightly.yml- Scheduled clean validation to catch Xcode/simulator/runtime drift.
.github/workflows/release-testflight.yml- Protected TestFlight release workflow.
.github/workflows/release-appstore.yml- Protected placeholder only; App Store submission remains manual.
Actions are pinned by commit SHA and the repository requires SHA pinning for Actions.
Dependabot is configured for:
- GitHub Actions
- Bundler/fastlane
- Swift packages
Review dependency updates normally through PR checks. Be careful with release tooling updates because fastlane, Bundler, Xcode, and App Store Connect behavior are tightly coupled.
If builds fail due to missing simulators:
scripts/ci/setup-xcode-simulator.sh
xcrun simctl list devices available
xcrun simctl list runtimesIf package tests fail only through swift test, rerun with ./run_tests.sh; swift test is not the supported validation path.
If CI fails but local builds pass, inspect uploaded diagnostics:
- xcodebuild logs
.xcresultbundles- simulator diagnostics
If release signing or upload fails, use Release Process and inspect testflight-artifacts before changing secrets or signing assets.