A Kotlin Multiplatform SDK for capturing and uploading scorecard images to Firebase Storage. Share business logic across Android and iOS while using native UI frameworks.
- Camera Capture - Native camera integration with landscape orientation
- Document Detection - Real-time paper/document edge detection using Vision framework (iOS)
- Auto-Crop - Automatically crops captured images to detected document boundaries
- Gallery Picker - Select images from device photo library
- Image Processing - Automatic resizing and JPEG compression
- Firebase Upload - Direct upload to Firebase Storage with progress tracking
- Compose UI Components - Ready-to-use camera UI components for Android
- SwiftUI Support - Native iOS implementation with SwiftUI
- Permission Handling - Cross-platform permission management
┌─────────────────────────────────────────────────────────────┐
│ Shared Module │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Storage │ │ Permissions │ │ UI Components │ │
│ │ Repository │ │ Handler │ │ (Compose Common) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐│
│ │ Koin Dependency Injection ││
│ └─────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Android │ │ iOS │
│ ┌───────────┐ │ │ ┌───────────┐ │
│ │ CameraX │ │ │ │AVFoundation│ │
│ │ Compose │ │ │ │ SwiftUI │ │
│ └───────────┘ │ │ │ Vision │ │
└─────────────────┘ └─────────────────┘
GitHub Packages
// settings.gradle.kts
dependencyResolutionManagement {
repositories {
maven {
url = uri("https://maven.pkg.github.com/golfgenius/ScoreScanSDK")
credentials {
username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR")
password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN")
}
}
}
}
// build.gradle.kts
dependencies {
implementation("com.swingu.scorecardcam:scorescan-sdk-android:1.0.0")
}Requires a GitHub Personal Access Token (PAT) with
read:packagesscope. Addgpr.userandgpr.keyto yourgradle.propertiesor set the environment variables.
Local Build
./gradlew :shared:publishAndroidSDKSwift Package Manager
In Xcode: File > Add Package Dependencies > Enter:
https://github.com/golfgenius/ScoreScanSDK
Requires access to the private repository. Add your GitHub account in Xcode > Settings > Accounts, or configure SSH keys.
XCFramework (Manual)
./gradlew :shared:buildXCFrameworkThe built framework will be at shared/build/XCFrameworks/release/ScoreScanSDK.xcframework.
// Application.kt
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
setAndroidContext(this)
initKoin { androidContext(this@MyApp) }
}
}
// Upload an image
val uploadService = getKoinHelper().getImageUploadService()
lifecycleScope.launch {
when (val result = uploadService.uploadImageWithAutoName(
source = imagePath,
folder = "scorecards"
)) {
is UploadResult.Success -> Log.d("Upload", "URL: ${result.downloadUrl}")
is UploadResult.Error -> Log.e("Upload", result.message)
}
}// App.swift
@main
struct MyApp: App {
init() {
FirebaseApp.configure()
ModulesKt.doInitKoin()
}
}
// Use the camera view
ScorecardCameraView(
config: ScorecardConfig(
folder: "scorecards",
maxWidth: 4032,
maxHeight: 3024,
quality: 95
),
onResult: { result in
switch result {
case .success(let url):
print("Uploaded: \(url)")
case .cancelled:
break
case .error(let message):
print("Error: \(message)")
}
}
)| Platform | Minimum Version |
|---|---|
| Android | API 24 (Android 7.0) |
| iOS | 16.0 |
| Kotlin | 2.3.0 |
- Firebase Storage (via GitLive firebase-kotlin-sdk)
- Koin 4.0 for dependency injection
- Compose Multiplatform for UI components
- CameraX (Android) / AVFoundation + Vision (iOS)
ScoreScan/
├── shared/ # Kotlin Multiplatform shared module
│ ├── commonMain/ # Shared code
│ │ ├── di/ # Koin modules
│ │ ├── permissions/ # Permission abstractions
│ │ ├── storage/ # Firebase Storage logic
│ │ └── ui/camera/ # Compose UI components
│ ├── androidMain/ # Android-specific implementations
│ └── iosMain/ # iOS-specific implementations
├── Sources/
│ └── ScorecardCamUI/ # Swift UI layer (SPM target)
│ ├── ScorecardCameraView.swift # Main camera + review UI
│ ├── DocumentDetector.swift # Vision rectangle detection & cropping
│ ├── DocumentOverlayView.swift # Live detection overlay
│ └── CameraPreviewView.swift # AVCaptureSession preview
├── composeApp/ # Android demo app
├── iosApp/ # iOS demo app (SwiftUI)
├── Package.swift # Swift Package Manager manifest
└── .github/workflows/ # CI/CD release automation
# Build Android AAR
./gradlew :shared:assembleRelease
# Build iOS XCFramework
./gradlew :shared:buildXCFramework
# Publish to local Maven
./gradlew :shared:publishAndroidSDK
# Run Android app
./gradlew :composeApp:installDebug
# Run iOS app
open iosApp/iosApp.xcworkspace- Create a project at Firebase Console
- Enable Firebase Storage
- Add platform config files:
- Android:
google-services.jsonincomposeApp/ - iOS:
GoogleService-Info.plistiniosApp/iosApp/
- Android:
SDK releases are automated via GitHub Actions. When a GitHub Release is published:
- Android - The AAR is published to GitHub Packages (Maven)
- iOS - The XCFramework is built, zipped, and committed to the repo for SPM resolution
Both distribution channels require repository access, keeping the SDK private.
# Android AAR + Maven
./gradlew :shared:publishAndroidSDK
# iOS XCFramework
./gradlew :shared:buildXCFrameworkCopyright 2024 Swingu
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Contributions are welcome! Please open an issue or submit a pull request.