# iOS App (Xcode host) Kotlin Multiplatform Compose UI runs inside a native iOS shell. Sources here are **Xcode-ready scaffolding**; building requires **macOS + Xcode**. ## App Group migration | Old (Flutter) | New (KMP) | |---|---| | `group.com.zhushenwudi.lovelivemusicplayer` | **`group.top.zhushenwudi.llmp`** | Kotlin writes widget / PiP / CarPlay keys via `WidgetDataWriter` (`shared/.../IosPlatformHooks.kt`). Swift Widget + CarPlay read the same suite. ## Build steps (macOS) ```bash cd LoveLiveMusicPlayerKMP ./gradlew :composeApp:linkDebugFrameworkIosSimulatorArm64 # or iosArm64 for device ``` 1. Open Xcode → **File → New → Project → App** (or add targets to an existing workspace). 2. Set bundle ID **`top.zhushenwudi.llmp`** (main app). 3. Copy `iosApp/iosApp/*` into the app target; add `WidgetExtension/*` as a Widget Extension target (`top.zhushenwudi.llmp.widget`). 4. Link the Gradle-produced **`ComposeApp.framework`** (and embedded **`Shared.framework`** if split). 5. Signing: enable App Groups `group.top.zhushenwudi.llmp` on **both** app + widget targets (`iosApp.entitlements`, `WidgetExtension.entitlements`). 6. CarPlay: entitlements include `com.apple.developer.carplay-audio` and `com.apple.developer.playable-content` — **requires Apple Developer approval** before TestFlight/App Store CarPlay works. 7. Add **`black.mp4`** (minimal black video) to the app bundle for PiP — copy from Flutter `ios/Runner` if present, or generate a 1-frame H.264 clip. 8. Copy widget image assets from Flutter `ios/HomeWidgetExample/Assets.xcassets` (`logo`, `cd_white`, `cd_black`, favorites). Entry point: ```swift MainViewControllerKt.MainViewController() // ComposeApp framework ``` `MainViewController.kt` calls `installIosPlatformServices()` on startup. ## Targets & layout ``` iosApp/ iosApp/ AppDelegate.swift — lifecycle, widget shutdown, notification wiring SceneDelegate.swift — UIWindow + PiP notification handlers ContentView.swift — SwiftUI host for Compose UIViewController HostBridge.swift — share sheet, deep links, widget/CarPlay → host Info.plist iosApp.entitlements CarPlay/CarPlaySceneDelegate.swift pip/ — desktop lyric PiP (from Flutter Runner/pip) WidgetExtension/ — White/Black home widgets (from Flutter HomeWidgetExample) ``` ## Kotlin ↔ Swift bridge (remaining macOS work) | Feature | Kotlin side | Swift side | Gap | |---|---|---|---| | Playback | `PlayerController.ios.kt` AVPlayer + MPRemoteCommandCenter | — | Done in Kotlin | | Home widget data | `IosHomeWidgetController` → UserDefaults | Widget reads keys | Wire widget tap → Kotlin player (export callback or observe `llmpHostTogglePlay`) | | PiP lyrics | `IosDesktopLyricController` posts notifications | `PipScreenManager` | Done via NotificationCenter | | Share | `IosShareBridge` posts `llmpShareRequest` | `HostBridge.presentShare` | Done | | Deep link `llmp://` | `IosDeepLinkHandler` | `SceneDelegate` + `HostBridge` | Handle payload in Kotlin UI layer | | CarPlay | `IosCarPlayHook.refreshCatalog()` writes JSON | `CarPlaySceneDelegate` | Play/group/menu callbacks need Kotlin export | ## CarPlay note `CarPlaySceneDelegate` reads `carplay_*` JSON from the App Group. Full parity with Flutter `flutter_carplay` needs exported Kotlin handlers for `onPlay(musicId)` — until then, NotificationCenter events (`llmpHostCarPlayPlay`, etc.) are posted for the host to forward. ## Permissions Add usage strings in Xcode as needed (camera for QR, photo library, etc.) — see Flutter `ios/Runner/Info.plist` for reference.