Skip to content
WolfWave
Developers

Development

Build WolfWave from source and contribute. Xcode 16, macOS 26, Config.xcconfig setup, make targets, the test suite, notarization, and the release pipeline.

Everything you need to build WolfWave from source and contribute. Just want to use the app? See Installation.

Prerequisites

  • macOS 26.0+ (Tahoe)
  • Apple Silicon (M1 or later)
  • Xcode 16.0+
  • Command Line Tools. Run xcode-select --install if you haven't.

Clone the repo

git clone https://github.com/MrDemonWolf/wolfwave.git
cd wolfwave

Configure API keys

Copy the example config and set your keys:

cp apps/native/WolfWave/Config.xcconfig.example apps/native/WolfWave/Config.xcconfig
TWITCH_CLIENT_ID = your_twitch_client_id_here
DISCORD_CLIENT_ID = your_discord_client_id_here

# Optional. Fork/whitelabel branding overrides. Leave as-is for the upstream defaults.
DOCS_URL = https://mrdemonwolf.github.io/wolfwave
COMMUNITY_DISCORD_URL = https://mrdwolf.net/discord
COPYRIGHT_HOLDER = MrDemonWolf, Inc.

Get a Twitch Client ID at dev.twitch.tv/console/apps. Get a Discord Application ID at discord.com/developers/applications.

Config.xcconfig is gitignored. Each contributor uses their own.

Branding overrides

DOCS_URL, COMMUNITY_DISCORD_URL, and COPYRIGHT_HOLDER control the docs link, community Discord invite, and legal entity shown in About + Monthly Wrap. Three levels, in priority order:

  1. Per-machine / fork: set them in Config.xcconfig. Gitignored, affects only your local builds.
  2. Project-wide default: the fallback strings in Core/AppConstants.swift. This is the source of truth shipped to every user.
  3. CI: the release and test workflows write these keys into the generated Config.xcconfig so built apps carry literal values.

Each key falls back to the AppConstants default when blank, so leaving them out never breaks a build. To rename the app itself, edit CFBundleDisplayName in Info.plist (or set PRODUCT_NAME in an xcconfig overlay).

Build and run

make open-xcode

Hit ⌘R in Xcode. That's it.

CommandDescription
make buildDebug build
make cleanClean build artifacts
make testRun unit tests (XCTest + Swift Testing)
make update-depsResolve SwiftPM dependencies
make open-xcodeOpen the Xcode project
make ciCI-friendly build
make prod-buildRelease build + DMG in builds/
make prod-installRelease build + install to /Applications
make notarizeNotarize the DMG (requires Developer ID)
make verify-notarizeVerify notarization of DMG

Testing

Run the suite with make test or Cmd+U in Xcode. The test target (WolfWaveTests) is a hosted unit test bundle that runs inside the app process.

Tests live in apps/native/WolfWaveTests/ and cover the playback parser, command dispatcher, Twitch + Discord services, WebSocket auth handshake, Keychain wrapper, onboarding state machine, listening history pipeline, and the Sparkle updater. For the live pass count, run make test locally — enumerating numbers here just rots on every PR.

Test files are auto-discovered: drop a .swift file into apps/native/WolfWaveTests/ and use @testable import WolfWave. No Xcode project edit needed.

Building the OBS widget

The bundled widget.html is a generated artifact from the apps/widget/ workspace, committed so Xcode-only builds keep working. If you touch widget source or design tokens, rebuild with make widget and commit the output. The full pipeline, rebuild triggers, and dev loop live in the widget guide.

Rich Presence art assets (fork maintainers)

Discord Rich Presence references two asset names registered against the Discord application: apple_music (large image fallback + playing badge) and pause (the paused badge). The PNGs live in discord-assets/ and are not bundled with the app. If you ship a fork with your own DISCORD_CLIENT_ID, upload both to your Discord application's Rich Presence → Art Assets page or presence won't render. See discord-assets/README.md.

Production build

make prod-build

The DMG lands at builds/WolfWave-{version}.dmg. With a Developer ID Application certificate in your Keychain, the app is re-signed for distribution as part of the build. make prod-install builds and drops straight into /Applications.

Notarization

APPLE_ID=you@example.com \
APPLE_TEAM_ID=YOUR_TEAM_ID \
APPLE_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx \
make notarize

Generate an app-specific password at appleid.apple.com under Sign-In and Security → App-Specific Passwords.

CI/CD

  • Test (test.yml): xcodebuild test on every push and PR to main.
  • Build & Release (build_release.yml): on a v* tag push, builds, signs, notarizes, embeds release notes in the Sparkle appcast, and uploads the DMG to a draft GitHub Release for review.
  • Update Homebrew (update_homebrew.yml): bumps the Homebrew cask after a Release is published.
  • Docs (docs.yml): deploys this site to GitHub Pages.
  • Nightly (nightly.yml): daily signed build off main feeding the opt-in Nightly channel.

Required GitHub secrets

SecretDescription
TWITCH_CLIENT_IDTwitch application Client ID
DISCORD_CLIENT_IDDiscord application ID
DEVELOPER_ID_CERT_P12Base64-encoded Developer ID certificate
DEVELOPER_ID_CERT_PASSWORDPassword for the P12 certificate
APPLE_IDApple ID for notarization
APPLE_TEAM_IDApple Developer Team ID
APPLE_APP_PASSWORDApp-specific password for notarization
SPARKLE_PRIVATE_KEYEdDSA private key for appcast signing

Triggering a release

Push a version tag matching the app's MARKETING_VERSION:

git tag vX.Y.Z
git push origin vX.Y.Z

The workflow builds, signs, notarizes, and uploads to a draft Release. Rerunning it updates the draft's assets; it refuses to replace an already-published release. Review the draft, publish, and the Homebrew cask updates on its own. The full checklist lives in the repo's Release Checklist.

Next steps

On this page