The Beekeeper app uses Compose Multiplatform which shares 95% of code between Android and iOS. The iOS-specific files are ready, but you need to build and run from a Mac.
-
iOS Framework Configuration -
composeApp/build.gradle.ktshas iOS targets configured:- iosX64 (Intel simulator)
- iosArm64 (Physical devices)
- iosSimulatorArm64 (M1/M2/M3 simulator)
-
Platform-Specific Code:
composeApp/src/iosMain/kotlin/com/beekeeper/app/data/database/DatabaseDriverFactory.kt- SQLite for iOScomposeApp/src/iosMain/kotlin/com/beekeeper/app/data/api/ApiConfig.ios.kt- API configurationcomposeApp/src/iosMain/kotlin/com/beekeeper/app/MainViewController.kt- iOS entry point
-
iOS App Wrapper:
iosApp/iOSApp.swift- SwiftUI wrapperiosApp/Info.plist- App permissions (camera, location, photos)
From the project root:
# For M1/M2/M3 Mac (Apple Silicon)
./gradlew linkDebugFrameworkIosSimulatorArm64
# For Intel Mac
./gradlew linkDebugFrameworkIosX64
# For physical iPhone/iPad
./gradlew linkDebugFrameworkIosArm64The framework will be built to:
composeApp/build/bin/iosSimulatorArm64/debugFramework/ComposeApp.framework
-
Open Xcode
-
Create new App project:
- Product Name: Beekeeper
- Bundle Identifier: com.beekeeper.app
- Interface: SwiftUI
- Language: Swift
-
Replace the default App file with
iosApp/iOSApp.swift -
Add the Framework:
- Drag
ComposeApp.frameworkinto Xcode project - Select "Copy items if needed"
- Add to "Frameworks, Libraries, and Embedded Content"
- Set to "Embed & Sign"
- Drag
-
Add
Info.plistpermissions fromiosApp/Info.plist
- Select your simulator or device
- Press ⌘R to build and run
Currently only Android has camera support (CameraX). For iOS, you need to add:
// In a new file: CameraView.swift
import SwiftUI
import AVFoundation
struct CameraView: UIViewControllerRepresentable {
@Binding var capturedImage: UIImage?
@Environment(\.presentationMode) var presentationMode
func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.delegate = context.coordinator
picker.sourceType = .camera
return picker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
let parent: CameraView
init(_ parent: CameraView) {
self.parent = parent
}
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[.originalImage] as? UIImage {
parent.capturedImage = image
}
parent.presentationMode.wrappedValue.dismiss()
}
}
}Then integrate it into the Compose screens using UIViewController interop.
To test on a real iPhone/iPad:
- Connect device via USB
- Build for iosArm64:
./gradlew linkDebugFrameworkIosArm64
- In Xcode, select your device and run
Make sure your Mac can reach the backend:
- If backend is on same Mac:
localhost:2020works - If backend is on another machine: Update
ApiConfig.ios.ktwith the IP address
- Make changes to Kotlin code in
composeApp/src/commonMain/ - Rebuild framework:
./gradlew linkDebugFrameworkIosSimulatorArm64
- In Xcode: Product → Clean Build Folder (Shift + ⌘ + K)
- Build and run in Xcode (⌘R)
- Make sure you built the framework first
- Check the framework path in Xcode project settings
- Clean Xcode build: Shift + ⌘ + K
- Rebuild the Kotlin framework
- Restart Xcode
- Check
ApiConfig.ios.kthas correct backend URL - Ensure backend is running and accessible from iOS simulator/device
- Check
Info.plisthasNSAppTransportSecurityif using HTTP (not HTTPS)
- ✅ Build the Kotlin framework
- ✅ Create Xcode project
- ✅ Test app launches
⚠️ Add iOS camera implementation⚠️ Test all features (tasks, inspections, AI chat)⚠️ Handle iOS-specific UI adjustments if needed