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 --installif you haven't.
Clone the repo
git clone https://github.com/MrDemonWolf/wolfwave.git
cd wolfwaveConfigure API keys
Copy the example config and set your keys:
cp apps/native/WolfWave/Config.xcconfig.example apps/native/WolfWave/Config.xcconfigTWITCH_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:
- Per-machine / fork: set them in
Config.xcconfig. Gitignored, affects only your local builds. - Project-wide default: the fallback strings in
Core/AppConstants.swift. This is the source of truth shipped to every user. - CI: the release and test workflows write these keys into the generated
Config.xcconfigso 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-xcodeHit ⌘R in Xcode. That's it.
| Command | Description |
|---|---|
make build | Debug build |
make clean | Clean build artifacts |
make test | Run unit tests (XCTest + Swift Testing) |
make update-deps | Resolve SwiftPM dependencies |
make open-xcode | Open the Xcode project |
make ci | CI-friendly build |
make prod-build | Release build + DMG in builds/ |
make prod-install | Release build + install to /Applications |
make notarize | Notarize the DMG (requires Developer ID) |
make verify-notarize | Verify 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-buildThe 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 notarizeGenerate an app-specific password at appleid.apple.com under Sign-In and Security → App-Specific Passwords.
CI/CD
- Test (
test.yml):xcodebuild teston every push and PR tomain. - Build & Release (
build_release.yml): on av*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 offmainfeeding the opt-in Nightly channel.
Required GitHub secrets
| Secret | Description |
|---|---|
TWITCH_CLIENT_ID | Twitch application Client ID |
DISCORD_CLIENT_ID | Discord application ID |
DEVELOPER_ID_CERT_P12 | Base64-encoded Developer ID certificate |
DEVELOPER_ID_CERT_PASSWORD | Password for the P12 certificate |
APPLE_ID | Apple ID for notarization |
APPLE_TEAM_ID | Apple Developer Team ID |
APPLE_APP_PASSWORD | App-specific password for notarization |
SPARKLE_PRIVATE_KEY | EdDSA 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.ZThe 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
Changelog
Every WolfWave release. New features, fixes, and breaking changes. Auto-updating via Sparkle. macOS menu bar app for Apple Music streamers.
Architecture
How WolfWave is built. MVVM + service-oriented Swift. ScriptingBridge → Apple Music, EventSub WebSocket → Twitch, IPC → Discord, WebSocket → overlay.