From 0b4e201808898704caa96eaf1ca491e27eb6d9e9 Mon Sep 17 00:00:00 2001 From: Syer10 Date: Fri, 1 Dec 2023 22:08:05 -0500 Subject: [PATCH] Make iOS application work --- i18n/build.gradle.kts | 1 + .../kotlin/ca/gosyer/jui/ios/Main.kt | 93 +++++++------------ iosApp/iosApp.xcodeproj/project.pbxproj | 20 +++- iosApp/iosApp/ContentView.swift | 4 +- .../ui/base/image/IosImageLoaderBuilder.kt | 2 + .../jui/ui/base/prefs/IosColorExtensions.kt | 2 + .../jui/uicore/resources/IosImageResource.kt | 2 + 7 files changed, 63 insertions(+), 61 deletions(-) diff --git a/i18n/build.gradle.kts b/i18n/build.gradle.kts index 37de7a9bab..a6e6be1d09 100644 --- a/i18n/build.gradle.kts +++ b/i18n/build.gradle.kts @@ -24,6 +24,7 @@ kotlin { binaries { framework { baseName = "i18n" + isStatic = true } } } diff --git a/ios/src/uikitMain/kotlin/ca/gosyer/jui/ios/Main.kt b/ios/src/uikitMain/kotlin/ca/gosyer/jui/ios/Main.kt index c5339fa0b9..e9045eb229 100644 --- a/ios/src/uikitMain/kotlin/ca/gosyer/jui/ios/Main.kt +++ b/ios/src/uikitMain/kotlin/ca/gosyer/jui/ios/Main.kt @@ -1,3 +1,5 @@ +@file:OptIn(ExperimentalForeignApi::class, BetaInteropApi::class) + package ca.gosyer.jui.ios import androidx.compose.animation.Crossfade @@ -24,11 +26,13 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.compose.ui.window.Application +import androidx.compose.ui.window.ComposeUIViewController import ca.gosyer.jui.ui.base.theme.AppTheme import ca.gosyer.jui.ui.main.MainMenu import ca.gosyer.jui.uicore.vm.ContextWrapper import ca.gosyer.jui.uicore.vm.Length +import kotlinx.cinterop.BetaInteropApi +import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.autoreleasepool import kotlinx.cinterop.cstr import kotlinx.cinterop.memScoped @@ -37,7 +41,16 @@ import kotlinx.cinterop.useContents import kotlinx.coroutines.delay import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import org.lighthousegames.logging.FixedLogLevel +import org.lighthousegames.logging.KmLog +import org.lighthousegames.logging.KmLogging +import org.lighthousegames.logging.LogFactory +import org.lighthousegames.logging.LogLevel +import org.lighthousegames.logging.LogLevelController +import org.lighthousegames.logging.Logger +import org.lighthousegames.logging.TagProvider import platform.Foundation.NSStringFromClass +import platform.Foundation.NSThread import platform.UIKit.UIApplication import platform.UIKit.UIApplicationDelegateProtocol import platform.UIKit.UIApplicationDelegateProtocolMeta @@ -45,76 +58,40 @@ import platform.UIKit.UIApplicationMain import platform.UIKit.UIResponder import platform.UIKit.UIResponderMeta import platform.UIKit.UIScreen +import platform.UIKit.UIViewController import platform.UIKit.UIWindow import platform.UIKit.safeAreaInsets import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds -fun main() { - val args = emptyArray() - memScoped { - val argc = args.size + 1 - val argv = (arrayOf("skikoApp") + args).map { it.cstr.ptr }.toCValues() - autoreleasepool { - UIApplicationMain(argc, argv, null, NSStringFromClass(SkikoAppDelegate)) - } - } -} - -class SkikoAppDelegate - @OverrideInit - constructor() : UIResponder(), UIApplicationDelegateProtocol { - companion object : UIResponderMeta(), UIApplicationDelegateProtocolMeta - - private var _window: UIWindow? = null - override fun window() = _window - override fun setWindow(window: UIWindow?) { - _window = window - } +fun initializeApplication(): UIViewController { + val appComponent = AppComponent.getInstance(ContextWrapper()) - private val context = ContextWrapper() + appComponent.migrations.runMigrations() + appComponent.appMigrations.runMigrations() - private val appComponent = AppComponent.getInstance(context) + appComponent.downloadService.init() + appComponent.libraryUpdateService.init() - init { - appComponent.migrations.runMigrations() - appComponent.appMigrations.runMigrations() + val uiHooks = appComponent.hooks + val context = appComponent.context - appComponent.downloadService.init() - appComponent.libraryUpdateService.init() - } - - val uiHooks = appComponent.hooks - - override fun application( - application: UIApplication, - didFinishLaunchingWithOptions: Map?, - ): Boolean { - window = UIWindow(frame = UIScreen.mainScreen.bounds).apply { - val insets = safeAreaInsets.useContents { - WindowInsets(left.dp, top.dp, right.dp, bottom.dp) + return ComposeUIViewController { + CompositionLocalProvider(*uiHooks) { + AppTheme { + Box(Modifier.fillMaxSize()) { + MainMenu() + ToastOverlay( + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(bottom = 64.dp), + context = context, + ) } - - rootViewController = Application("Tachidesk-JUI") { - CompositionLocalProvider(*uiHooks) { - AppTheme { - Box(Modifier.fillMaxSize().windowInsetsPadding(insets)) { - MainMenu() - ToastOverlay( - modifier = Modifier - .align(Alignment.BottomCenter) - .padding(bottom = 64.dp), - context = context, - ) - } - } - } - } - makeKeyAndVisible() } - return true } } +} @Composable fun ToastOverlay( diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj index ca20bc135f..ed4404dd10 100644 --- a/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -17,7 +17,7 @@ 058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; }; - 7555FF7B242A565900829871 /* .app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = .app; sourceTree = BUILT_PRODUCTS_DIR; }; + 7555FF7B242A565900829871 /* .app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; name = .app; path = "Tachidesk-JUI.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; @@ -87,6 +87,7 @@ F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */, 7555FF77242A565900829871 /* Sources */, 7555FF79242A565900829871 /* Resources */, + 899476382B1ACFC30060F0C4 /* ShellScript */, ); buildRules = ( ); @@ -143,6 +144,23 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 899476382B1ACFC30060F0C4 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$SRCROOT/../gradlew\" -p \"$SRCROOT/../\" :i18n:copyFrameworkResourcesToApp \\\n -Pmoko.resources.PLATFORM_NAME=\"$PLATFORM_NAME\" \\\n -Pmoko.resources.CONFIGURATION=\"$CONFIGURATION\" \\\n -Pmoko.resources.ARCHS=\"$ARCHS\" \\\n -Pmoko.resources.BUILT_PRODUCTS_DIR=\"$BUILT_PRODUCTS_DIR\" \\\n -Pmoko.resources.CONTENTS_FOLDER_PATH=\"$CONTENTS_FOLDER_PATH\" \n"; + }; F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; diff --git a/iosApp/iosApp/ContentView.swift b/iosApp/iosApp/ContentView.swift index 3cd5c325b1..06019d7fd3 100644 --- a/iosApp/iosApp/ContentView.swift +++ b/iosApp/iosApp/ContentView.swift @@ -1,10 +1,10 @@ import UIKit import SwiftUI -import ComposeApp +import ios struct ComposeView: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIViewController { - MainViewControllerKt.MainViewController() + MainKt.initializeApplication() } func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/image/IosImageLoaderBuilder.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/image/IosImageLoaderBuilder.kt index 938dd0ecf5..04934f8c95 100644 --- a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/image/IosImageLoaderBuilder.kt +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/image/IosImageLoaderBuilder.kt @@ -16,6 +16,7 @@ import com.seiko.imageloader.cache.memory.maxSizePercent import com.seiko.imageloader.component.ComponentRegistryBuilder import com.seiko.imageloader.component.setupDefaultComponents import com.seiko.imageloader.option.OptionsBuilder +import kotlinx.cinterop.ExperimentalForeignApi import okio.Path.Companion.toPath import platform.Foundation.NSCachesDirectory import platform.Foundation.NSFileManager @@ -39,6 +40,7 @@ actual fun DiskCacheBuilder.configure( maxSizeBytes(1024 * 1024 * 150) // 150 MB } +@OptIn(ExperimentalForeignApi::class) private fun getCacheDir(): String { return NSFileManager.defaultManager.URLForDirectory( NSCachesDirectory, diff --git a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/prefs/IosColorExtensions.kt b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/prefs/IosColorExtensions.kt index 197ea6c42a..0d0f2d6434 100644 --- a/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/prefs/IosColorExtensions.kt +++ b/presentation/src/iosMain/kotlin/ca/gosyer/jui/ui/base/prefs/IosColorExtensions.kt @@ -7,6 +7,7 @@ package ca.gosyer.jui.ui.base.prefs import androidx.compose.ui.graphics.Color +import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.alloc import kotlinx.cinterop.memScoped import kotlinx.cinterop.ptr @@ -16,6 +17,7 @@ import platform.UIKit.UIColor fun Color.toUIColor() = UIColor(red = red.toDouble(), green = green.toDouble(), blue = blue.toDouble(), alpha = 1.0) +@OptIn(ExperimentalForeignApi::class) internal actual fun Color.toHsv(): FloatArray = memScoped { val uiColor = toUIColor() diff --git a/ui-core/src/iosMain/kotlin/ca/gosyer/jui/uicore/resources/IosImageResource.kt b/ui-core/src/iosMain/kotlin/ca/gosyer/jui/uicore/resources/IosImageResource.kt index 8d527e82c3..b8e16fbf75 100644 --- a/ui-core/src/iosMain/kotlin/ca/gosyer/jui/uicore/resources/IosImageResource.kt +++ b/ui-core/src/iosMain/kotlin/ca/gosyer/jui/uicore/resources/IosImageResource.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.toComposeImageBitmap import androidx.compose.ui.graphics.vector.rememberVectorPainter import dev.icerock.moko.resources.ImageResource +import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.get import org.jetbrains.skia.ColorAlphaType import org.jetbrains.skia.ColorType @@ -42,6 +43,7 @@ actual fun ImageResource.toPainter(): Painter { // Taken from https://github.com/touchlab/DroidconKotlin/blob/main/shared-ui/src/iosMain/kotlin/co/touchlab/droidcon/ui/util/ToSkiaImage.kt // TODO: Add support for remaining color spaces when the Skia library supports them. +@OptIn(ExperimentalForeignApi::class) private fun UIImage.toSkiaImage(): Image? { val imageRef = CGImageCreateCopyWithColorSpace(this.CGImage, CGColorSpaceCreateDeviceRGB()) ?: return null