de107a4295aadcddc698c5ad74d0e8809e3e87ee
MyIce Kotlin Multiplatform
Ice hockey schedule and convocation viewer for Android, iOS, and Desktop (macOS/Windows/Linux). Port of the Flutter app 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)
# Run directly
./gradlew composeApp:run
# Build distributable DMG
./gradlew composeApp:packageDistributionForCurrentOS
# Output: composeApp/build/compose/binaries/main/dmg/myice-1.0.0.dmg
Android
# 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):
storeFile=myice.keystore
storePassword=<your-password>
keyAlias=<your-alias>
keyPassword=<your-password>
Generate a keystore:
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:
- Open Xcode → New Project → iOS App
- Name it
MyIceApp, bundle IDch.parano.myice - Add the Swift files from
iosApp/ - Set the custom URL scheme
myicein the URL Types - Build and run on a simulator or device
Tests
./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
Private project.
Languages
Kotlin
98.3%
Swift
1.7%