The app:iosApp module is the native iOS application built with SwiftUI and UIKit, consuming the shared Kotlin Multiplatform code from app:ios-kit. It provides the iOS-specific app shell, navigation, and platform integration.
- Provide iOS application entry point
- Integrate shared Kotlin code via CocoaPods
- Implement iOS-specific UI adaptations
- Configure iOS app settings and capabilities
- Support App Store distribution via Fastlane
- Enable Mac Catalyst for macOS support
app/iosApp (iOS Application - Swift/SwiftUI)
├── iosApp/
│ ├── iOSApp.swift # App entry point
│ ├── AppDelegate.swift # App lifecycle
│ ├── ComposeController.swift # Compose UI wrapper
│ ├── RootHolder.swift # Root view holder
│ ├── CipherChecker.swift # SQLCipher verification
│ ├── Assets.xcassets/ # App icons & images
│ ├── Info.plist # App configuration
│ └── Preview Content/ # SwiftUI previews
├── iosApp.xcodeproj/ # Xcode project
├── iosApp.xcworkspace/ # Xcode workspace (with pods)
├── Podfile # CocoaPods dependencies
├── Podfile.lock # Locked dependency versions
├── Pods/ # Downloaded CocoaPods
└── fastlane/ # CI/CD automation
├── Fastfile
├── Appfile
└── metadata/ # App Store metadata
SwiftUI app entry point:
import SwiftUI
import iosComposePod
@main
struct iOSApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
init() {
// Initialize Kotlin/Native code
IosAppKt.doInitKoin()
}
var body: some Scene {
WindowGroup {
ComposeController()
.ignoresSafeArea(.all)
}
}
}UIKit bridge to Compose Multiplatform:
import UIKit
import SwiftUI
import iosComposePod
struct ComposeController: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIViewController {
return MainKt.ComposeViewController()
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
// Updates handled by Compose
}
}Manages app root and lifecycle:
class RootHolder: ObservableObject {
let root: Root
init() {
root = RootBuilder().build()
}
deinit {
root.onDestroy()
}
}Verifies SQLCipher functionality:
import SQLCipher
class CipherChecker {
static func verifySQLCipher() -> Bool {
let version = sqlite3_libversion_number()
return version > 0
}
}- Target: iosApp
- Minimum iOS Version: 14.0
- Supported Platforms: iOS, Mac Catalyst (macOS)
- Languages: Swift 5.9+
- Frameworks: SwiftUI, UIKit, Combine
- iosApp - Debug builds for development
- iosApp Release - Release builds for distribution
- Check Pods Manifest.lock - Verify pod installation
- Sources - Compile Swift code
- Frameworks - Link frameworks and libraries
- Embed Frameworks - Embed Kotlin framework
- Copy Resources - Bundle resources
- Build iosComposePod - CocoaPods script phase
platform :ios, '14.0'
use_frameworks!
target 'iosApp' do
# Kotlin Multiplatform shared code
pod 'iosComposePod', :path => '../ios-kit'
# SQLCipher for database encryption
pod 'SQLCipher', '~> 4.5.5'
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.0'
end
end
endcd app/iosApp
pod installAlways use iosApp.xcworkspace (not .xcodeproj) after pod install.
- Open
iosApp.xcworkspacein Xcode - Select simulator or device
- Press ⌘R (Run)
# Build for simulator
xcodebuild -workspace iosApp.xcworkspace \
-scheme iosApp \
-configuration Debug \
-sdk iphonesimulator \
-derivedDataPath build
# Build for device
xcodebuild -workspace iosApp.xcworkspace \
-scheme iosApp \
-configuration Release \
-sdk iphoneos \
-derivedDataPath build# Build for App Store
xcodebuild -workspace iosApp.xcworkspace \
-scheme iosApp \
-configuration Release \
-sdk iphoneos \
-archivePath build/iosApp.xcarchive \
archive
# Export IPA
xcodebuild -exportArchive \
-archivePath build/iosApp.xcarchive \
-exportPath build/ipa \
-exportOptionsPlist ExportOptions.plistLocated in Note Delight Unit-Tests/:
import XCTest
@testable import iosApp
class Note_Delight_Unit_Tests: XCTestCase {
func testSQLCipher() {
XCTAssertTrue(CipherChecker.verifySQLCipher())
}
func testKotlinInit() {
// Test Kotlin initialization
IosAppKt.doInitKoin()
// Assertions...
}
}Can be added in a separate UI test target:
import XCTest
class NoteDelightUITests: XCTestCase {
func testCreateNote() {
let app = XCUIApplication()
app.launch()
// Test note creation flow
app.buttons["Create Note"].tap()
// ...
}
}# Run all tests
xcodebuild test \
-workspace iosApp.xcworkspace \
-scheme iosApp \
-destination 'platform=iOS Simulator,name=iPhone 15'Key configurations:
<key>CFBundleDisplayName</key>
<string>Note Delight</string>
<key>CFBundleIdentifier</key>
<string>com.softartdev.notedelight</string>
<key>CFBundleVersion</key>
<string>841</string>
<key>CFBundleShortVersionString</key>
<string>8.4.1</string>
<key>UIApplicationSceneManifest</key>
<dict>
<!-- SwiftUI scene configuration -->
</dict>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>- App Groups: For sharing data
- iCloud: For cloud storage (optional)
- Background Modes: If needed
- File Provider: For file access (optional)
Automates TestFlight and App Store deployment:
default_platform(:ios)
platform :ios do
desc "Build and upload to TestFlight"
lane :beta do
build_app(
workspace: "iosApp.xcworkspace",
scheme: "iosApp",
configuration: "Release",
export_method: "app-store"
)
upload_to_testflight(
skip_waiting_for_build_processing: true
)
end
desc "Release to App Store"
lane :release do
build_app(
workspace: "iosApp.xcworkspace",
scheme: "iosApp",
configuration: "Release",
export_method: "app-store"
)
upload_to_app_store(
submit_for_review: false,
automatic_release: false
)
end
endcd app/iosApp
fastlane beta # Deploy to TestFlight
fastlane release # Deploy to App StoreThe app supports running on macOS via Mac Catalyst:
- In Xcode project settings
- Select "iosApp" target
- Check "Mac" in "Supported Destinations"
#if targetEnvironment(macCatalyst)
// Mac-specific code
window.windowScene?.titlebar?.toolbar = NSToolbar()
#else
// iOS-specific code
#endif- App Store Connect: Create app listing
- Certificates: Valid distribution certificate
- Provisioning Profile: App Store profile
- App Icon: All required sizes in Assets.xcassets
- Screenshots: Required for all device sizes
- Privacy Policy: Required URL
Update in multiple places:
- Xcode project: Build number & version
Info.plist: CFBundleVersion & CFBundleShortVersionString- Fastlane metadata: Version information
# Via Fastlane
cd app/iosApp
fastlane release
# Or manually via Xcode
# Product → Archive → Distribute App- English (default)
- Russian
// In SwiftUI
Text("note_title")
.localized()
// Or use generated strings
Text(String(localized: "note_title"))- In Xcode: File → New → File → Strings File
- Name:
Localizable.strings - Localize: Select languages in File Inspector
- Add translations
iosComposePod- Kotlin Multiplatform shared codeSQLCipher- Database encryption
- SwiftUI - Modern UI framework
- UIKit - Traditional UI framework
- Combine - Reactive framework
- Foundation - Core functionality
- Enable bitcode (if supported)
- Use on-demand resources
- Compress images
- Remove unused code
- Lazy loading
- Background processing
- Image caching
- Database optimization
When working with this module:
- Swift conventions: Follow Swift API design guidelines
- SwiftUI: Use SwiftUI for new iOS features
- Kotlin interop: Test Swift-Kotlin bridge thoroughly
- Xcode: Keep project files organized
- CocoaPods: Keep Podfile.lock in version control
- Testing: Write unit and UI tests
- Accessibility: Support VoiceOver and Dynamic Type
- Privacy: Handle sensitive data appropriately
- App Store: Follow App Store Review Guidelines
- Version: Keep version numbers synchronized
struct ContentView: View {
@StateObject private var rootHolder = RootHolder()
var body: some View {
ComposeController()
.environmentObject(rootHolder)
.ignoresSafeArea()
}
}do {
try someKotlinFunction()
} catch let error as NSError {
print("Kotlin error: \(error.localizedDescription)")
}class ViewManager {
weak var delegate: Delegate?
var kotlinObject: KotlinClass?
deinit {
// Clean up Kotlin objects
kotlinObject?.onDestroy()
}
}- Pod not found: Run
pod install - Framework not found: Clean build folder (⇧⌘K)
- Version mismatch: Run
pod update
- Signing errors: Check provisioning profiles
- Framework errors: Verify ios-kit is built
- Swift errors: Check Swift version compatibility
- Crash on launch: Check initialization order
- Black screen: Verify Compose UI setup
- Memory leaks: Use Instruments to profile
- Depends on:
app:ios-kit(Kotlin framework) - Alternative apps:
app:android,app:desktop,app:web