-
Notifications
You must be signed in to change notification settings - Fork 31
Expand file tree
/
Copy pathAI-Coding-Handbook
More file actions
182 lines (149 loc) · 10.3 KB
/
Copy pathAI-Coding-Handbook
File metadata and controls
182 lines (149 loc) · 10.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
AI-Coding-Handbook
Purpose: Single source of truth for this repository. Keep it concise, accurate, and updated alongside code changes.
Project Overview
- Name: Music Assistant KMP Client (`MusicAssistantClient`)
- Goal: Cross‑platform client for the Music Assistant server providing playback control, queues, and library browsing.
- Tech: Kotlin Multiplatform + Compose Multiplatform; targets Android, iOS, Desktop (JVM); DI via Koin; HTTP/WebSocket via Ktor; persistence via Multiplatform Settings; logging via Kermit; image loading via Coil.
High-Level Architecture
- composeApp (KMP module)
- commonMain: Shared UI (Compose), domain/data, DI, settings, networking.
- androidMain: Android shells (activities/services/notification), player impl.
- iosMain: iOS glue (UIViewController host), Koin module, player shim.
- desktopMain: Desktop launcher and utilities.
- iosApp (Swift app shell)
- SwiftUI entry point hosting the KMP `ComposeApp` framework UI.
Key Data Flow
1) `SettingsRepository` stores theme and server connection (`ConnectionInfo`).
2) `ServiceClient` maintains WebSocket session to Music Assistant server, emits events, handles requests/responses.
3) `MainDataSource` observes session/events, exposes players/queues, orchestrates local builtin player state via `MediaPlayerController`.
4) UI `App()` composes screens; view models (Koin) read from repositories/data source and issue commands.
Modules and Important Files
Root Gradle
- `build.gradle.kts` and `settings.gradle.kts`: plugin and repository management; includes `:composeApp`.
composeApp module
- `composeApp/build.gradle.kts`
- Targets: Android, iOS (x64/arm64/sim), Desktop.
- iOS builds a static framework `ComposeApp` consumed by Xcode project.
- Common deps: Compose runtime/foundation/material, Koin, Ktor, Navigation, Coil, Settings, Reorderable, Kermit.
Shared (commonMain)
- API (`composeApp/src/commonMain/kotlin/io/music_assistant/client/api/`)
- `ConnectionInfo.kt`: Host/port/TLS details and helpers.
- `Requests.kt`: Typed builders for server commands (players, queues, music library, builtin player).
- `ServiceClient.kt`: Ktor WebSocket client; manages `SessionState`, listens, routes responses/events, exposes `events` and `serverInfo`.
- Data (`.../data/`)
- `MainDataSource.kt`: Core orchestrator; tracks players/queues, processes server events, interacts with `MediaPlayerController`; exposes state used by UI and handles actions.
- `model/`: DTOs for client and server representations and event decoding.
- DI (`.../di/`)
- `initKoin.kt`: Starts Koin combining `sharedModule` with platform modules.
- `SharedModule.kt`: Provides `SettingsRepository`, `ServiceClient`, `MainDataSource`, `MediaPlayerController` (expect/actual), and ViewModels.
- Player (`.../player/`)
- `MediaPlayerController.kt`: expect declarations for media controls; `PlatformContext` expect.
- Platform actuals live per target (Android/iOS/Desktop).
- Settings (`.../settings/`)
- `provideSettings.kt`: Platform-agnostic Settings provider (actuals per target).
- `SettingsRepository.kt`: Theme, connection info, sorting, local player id.
- UI (`.../ui/`)
- `compose/App.kt`: Root `App()` with theme and `NavHost` to `Main`, `Library`, `Settings`.
- `compose/main/*`: Main hub: players list, queue section, FAB actions; uses `MainViewModel` state.
- `compose/library/*`: Library browsing.
- `compose/settings/*`: Settings UI (theme, server config).
- `compose/nav/*`: Typed routes and helpers.
- `theme/*`: Colors, theme, system appearance bridge, `ThemeViewModel`.
- Utils (`.../utils/`)
- `SessionState.kt`: Connected/Connecting/Disconnected states.
- `myJson.kt`: Shared JSON configuration for serialization.
- Navigation helpers, extensions.
Android (androidMain)
- Entry/activity: `MainActivity.kt`, `MyApplication.kt`.
- Services: `MainMediaPlaybackService`, Android Auto service, notification manager, media session helper.
- Player actual: `MediaPlayerController.android.kt` integrates with ExoPlayer (via Media3).
- Resources: manifests, drawables, launcher assets.
iOS (iosMain in KMP)
- `MainViewController.kt`: `ComposeUIViewController { App() }` with Koin `iosModule()` initialization.
- DI: `IosModule.kt` provides `PlatformContext`.
- Player actual: `MediaPlayerController.ios.kt` (stubbed — implement for real playback when needed).
iOS Swift App (iosApp)
- `iOSApp.swift`: SwiftUI `@main` App hosting `ContentView`.
- `ContentView.swift`: Wraps KMP `MainViewController()` into SwiftUI via `UIViewControllerRepresentable`.
- `Configuration/Config.xcconfig`: `TEAM_ID`, `BUNDLE_ID`, `APP_NAME`.
- Assets and Xcode project; build phase calls Gradle `:composeApp:embedAndSignAppleFrameworkForXcode` to produce `ComposeApp.framework`.
Navigation
- Start destination: `MainScreen`.
- Other routes: `LibraryScreen` with args, `SettingsScreen`.
View Models (Koin)
- `ThemeViewModel`, `MainViewModel`, `SettingsViewModel`, `LibraryViewModel` provided via `sharedModule`.
- Use `koinViewModel<...>()` in Compose. State exposed via Kotlin Flows and collected with `collectAsStateWithLifecycle`.
Builtin Player Flow
- `MainDataSource` registers local builtin player with server using a persistent local player id.
- Responds to server `BuiltinPlayerEvent` to prepare/play/pause/stop via `MediaPlayerController`.
- Reports position/playing state back with `updateBuiltInPlayerStateRequest` at dynamic intervals.
What Each Major Component Does
- `ServiceClient`: network session, request/response routing, event stream.
- `MainDataSource`: state aggregation, commands for queues and players, builtin player bridge.
- `MediaPlayerController`: abstract media control API with platform implementations.
- `SettingsRepository`: user preferences and connection info persistence.
- UI screens: reflect state and delegate actions back to `MainDataSource` via ViewModels.
- DI modules: wire dependencies per platform and shared.
iOS Development: Build & Test Instructions
Prerequisites
- macOS with Xcode 15+ and Command Line Tools installed.
- JDK 17 (Gradle config uses Java 17); Android SDK not required for iOS build.
- CocoaPods not used; KMP builds a static framework consumed by Xcode.
First-Time Setup
1) Open Terminal in repo root.
2) Ensure Gradle wrapper is executable: `chmod +x ./gradlew`.
3) (Optional) Prebuild KMP framework for iOS simulator: `./gradlew :composeApp:embedAndSignAppleFrameworkForXcode`.
Running on iOS Simulator via Xcode
1) Open `iosApp/iosApp.xcodeproj` in Xcode.
2) Set signing:
- Edit `iosApp/Configuration/Config.xcconfig` with your `TEAM_ID` and desired `BUNDLE_ID`.
- In the project’s Signing settings, ensure your team is selected.
3) Select a Simulator (e.g., iPhone 15) and Build/Run.
- The Xcode build phase triggers the Gradle task to produce `ComposeApp.framework` into `composeApp/build/xcode-frameworks/...` and links it.
Running on a Physical iOS Device
1) Connect device and select as the run destination.
2) Ensure provisioning allows your `BUNDLE_ID` and Team.
3) Build/Run from Xcode; Gradle task will embed/sign the KMP framework as part of build.
Configuring the App
- On first launch you will be prompted to configure connection settings if none are stored.
- Enter server host, port, and TLS choice to connect to a running Music Assistant server.
Testing Checklist (Manual)
- App launches and shows Main screen state.
- Navigate to Settings and set server connection; verify session transitions from Connecting to Connected.
- Players list loads; select a player; queue displays.
- Perform player actions: play/pause/next/previous; verify requests sent.
- Modify repeat/shuffle; verify state updates.
- Add/remove/move queue items and observe updates.
- Switch theme in Settings and verify UI update.
Unit/UI Tests (KMP side)
- Common logic is suitable for unit tests (ServiceClient abstractions, MainDataSource transformations). Add tests under `composeApp/src/commonTest`.
- iOS UI tests can be added in Xcode for SwiftUI shell, though primary UI is Compose embedded view.
iOS Player Implementation Note
- `MediaPlayerController.ios.kt` is currently stubbed; implement using AVFoundation (AVPlayer) to enable local builtin playback on iOS. Ensure calls run on main thread and report position/playing state to `MainDataSource` as in Android.
SwiftUI Best Practices (for iOS shell)
- Follow `.cursor/rules/ios-swift-dev-rule.mdc` for view model observation, environment usage, accessibility, and previews when editing Swift files.
Conventions
- Koin for DI; prefer `singleOf`/`viewModelOf` wiring in `SharedModule.kt`.
- Compose navigation uses typed routes in `ui/compose/nav` with `toRoute()` helpers.
- Flows collected with lifecycle-aware `collectAsStateWithLifecycle`.
How to Update This Handbook
- Keep sections aligned with code changes. When adding modules or screens, add a short description here.
- For iOS changes, update the Build & Test section if the Gradle/Xcode integration changes.
iOS TODOs (Current Gaps & Next Steps)
- Implement local playback on iOS:
- File: `composeApp/src/iosMain/kotlin/io/music_assistant/client/player/MediaPlayerController.ios.kt`
- Replace TODOs using AVFoundation (AVPlayer): implement `prepare`, `start`, `pause`, `stop`, `seekTo`, `getCurrentPosition`, `getDuration`, `isPlaying`, `release`.
- Ensure all player interactions run on the main thread.
- Call `MediaPlayerListener` callbacks appropriately (`onReady`, `onAudioCompleted`, `onError`).
- Tie into `MainDataSource.updateLocalPlayerState` cadence for accurate progress reporting.
- Verify DI wiring for iOS:
- File: `composeApp/src/iosMain/kotlin/io/music_assistant/client/di/IosModule.kt`
- Confirm any additional platform services (e.g., audio session configuration) are provided when AVPlayer is added.
- Audio session configuration:
- Configure `AVAudioSession` (category `.playback`, mode `.default`, setActive true) during iOS bootstrap to allow background/lock‑screen playback when implemented.
- Consider interruptions handling (route changes, phone calls) and propagate to `MediaPlayerController`/`MainDataSource`.
- Background and remote controls (future):
- If background playback is desired, add MPNowPlayingInfoCenter updates and remote command center handlers in Swift or via Skiko/interop layer.
- Stability and lifecycle checks:
- Ensure `MainViewController()` and Koin init are idempotent on app foreground/background transitions.
- Validate that `ServiceClient` reconnects properly when app resumes on iOS.