# MyIce Kotlin Multiplatform Ice hockey schedule and convocation viewer for Android, iOS, and Desktop (macOS/Windows/Linux). Port of the [Flutter app](https://gitea.parano.ch/herel/myice/myice_mobile) to Kotlin Multiplatform with Compose Multiplatform. ## Features - **OAuth login** via Infomaniak OIDC (platform-specific: Custom Tabs on Android, ASWebAuthenticationSession on iOS, local HTTP server on Desktop) - **Schedule viewing** — games and practices with colored event cards - **Filtering** — by account, age group, sub-group, and event type (all/games/practices) - **Event details** — convocations with player rosters and staff lists - **Offline caching** — schedule data persisted locally for offline access - **Sub-group extraction** — automatically parses team sub-groups from event names ## Tech Stack | Component | Technology | |-----------|-----------| | UI | Compose Multiplatform 1.11.1 (Material 3) | | Language | Kotlin 2.3.20 | | Networking | Ktor 3.5.1 (CIO on Android/Desktop, Darwin on iOS) | | Serialization | kotlinx.serialization 1.11.0 | | State | androidx.lifecycle ViewModel + StateFlow | | Storage | multiplatform-settings 1.3.0 | | Build | Gradle 9.5.1, AGP 8.13.2 | ## Project Structure ``` composeApp/ ├── src/ │ ├── commonMain/kotlin/ch/parano/myice/ │ │ ├── App.kt # Root composable + routing │ │ ├── models/ # Serializable data models │ │ ├── network/ # ApiService, AuthTokenHolder, ApiConfig │ │ ├── auth/ # OAuthClient interface, AuthSettings, TokenParser │ │ ├── cache/ # ScheduleCache (chunked storage) │ │ ├── filter/ # TypeFilter, EventFilter logic │ │ ├── viewmodel/ # AuthViewModel, ScheduleViewModel, EventDetailViewModel │ │ └── ui/ │ │ ├── theme/ # MyIceTheme (Material 3, indigo) │ │ ├── components/ # EventCard, FilterDropdown, LoadingIndicator │ │ └── screens/ # LoginScreen, ScheduleScreen, EventDetailScreen │ ├── androidMain/ # Android OAuth, MainActivity, CIO engine │ ├── iosMain/ # iOS OAuth, MainViewController, Darwin engine │ ├── desktopMain/ # Desktop OAuth, Main.kt, CIO engine │ └── commonTest/ # Unit tests (47 tests) ├── build.gradle.kts └── src/androidMain/AndroidManifest.xml ``` ## Build & Run ### Prerequisites - JDK 11+ - Android SDK (for Android builds) - Xcode (for iOS builds, macOS only) - Kotlin Multiplatform Mobile plugin (for iOS) ### Desktop (macOS) ```bash # Run directly ./gradlew composeApp:run # Build distributable DMG ./gradlew composeApp:packageDistributionForCurrentOS # Output: composeApp/build/compose/binaries/main/dmg/myice-1.0.0.dmg ``` ### Android ```bash # Debug APK ./gradlew composeApp:assembleDebug # Release APK (requires keystore, see below) ./gradlew composeApp:assembleRelease # Install and launch on connected device ~/Library/Android/sdk/platform-tools/adb install -r composeApp/build/outputs/apk/debug/composeApp-debug.apk ~/Library/Android/sdk/platform-tools/adb shell am start -n ch.parano.myice/.MainActivity ``` #### Release Signing Create `keystore.properties` at the project root (gitignored): ```properties storeFile=myice.keystore storePassword= keyAlias= keyPassword= ``` Generate a keystore: ```bash keytool -genkeypair -v \ -keystore myice.keystore \ -alias myice \ -keyalg RSA -keysize 2048 \ -validity 10000 ``` ### iOS The Swift source files and `Info.plist` are in `iosApp/`. The Xcode project (`.xcodeproj`) must be created manually: 1. Open Xcode → New Project → iOS App 2. Name it `MyIceApp`, bundle ID `ch.parano.myice` 3. Add the Swift files from `iosApp/` 4. Set the custom URL scheme `myice` in the URL Types 5. Build and run on a simulator or device ### Tests ```bash ./gradlew composeApp:desktopTest ``` ## Architecture ``` ┌─────────────────────────────────┐ │ UI (Compose) │ │ LoginScreen ScheduleScreen │ │ EventDetailScreen │ └──────────────┬──────────────────┘ │ StateFlow ┌──────────────┴──────────────────┐ │ ViewModels │ │ AuthVM ScheduleVM EventDetVM │ └──────────────┬──────────────────┘ │ ┌──────────────┴──────────────────┐ │ AuthSettings + Cache │ │ (multiplatform-settings) │ └──────────────┬──────────────────┘ │ ┌──────────────┴──────────────────┐ │ ApiService (Ktor) │ │ /userinfo /accounts /schedule│ │ /game/{id} │ └─────────────────────────────────┘ ``` ## Backend The app communicates with the MyIce FastAPI backend at `https://myice.parano.ch`. | Endpoint | Description | |----------|-------------| | `GET /login` | OAuth login redirect (Infomaniak OIDC) | | `GET /userinfo` | Current user info (email) | | `GET /accounts` | Available accounts/teams | | `GET /schedule?account=` | Events for an account | | `GET /game/{id}?account=` | Event detail with convocation | ## License Copyright (C) 2026 parano.ch This program is free software: you can redistribute it and/or modify it under the terms of the [GNU General Public License v3.0](LICENSE) as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.