418a8978f2e82b7164e8034b9ebbf491f62b99d2
Complete the MyIceK rename by updating the Kotlin package from ch.parano.myice to ch.parano.myicek across all source sets (androidMain, commonMain, desktopMain, iosMain, commonTest), the build.gradle.kts (namespace, applicationId, mainClass, desktop packageName) and the iOS Info.plist bundle URL name. The myice:// OAuth scheme is kept unchanged as it depends on the backend. Also add pull-to-refresh to ScheduleScreen using Material 3 PullToRefreshBox, replacing the manual loading overlay.
MyIce Kotlin Multiplatform
Ice hockey schedule and convocation viewer for Android, iOS, and Desktop (macOS/Windows/Linux). 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
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 as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
Languages
Kotlin
98%
Swift
1.2%
Makefile
0.8%