Contributing
March 2, 2026 · View on GitHub
Thank you for helping improve ayna! This document captures the practical steps for local development, testing, and submitting changes.
Prerequisites
- macOS 26.0 (Tahoe) or newer
- Xcode 16.0 or newer (Swift 6.0+ toolchain)
- iOS Simulator or device for iOS development
- watchOS Simulator for watchOS development
- Clone the repo. Open
Package.swiftfor macOS, orAynaMobile.xcodeprojfor iOS/watchOS.
git clone https://github.com/yourusername/ayna.git
cd ayna
open Package.swift # macOS
open AynaMobile.xcodeproj # iOS / watchOS
Build from Source
- For macOS: open
Package.swiftin Xcode or useswift buildfrom the terminal. - For iOS/watchOS: open
AynaMobile.xcodeprojin Xcode. - Select the appropriate scheme and destination:
- Ayna scheme with My Mac for macOS (from Package.swift)
- Ayna-iOS scheme with an iOS Simulator (from AynaMobile.xcodeproj)
- Ayna-watchOS scheme with a watchOS Simulator (from AynaMobile.xcodeproj)
- Run with Cmd+R or click the Run button.
- Prefer running from Terminal? Use:
# macOS
swift build
# iOS (cross-compile)
swift build --triple arm64-apple-ios26.0
# watchOS (cross-compile)
swift build --triple arm64-apple-watchos12.0
Local Development
- Select the appropriate scheme and destination in Xcode (see Build from Source above).
- Build & run with Cmd+R.
- Important: Code in
Sources/Ayna/must compile for all platforms (macOS, iOS, watchOS). Never useAppKit/UIKitinSources/Ayna/without#if os()guards. - After modifying shared code, verify builds on multiple platforms:
swift build swift build --triple arm64-apple-ios26.0
Testing
Unit tests use Swift Testing (not XCTest). UI tests remain on XCTest.
- Run SwiftLint before committing:
swiftlint --strict - Run the unit test suite with:
swift test - UI tests live under
Tests/AynaUITests/. They launch the app with--ui-testingplusAYNA_UI_TESTING=1, which swaps in-memory storage, deterministic models, and mocked OpenAI responses. You can run only the UI bundle with:xcodebuild -project AynaUITests.xcodeproj -scheme AynaUITests -destination 'platform=macOS' test - Unit tests live in
Tests/AynaTests/and never touch the real Keychain or network. Use the helpers provided there:InMemoryKeychainStoragekeeps credentials in-memory during tests.MockURLProtocolinterceptsURLSessiontraffic forAIService.EncryptedConversationStoreandConversationManageraccept dependency-injected stores/file URLs for isolation.
- Keep every test deterministic—avoid real network calls, timers, or writes outside temporary directories.
See docs/testing.md for detailed testing patterns and templates.
Architecture
Core Structure
The codebase follows clean SwiftUI architecture with clear separation:
Models → ViewModels → Views → Services
Models (Models/Conversation.swift, Models/Message.swift)
- Pure data structures conforming to
Codablefor persistence - All models use
UUIDfor identification Conversationcontains array ofMessageobjects and metadata
ViewModels (ViewModels/ConversationManager.swift)
ConversationManager: Single source of truth for all conversation state- Manages CRUD operations, search, and persistence
- Uses
@Publishedproperties for reactive UI updates
Views
MacContentView/IOSContentView: Root view withNavigationSplitViewMacSidebarView/IOSSidebarView: Conversation listMacChatView/IOSChatView: Main chat interfaceMacSettingsView/IOSSettingsView: Configuration tabs
Services
AIService: Manages API communication (OpenAI-compatible endpoints with Azure auto-detection and Apple Intelligence)MCPServerManager: Handles Model Context Protocol tools (macOS only)KeychainStorage: Securely stores API keys
Design System (Sources/Ayna/Design/)
Theme(ColorTokens.swift): Semantic color tokens that adapt to light/dark mode and platformTypography: Consistent text styles with platform-appropriate sizingSpacing: Layout constants using a 4pt grid systemMotion(Animation.swift): Standardized animation presets and transitions
When building UI, prefer using design tokens over hardcoded values:
// Prefer this:
Text("Hello").font(Typography.body).foregroundStyle(Theme.textPrimary)
// Over this:
Text("Hello").font(.system(size: 14)).foregroundColor(.primary)
State Management
@StateObjectin App entry point forConversationManager.environmentObject()to inject throughout view hierarchy- Access via
@EnvironmentObjectin child views
Code Style and Patterns
- SwiftUI: Use
NavigationSplitViewfor layout. Use@StateObject,@EnvironmentObjectfor state. - Linting: Run
swiftlint --strictandswiftformat .before committing. - Logging: Use
DiagnosticsLoggerwith emoji prefixes for easy scanning. - Error Handling: Display user-friendly errors in the UI; log detailed errors with context.
Common Development Tasks
Adding a New AI Model
- Add the model identifier to
AIService.availableModels. - The model will automatically appear in the Settings picker.
Modifying UI Layout
- The app uses a minimum window size of 900x600.
- Sidebar minimum width is 260px.
- Use native SwiftUI controls for consistency.
Continuous Integration
Two GitHub Actions run automatically on pushes and pull requests:
.github/workflows/tests.ymlbuilds the project and runsswift teston a macOS runner..github/workflows/dev-build.ymlproduces a signed Release build plus a DMG artifact for manual verification.
Please make sure swift test succeeds locally before pushing to avoid CI noise.
Pull Request Checklist
- Tests pass locally (
swift test). - If modifying
Sources/Ayna/, verify iOS build:swift build --triple arm64-apple-ios26.0 - Run linting:
swiftlint --strict && swiftformat . - New source files include concise comments only where logic is non-obvious.
- Security-sensitive code (Keychain, encryption) includes informative logging on error paths.
- Use design tokens from
Sources/Ayna/Design/for colors, typography, spacing, and animations. - Update documentation (this file,
README.md,AGENTS.md, orSECURITY.md) when behavior changes.
We appreciate every contribution—thank you for helping keep ayna fast, secure, and reliable!