diff --git a/.idea/xcode.xml b/.idea/xcode.xml new file mode 100644 index 0000000..4eb3242 --- /dev/null +++ b/.idea/xcode.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 9976c03..b45422f 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -129,7 +129,7 @@ dependencies { } compose.resources { - publicResClass = false + publicResClass = true packageOfResClass = "ru.fromchat" generateResClass = auto } diff --git a/composeApp/src/androidMain/kotlin/ru/fromchat/MainActivity.kt b/composeApp/src/androidMain/kotlin/ru/fromchat/MainActivity.kt index dd3140b..66f6b01 100644 --- a/composeApp/src/androidMain/kotlin/ru/fromchat/MainActivity.kt +++ b/composeApp/src/androidMain/kotlin/ru/fromchat/MainActivity.kt @@ -15,3 +15,4 @@ class MainActivity : ComponentActivity() { } } } + diff --git a/composeApp/src/androidMain/kotlin/ru/fromchat/api/AndroidKtorLogger.kt b/composeApp/src/androidMain/kotlin/ru/fromchat/api/AndroidKtorLogger.kt index 0c4aa07..5210d42 100644 --- a/composeApp/src/androidMain/kotlin/ru/fromchat/api/AndroidKtorLogger.kt +++ b/composeApp/src/androidMain/kotlin/ru/fromchat/api/AndroidKtorLogger.kt @@ -14,3 +14,4 @@ object AndroidKtorLogger : Logger { } } + diff --git a/composeApp/src/androidMain/kotlin/ru/fromchat/core/Logger.android.kt b/composeApp/src/androidMain/kotlin/ru/fromchat/core/Logger.android.kt index 9f132a3..cea751a 100644 --- a/composeApp/src/androidMain/kotlin/ru/fromchat/core/Logger.android.kt +++ b/composeApp/src/androidMain/kotlin/ru/fromchat/core/Logger.android.kt @@ -19,3 +19,4 @@ actual object Logger { Log.e(tag, message, throwable) } } + diff --git a/composeApp/src/androidMain/res/values/strings.xml b/composeApp/src/androidMain/res/values/strings.xml index 7808cd4..bd51127 100644 --- a/composeApp/src/androidMain/res/values/strings.xml +++ b/composeApp/src/androidMain/res/values/strings.xml @@ -1,23 +1,3 @@ - FromChat - Регистрация - Создайте новый аккаунт - Зарегистрироваться - Имя пользователя - Пароль - Подтвердите пароль - Войти - Войдите в аккаунт - Добро пожаловать! - Пожалуйста, заполните все поля - Имя пользователя должно быть от 3 до 20 символов - Пароль должен быть от 5 до 50 символов - Пароли не совпадают - Чаты - Контакты - ЛС - Скоро будет... - Общий чат - Вы: Последние сообщение - Введите сообщение + FromChat \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml new file mode 100644 index 0000000..b98b4cb --- /dev/null +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -0,0 +1,44 @@ + + + FromChat + + + Back + Settings + Home + About app + + + Welcome! + Sign In + Sign in to your account + Registration + Create a new account + Register + Username + Password + Confirm Password + Display Name + Display name must be between 1 and 64 characters + + + Please fill in all fields + Username must be between 3 and 20 characters + Password must be between 5 and 50 characters + Passwords do not match + + + Chats + Contacts + Direct Messages + Coming soon... + General Chat + You: Last message + Type a message... + + + Unexpected error + Invalid username or password + Connection error + An unknown error occurred + diff --git a/composeApp/src/commonMain/kotlin/ru/fromchat/api/Utils.kt b/composeApp/src/commonMain/kotlin/ru/fromchat/api/Utils.kt index 48b5ec2..d460e35 100644 --- a/composeApp/src/commonMain/kotlin/ru/fromchat/api/Utils.kt +++ b/composeApp/src/commonMain/kotlin/ru/fromchat/api/Utils.kt @@ -5,6 +5,7 @@ import io.ktor.client.call.body import io.ktor.client.plugins.ClientRequestException suspend inline fun apiRequest( + unexpectedError: String, onError: (String, Exception) -> Unit = { _, _ -> }, onSuccess: (Response) -> Unit = {}, request: suspend () -> Response @@ -17,7 +18,7 @@ suspend inline fun apiRequest( val message = if (e.response.status.value in arrayOf(401, 403)) { e.response.body().detail } else { - "Unexpected error" + unexpectedError } Logger.e("API", "API request failed: $message", e) @@ -26,7 +27,7 @@ suspend inline fun apiRequest( return Result.failure(e) } catch (e: Exception) { Logger.e("API", "API request failed: ${e.message}", e) - onError("Unexpected error", e) + onError(unexpectedError, e) return Result.failure(e) } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/ru/fromchat/api/WebSocketManager.kt b/composeApp/src/commonMain/kotlin/ru/fromchat/api/WebSocketManager.kt index e0916be..e5e2abd 100644 --- a/composeApp/src/commonMain/kotlin/ru/fromchat/api/WebSocketManager.kt +++ b/composeApp/src/commonMain/kotlin/ru/fromchat/api/WebSocketManager.kt @@ -1,6 +1,5 @@ package ru.fromchat.api -import ru.fromchat.core.Logger import io.ktor.client.plugins.websocket.DefaultClientWebSocketSession import io.ktor.client.plugins.websocket.webSocket import io.ktor.client.request.url @@ -10,6 +9,7 @@ import io.ktor.websocket.readText import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.cancel import kotlinx.coroutines.delay @@ -20,6 +20,8 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout import kotlinx.serialization.json.Json import ru.fromchat.WS_API_HOST +import ru.fromchat.core.Logger +import kotlin.concurrent.Volatile import kotlin.coroutines.suspendCoroutine object WebSocketManager { @@ -41,7 +43,8 @@ object WebSocketManager { } // State - @Volatile private var connecting: Boolean = false + @Volatile + private var connecting: Boolean = false @Volatile private var session: DefaultClientWebSocketSession? = null fun connect() { diff --git a/composeApp/src/commonMain/kotlin/ru/fromchat/core/Logger.kt b/composeApp/src/commonMain/kotlin/ru/fromchat/core/Logger.kt index 35a0c07..b0b8220 100644 --- a/composeApp/src/commonMain/kotlin/ru/fromchat/core/Logger.kt +++ b/composeApp/src/commonMain/kotlin/ru/fromchat/core/Logger.kt @@ -6,3 +6,4 @@ expect object Logger { fun w(tag: String, message: String, throwable: Throwable? = null) fun e(tag: String, message: String, throwable: Throwable? = null) } + diff --git a/composeApp/src/commonMain/kotlin/ru/fromchat/ui/App.kt b/composeApp/src/commonMain/kotlin/ru/fromchat/ui/App.kt index fcf7e71..bc3a2a9 100644 --- a/composeApp/src/commonMain/kotlin/ru/fromchat/ui/App.kt +++ b/composeApp/src/commonMain/kotlin/ru/fromchat/ui/App.kt @@ -62,7 +62,10 @@ fun App() { ) { composable("login") { LoginScreen( - onLoginSuccess = { navController.navigate("chat") }, + onLoginSuccess = { + navController.navigate("chat") + navController.popBackStack() + }, onNavigateToRegister = { navController.navigate("register") } ) } diff --git a/composeApp/src/commonMain/kotlin/ru/fromchat/ui/auth/LoginScreen.kt b/composeApp/src/commonMain/kotlin/ru/fromchat/ui/auth/LoginScreen.kt index b0c1b54..41e1955 100644 --- a/composeApp/src/commonMain/kotlin/ru/fromchat/ui/auth/LoginScreen.kt +++ b/composeApp/src/commonMain/kotlin/ru/fromchat/ui/auth/LoginScreen.kt @@ -24,22 +24,32 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.unit.dp +import com.pr0gramm3r101.utils.crypto.deriveAuthSecret import kotlinx.coroutines.launch -import ru.fromchat.R +import org.jetbrains.compose.resources.stringResource +import ru.fromchat.Res import ru.fromchat.api.ApiClient import ru.fromchat.api.LoginRequest import ru.fromchat.api.apiRequest +import ru.fromchat.error_unexpected +import ru.fromchat.fill_all_fields +import ru.fromchat.login +import ru.fromchat.login_d +import ru.fromchat.password +import ru.fromchat.register_button import ru.fromchat.ui.RowHeader -import com.pr0gramm3r101.utils.crypto.deriveAuthSecret +import ru.fromchat.username +import ru.fromchat.welcome @Composable fun LoginScreen( onLoginSuccess: () -> Unit, onNavigateToRegister: () -> Unit ) { + val errorUnexpected = stringResource(Res.string.error_unexpected) + Scaffold(contentWindowInsets = WindowInsets.safeDrawing) { innerPadding -> var username by remember { mutableStateOf("") } var password by remember { mutableStateOf("") } @@ -63,8 +73,8 @@ fun LoginScreen( ) { RowHeader( icon = Icons.AutoMirrored.Filled.Login, - title = stringResource(R.string.welcome), - subtitle = stringResource(R.string.login_d) + title = stringResource(Res.string.welcome), + subtitle = stringResource(Res.string.login_d) ) if (alert != null) { @@ -74,25 +84,25 @@ fun LoginScreen( OutlinedTextField( value = username, onValueChange = { username = it }, - label = { Text(stringResource(R.string.username)) }, + label = { Text(stringResource(Res.string.username)) }, singleLine = true ) OutlinedTextField( value = password, onValueChange = { password = it }, - label = { Text(stringResource(R.string.password)) }, + label = { Text(stringResource(Res.string.password)) }, singleLine = true, visualTransformation = PasswordVisualTransformation() ) FlowRow(horizontalArrangement = Arrangement.spacedBy(10.dp)) { - val alert_error_filling = stringResource(R.string.fill_all_fields) + val alertErrorFilling = stringResource(Res.string.fill_all_fields) Button( onClick = { if (username.isBlank() || password.isBlank()) { - alert = alert_error_filling + alert = alertErrorFilling return@Button } @@ -101,6 +111,7 @@ fun LoginScreen( val derived = deriveAuthSecret(username.trim(), password.trim()) apiRequest( + unexpectedError = errorUnexpected, onError = { message, _ -> alert = message }, @@ -111,11 +122,11 @@ fun LoginScreen( } } ) { - Text(stringResource(R.string.login)) + Text(stringResource(Res.string.login)) } Button(onClick = onNavigateToRegister) { - Text(stringResource(R.string.register_button)) + Text(stringResource(Res.string.register_button)) } } } diff --git a/composeApp/src/commonMain/kotlin/ru/fromchat/ui/auth/RegisterScreen.kt b/composeApp/src/commonMain/kotlin/ru/fromchat/ui/auth/RegisterScreen.kt index 4d33f9e..35c16c5 100644 --- a/composeApp/src/commonMain/kotlin/ru/fromchat/ui/auth/RegisterScreen.kt +++ b/composeApp/src/commonMain/kotlin/ru/fromchat/ui/auth/RegisterScreen.kt @@ -27,22 +27,38 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.unit.dp +import com.pr0gramm3r101.utils.crypto.deriveAuthSecret import kotlinx.coroutines.launch -import ru.fromchat.R +import org.jetbrains.compose.resources.stringResource +import ru.fromchat.Res import ru.fromchat.api.ApiClient import ru.fromchat.api.RegisterRequest import ru.fromchat.api.apiRequest +import ru.fromchat.back +import ru.fromchat.confirm_password +import ru.fromchat.display_name +import ru.fromchat.display_name_error +import ru.fromchat.error_unexpected +import ru.fromchat.fill_all_fields +import ru.fromchat.password +import ru.fromchat.password_length_error +import ru.fromchat.passwords_dont_match +import ru.fromchat.register +import ru.fromchat.register_button +import ru.fromchat.register_d import ru.fromchat.ui.LocalNavController import ru.fromchat.ui.RowHeader -import com.pr0gramm3r101.utils.crypto.deriveAuthSecret +import ru.fromchat.username +import ru.fromchat.username_length_error @Composable fun RegisterScreen( onRegistered: () -> Unit ) { + val errorUnexpected = stringResource(Res.string.error_unexpected) + Scaffold(contentWindowInsets = WindowInsets.safeDrawing) { innerPadding -> var username by remember { mutableStateOf("") } var displayName by remember { mutableStateOf("") } @@ -69,8 +85,8 @@ fun RegisterScreen( ) { RowHeader( icon = Icons.Filled.PersonAdd, - title = stringResource(R.string.register), - subtitle = stringResource(R.string.register_d) + title = stringResource(Res.string.register), + subtitle = stringResource(Res.string.register_d) ) if (alert != null) { @@ -80,21 +96,21 @@ fun RegisterScreen( OutlinedTextField( value = username, onValueChange = { username = it }, - label = { Text(stringResource(R.string.username)) }, + label = { Text(stringResource(Res.string.username)) }, singleLine = true ) OutlinedTextField( value = displayName, onValueChange = { displayName = it }, - label = { Text("Display Name") }, + label = { Text(stringResource(Res.string.display_name)) }, singleLine = true ) OutlinedTextField( value = password, onValueChange = { password = it }, - label = { Text(stringResource(R.string.password)) }, + label = { Text(stringResource(Res.string.password)) }, singleLine = true, visualTransformation = PasswordVisualTransformation() ) @@ -102,23 +118,24 @@ fun RegisterScreen( OutlinedTextField( value = confirmPassword, onValueChange = { confirmPassword = it }, - label = { Text(stringResource(R.string.confirm_password)) }, + label = { Text(stringResource(Res.string.confirm_password)) }, singleLine = true, visualTransformation = PasswordVisualTransformation() ) FlowRow(horizontalArrangement = Arrangement.spacedBy(10.dp)) { - val alert_error_filling = stringResource(R.string.fill_all_fields) - val alert_error_password_little = stringResource(R.string.password_length_error) - val alert_error_name_little = stringResource(R.string.username_length_error) - val alert_error_password_confrim = stringResource(R.string.passwords_dont_match) + val alertErrorFilling = stringResource(Res.string.fill_all_fields) + val alertErrorPasswordLittle = stringResource(Res.string.password_length_error) + val alertErrorNameLittle = stringResource(Res.string.username_length_error) + val alertErrorPasswordConfrim = stringResource(Res.string.passwords_dont_match) + val alertErrorDisplayName = stringResource(Res.string.display_name_error) IconButton( onClick = { navController.navigateUp() } ) { Icon( imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = "Back" + contentDescription = stringResource(Res.string.back) ) } @@ -126,23 +143,23 @@ fun RegisterScreen( onClick = { // Checks if (username.isBlank() || displayName.isBlank() || password.isBlank() || confirmPassword.isBlank()) { - alert = alert_error_filling + alert = alertErrorFilling return@Button } if (password != confirmPassword) { - alert = alert_error_password_confrim + alert = alertErrorPasswordConfrim return@Button } if (username.length !in 3..20) { - alert = alert_error_name_little + alert = alertErrorNameLittle return@Button } if (displayName.isBlank() || displayName.length > 64) { - alert = "Display name must be between 1 and 64 characters" + alert = alertErrorDisplayName return@Button } if (password.length !in 5..50) { - alert = alert_error_password_little + alert = alertErrorPasswordLittle return@Button } @@ -151,6 +168,7 @@ fun RegisterScreen( val derived = deriveAuthSecret(username.trim(), password) apiRequest( + unexpectedError = errorUnexpected, onError = { message, _ -> alert = message }, @@ -168,7 +186,7 @@ fun RegisterScreen( } } ) { - Text(stringResource(R.string.register_button)) + Text(stringResource(Res.string.register_button)) } } } diff --git a/composeApp/src/commonMain/kotlin/ru/fromchat/ui/main/ChatsTab.kt b/composeApp/src/commonMain/kotlin/ru/fromchat/ui/main/ChatsTab.kt index 29d6a17..00e8ce6 100644 --- a/composeApp/src/commonMain/kotlin/ru/fromchat/ui/main/ChatsTab.kt +++ b/composeApp/src/commonMain/kotlin/ru/fromchat/ui/main/ChatsTab.kt @@ -11,9 +11,12 @@ import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow -import ru.fromchat.R +import org.jetbrains.compose.resources.stringResource +import ru.fromchat.Res +import ru.fromchat.chat_last_mesaage +import ru.fromchat.chats +import ru.fromchat.public_chat import ru.fromchat.ui.LocalNavController @OptIn(ExperimentalMaterial3Api::class) @@ -28,7 +31,7 @@ fun ChatsTab() { MediumTopAppBar( title = { Text( - text = stringResource(R.string.chats), + text = stringResource(Res.string.chats), maxLines = 1, overflow = TextOverflow.Ellipsis ) @@ -40,8 +43,8 @@ fun ChatsTab() { LazyColumn(contentPadding = innerPadding) { item { ListItem( - headlineContent = { Text(stringResource(R.string.public_chat)) }, - supportingContent = { Text(stringResource(R.string.chat_last_mesaage)) }, + headlineContent = { Text(stringResource(Res.string.public_chat)) }, + supportingContent = { Text(stringResource(Res.string.chat_last_mesaage)) }, modifier = Modifier.clickable { navController.navigate("chats/publicChat") } diff --git a/composeApp/src/commonMain/kotlin/ru/fromchat/ui/main/MainScreen.kt b/composeApp/src/commonMain/kotlin/ru/fromchat/ui/main/MainScreen.kt index e80b1e8..a519426 100644 --- a/composeApp/src/commonMain/kotlin/ru/fromchat/ui/main/MainScreen.kt +++ b/composeApp/src/commonMain/kotlin/ru/fromchat/ui/main/MainScreen.kt @@ -22,10 +22,15 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import ru.fromchat.R +import org.jetbrains.compose.resources.stringResource +import ru.fromchat.Res +import ru.fromchat.chats +import ru.fromchat.coming_soon +import ru.fromchat.contacts +import ru.fromchat.dms import ru.fromchat.utils.exclude +@Suppress("AssignedValueIsNeverRead") @Composable fun MainScreen() { var selectedTab by remember { mutableStateOf("chats") } @@ -36,19 +41,19 @@ fun MainScreen() { NavigationBarItem( selected = selectedTab == "chats", onClick = { selectedTab = "chats" }, - label = { Text(stringResource(R.string.chats)) }, + label = { Text(stringResource(Res.string.chats)) }, icon = { Icon(Icons.AutoMirrored.Filled.Chat, contentDescription = null) } ) NavigationBarItem( selected = selectedTab == "contacts", onClick = { selectedTab = "contacts" }, - label = { Text(stringResource(R.string.contacts)) }, + label = { Text(stringResource(Res.string.contacts)) }, icon = { Icon(Icons.Filled.Contacts, contentDescription = null) } ) NavigationBarItem( selected = selectedTab == "dms", onClick = { selectedTab = "dms" }, - label = { Text(stringResource(R.string.dms)) }, + label = { Text(stringResource(Res.string.dms)) }, icon = { Icon(Icons.Filled.Mail, contentDescription = null) } ) } @@ -64,10 +69,10 @@ fun MainScreen() { when (selectedTab) { "chats" -> ChatsTab() "contacts" -> { - Text(stringResource(R.string.coming_soon)) + Text(stringResource(Res.string.coming_soon)) } "dms" -> { - Text(stringResource(R.string.coming_soon)) + Text(stringResource(Res.string.coming_soon)) } } } diff --git a/composeApp/src/commonMain/kotlin/ru/fromchat/utils/Generic.kt b/composeApp/src/commonMain/kotlin/ru/fromchat/utils/Generic.kt index 6a0c6fc..c2d0481 100644 --- a/composeApp/src/commonMain/kotlin/ru/fromchat/utils/Generic.kt +++ b/composeApp/src/commonMain/kotlin/ru/fromchat/utils/Generic.kt @@ -5,4 +5,5 @@ import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.exclude import androidx.compose.foundation.layout.only +@Suppress("NOTHING_TO_INLINE") inline fun WindowInsets.exclude(sides: WindowInsetsSides) = exclude(this.only(sides)) \ No newline at end of file diff --git a/composeApp/src/iosMain/kotlin/ru/fromchat/MainViewController.kt b/composeApp/src/iosMain/kotlin/ru/fromchat/MainViewController.kt index ee61d3f..ec3d9a8 100644 --- a/composeApp/src/iosMain/kotlin/ru/fromchat/MainViewController.kt +++ b/composeApp/src/iosMain/kotlin/ru/fromchat/MainViewController.kt @@ -4,3 +4,4 @@ import ru.fromchat.ui.App import androidx.compose.ui.window.ComposeUIViewController fun MainViewController() = ComposeUIViewController { App() } + diff --git a/composeApp/src/iosMain/kotlin/ru/fromchat/core/Logger.ios.kt b/composeApp/src/iosMain/kotlin/ru/fromchat/core/Logger.ios.kt index 8386ff3..7ec7641 100644 --- a/composeApp/src/iosMain/kotlin/ru/fromchat/core/Logger.ios.kt +++ b/composeApp/src/iosMain/kotlin/ru/fromchat/core/Logger.ios.kt @@ -19,3 +19,4 @@ actual object Logger { NSLog("ERROR: [%s] %s %s", tag, message, throwable?.message ?: "") } } + diff --git a/iosApp/Configuration/Config.xcconfig b/iosApp/Configuration/Config.xcconfig new file mode 100644 index 0000000..363e591 --- /dev/null +++ b/iosApp/Configuration/Config.xcconfig @@ -0,0 +1,3 @@ +TEAM_ID=82MWK23YNM +BUNDLE_ID=ru.fromchat.app +APP_NAME=FromChat \ No newline at end of file diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj new file mode 100644 index 0000000..b70fb0f --- /dev/null +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -0,0 +1,407 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; }; + 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; }; + 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; }; + 7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 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 /* FromChat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FromChat.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 = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B92378962B6B1156000C7307 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 058557D7273AAEEB004C7B11 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 42799AB246E5F90AF97AA0EF /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + 7555FF72242A565900829871 = { + isa = PBXGroup; + children = ( + AB1DB47929225F7C00F7AF9C /* Configuration */, + 7555FF7D242A565900829871 /* iosApp */, + 7555FF7C242A565900829871 /* Products */, + 42799AB246E5F90AF97AA0EF /* Frameworks */, + ); + sourceTree = ""; + }; + 7555FF7C242A565900829871 /* Products */ = { + isa = PBXGroup; + children = ( + 7555FF7B242A565900829871 /* FromChat.app */, + ); + name = Products; + sourceTree = ""; + }; + 7555FF7D242A565900829871 /* iosApp */ = { + isa = PBXGroup; + children = ( + 058557BA273AAA24004C7B11 /* Assets.xcassets */, + 7555FF82242A565900829871 /* ContentView.swift */, + 7555FF8C242A565B00829871 /* Info.plist */, + 2152FB032600AC8F00CF470E /* iOSApp.swift */, + 058557D7273AAEEB004C7B11 /* Preview Content */, + ); + path = iosApp; + sourceTree = ""; + }; + AB1DB47929225F7C00F7AF9C /* Configuration */ = { + isa = PBXGroup; + children = ( + AB3632DC29227652001CCB65 /* Config.xcconfig */, + ); + path = Configuration; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 7555FF7A242A565900829871 /* iosApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */; + buildPhases = ( + F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */, + 7555FF77242A565900829871 /* Sources */, + B92378962B6B1156000C7307 /* Frameworks */, + 7555FF79242A565900829871 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = iosApp; + packageProductDependencies = ( + ); + productName = iosApp; + productReference = 7555FF7B242A565900829871 /* FromChat.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 7555FF73242A565900829871 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 1130; + LastUpgradeCheck = 2610; + ORGANIZATIONNAME = orgName; + TargetAttributes = { + 7555FF7A242A565900829871 = { + CreatedOnToolsVersion = 11.3.1; + }; + }; + }; + buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 7555FF72242A565900829871; + packageReferences = ( + ); + productRefGroup = 7555FF7C242A565900829871 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 7555FF7A242A565900829871 /* iosApp */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 7555FF79242A565900829871 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */, + 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Compile Kotlin Framework"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \\\"YES\\\"\"\n exit 0\nfi\ncd \"$SRCROOT/..\"\n./gradlew :composeApp:embedAndSignAppleFrameworkForXcode\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 7555FF77242A565900829871 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */, + 7555FF83242A565900829871 /* ContentView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 7555FFA3242A565B00829871 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 7555FFA4242A565B00829871 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 7555FFA6242A565B00829871 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; + DEVELOPMENT_TEAM = 82MWK23YNM; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", + ); + INFOPLIST_FILE = iosApp/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = FromChat; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; + PRODUCT_NAME = "${APP_NAME}"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 7555FFA7242A565B00829871 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; + DEVELOPMENT_TEAM = 82MWK23YNM; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", + ); + INFOPLIST_FILE = iosApp/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = FromChat; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; + PRODUCT_NAME = "${APP_NAME}"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7555FFA3242A565B00829871 /* Debug */, + 7555FFA4242A565B00829871 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7555FFA6242A565B00829871 /* Debug */, + 7555FFA7242A565B00829871 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 7555FF73242A565900829871 /* Project object */; +} diff --git a/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/iosApp/iosApp.xcodeproj/project.xcworkspace/xcuserdata/denis0001-dev.xcuserdatad/UserInterfaceState.xcuserstate b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcuserdata/denis0001-dev.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..f73e8b4 Binary files /dev/null and b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcuserdata/denis0001-dev.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/iosApp/iosApp.xcodeproj/project.xcworkspace/xcuserdata/denis0001-dev.xcuserdatad/xcschemes/xcschememanagement.plist b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcuserdata/denis0001-dev.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..ee3458d --- /dev/null +++ b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcuserdata/denis0001-dev.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,5 @@ + + + + + diff --git a/iosApp/iosApp.xcodeproj/xcuserdata/denis0001-dev.xcuserdatad/xcschemes/iOS.xcscheme b/iosApp/iosApp.xcodeproj/xcuserdata/denis0001-dev.xcuserdatad/xcschemes/iOS.xcscheme new file mode 100644 index 0000000..0825f85 --- /dev/null +++ b/iosApp/iosApp.xcodeproj/xcuserdata/denis0001-dev.xcuserdatad/xcschemes/iOS.xcscheme @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iosApp/iosApp.xcodeproj/xcuserdata/denis0001-dev.xcuserdatad/xcschemes/xcschememanagement.plist b/iosApp/iosApp.xcodeproj/xcuserdata/denis0001-dev.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..2732651 --- /dev/null +++ b/iosApp/iosApp.xcodeproj/xcuserdata/denis0001-dev.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + iOS.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + 7555FF7A242A565900829871 + + primary + + + + + diff --git a/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json b/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..ee7e3ca --- /dev/null +++ b/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..ebd68fc --- /dev/null +++ b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images" : [ + { + "filename" : "logo_square.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/logo_square.png b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/logo_square.png new file mode 100644 index 0000000..8551b6d Binary files /dev/null and b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/logo_square.png differ diff --git a/iosApp/iosApp/Assets.xcassets/Contents.json b/iosApp/iosApp/Assets.xcassets/Contents.json new file mode 100644 index 0000000..4aa7c53 --- /dev/null +++ b/iosApp/iosApp/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/iosApp/iosApp/ContentView.swift b/iosApp/iosApp/ContentView.swift new file mode 100644 index 0000000..ec34983 --- /dev/null +++ b/iosApp/iosApp/ContentView.swift @@ -0,0 +1,18 @@ +import UIKit +import SwiftUI +import ComposeApp + +struct ComposeView: UIViewControllerRepresentable { + func makeUIViewController(context: Context) -> UIViewController { + MainViewControllerKt.MainViewController() + } + + func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} +} + +struct ContentView: View { + var body: some View { + ComposeView() + .ignoresSafeArea(.all) // Compose has own keyboard handler + } +} diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist new file mode 100644 index 0000000..aaf5e18 --- /dev/null +++ b/iosApp/iosApp/Info.plist @@ -0,0 +1,50 @@ + + + + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UILaunchScreen + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json b/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..4aa7c53 --- /dev/null +++ b/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/iosApp/iosApp/iOSApp.swift b/iosApp/iosApp/iOSApp.swift new file mode 100644 index 0000000..927e0b9 --- /dev/null +++ b/iosApp/iosApp/iOSApp.swift @@ -0,0 +1,10 @@ +import SwiftUI + +@main +struct iOSApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/utils/src/iosMain/kotlin/com/pr0gramm3r101/utils/crypto/Base64.ios.kt b/utils/src/iosMain/kotlin/com/pr0gramm3r101/utils/crypto/Base64.ios.kt index 4ed01bd..c70125a 100644 --- a/utils/src/iosMain/kotlin/com/pr0gramm3r101/utils/crypto/Base64.ios.kt +++ b/utils/src/iosMain/kotlin/com/pr0gramm3r101/utils/crypto/Base64.ios.kt @@ -1,23 +1,33 @@ package com.pr0gramm3r101.utils.crypto +import kotlinx.cinterop.BetaInteropApi import kotlinx.cinterop.ByteVar +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.cinterop.addressOf +import kotlinx.cinterop.get +import kotlinx.cinterop.reinterpret import kotlinx.cinterop.usePinned import platform.Foundation.NSData +import platform.Foundation.base64EncodedStringWithOptions +import platform.Foundation.create actual object Base64 { + @OptIn(ExperimentalForeignApi::class, BetaInteropApi::class) actual fun encode(bytes: ByteArray): String { return bytes.usePinned { pinned -> val nsData = NSData.create(bytes = pinned.addressOf(0), length = bytes.size.toULong()) nsData.base64EncodedStringWithOptions(0u) } } - + + @OptIn(BetaInteropApi::class, ExperimentalForeignApi::class) actual fun decode(base64: String): ByteArray { val nsData = NSData.create(base64EncodedString = base64, options = 0u) ?: return ByteArray(0) val length = nsData.length.toInt() - val bytes = nsData.bytes ?: return ByteArray(0) + val bytesPtr = nsData.bytes ?: return ByteArray(0) + val bytePtr = bytesPtr.reinterpret() return ByteArray(length) { index -> - bytes.reinterpret()[index] + bytePtr[index] } } -} +} \ No newline at end of file diff --git a/utils/src/iosMain/kotlin/com/pr0gramm3r101/utils/crypto/Hmac.ios.kt b/utils/src/iosMain/kotlin/com/pr0gramm3r101/utils/crypto/Hmac.ios.kt index 0b9d178..5e69b7d 100644 --- a/utils/src/iosMain/kotlin/com/pr0gramm3r101/utils/crypto/Hmac.ios.kt +++ b/utils/src/iosMain/kotlin/com/pr0gramm3r101/utils/crypto/Hmac.ios.kt @@ -1,11 +1,15 @@ package com.pr0gramm3r101.utils.crypto +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.cinterop.addressOf +import kotlinx.cinterop.usePinned import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import platform.CommonCrypto.CCHmac -import platform.CommonCrypto.kCCHmacAlgSHA256 +import platform.CoreCrypto.CCHmac +import platform.CoreCrypto.kCCHmacAlgSHA256 actual object Hmac { + @OptIn(ExperimentalForeignApi::class) actual suspend fun hmacSha256(key: ByteArray, data: ByteArray): ByteArray = withContext(Dispatchers.Default) { val result = ByteArray(32) diff --git a/utils/src/iosMain/kotlin/com/pr0gramm3r101/utils/settings/AsyncSettings.ios.kt b/utils/src/iosMain/kotlin/com/pr0gramm3r101/utils/settings/AsyncSettings.ios.kt index c1fd475..473922a 100644 --- a/utils/src/iosMain/kotlin/com/pr0gramm3r101/utils/settings/AsyncSettings.ios.kt +++ b/utils/src/iosMain/kotlin/com/pr0gramm3r101/utils/settings/AsyncSettings.ios.kt @@ -3,11 +3,12 @@ package com.pr0gramm3r101.utils.settings import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import platform.Foundation.NSUserDefaults +import platform.Foundation.NSBundle class IosAsyncSettings : AsyncSettings { private val defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults - override suspend fun putString(key: String, value: String) = withContext(Dispatchers.Default) { + override suspend fun putString(key: String, value: String): Unit = withContext(Dispatchers.Default) { defaults.setObject(value, key) defaults.synchronize() } @@ -16,7 +17,7 @@ class IosAsyncSettings : AsyncSettings { defaults.stringForKey(key) ?: default } - override suspend fun putInt(key: String, value: Int) = withContext(Dispatchers.Default) { + override suspend fun putInt(key: String, value: Int): Unit = withContext(Dispatchers.Default) { defaults.setInteger(value.toLong(), key) defaults.synchronize() } @@ -26,7 +27,7 @@ class IosAsyncSettings : AsyncSettings { if (value != null) value.toInt() else default } - override suspend fun putLong(key: String, value: Long) = withContext(Dispatchers.Default) { + override suspend fun putLong(key: String, value: Long): Unit = withContext(Dispatchers.Default) { defaults.setObject(value, key) defaults.synchronize() } @@ -35,7 +36,7 @@ class IosAsyncSettings : AsyncSettings { (defaults.objectForKey(key) as? platform.Foundation.NSNumber)?.longValue ?: default } - override suspend fun putFloat(key: String, value: Float) = withContext(Dispatchers.Default) { + override suspend fun putFloat(key: String, value: Float): Unit = withContext(Dispatchers.Default) { defaults.setFloat(value, key) defaults.synchronize() } @@ -45,7 +46,7 @@ class IosAsyncSettings : AsyncSettings { if (value != null) value.toFloat() else default } - override suspend fun putBoolean(key: String, value: Boolean) = withContext(Dispatchers.Default) { + override suspend fun putBoolean(key: String, value: Boolean): Unit = withContext(Dispatchers.Default) { defaults.setBool(value, key) defaults.synchronize() } @@ -58,7 +59,7 @@ class IosAsyncSettings : AsyncSettings { } } - override suspend fun putStringSet(key: String, value: Set) = withContext(Dispatchers.Default) { + override suspend fun putStringSet(key: String, value: Set): Unit = withContext(Dispatchers.Default) { defaults.setObject(value.toList(), key) defaults.synchronize() } @@ -68,7 +69,7 @@ class IosAsyncSettings : AsyncSettings { list.filterIsInstance().toSet() } - override suspend fun putStringList(key: String, value: List) = withContext(Dispatchers.Default) { + override suspend fun putStringList(key: String, value: List): Unit = withContext(Dispatchers.Default) { defaults.setObject(value, key) defaults.synchronize() } @@ -78,7 +79,7 @@ class IosAsyncSettings : AsyncSettings { list.filterIsInstance() } - override suspend fun remove(key: String) = withContext(Dispatchers.Default) { + override suspend fun remove(key: String): Unit = withContext(Dispatchers.Default) { defaults.removeObjectForKey(key) defaults.synchronize() } @@ -87,8 +88,9 @@ class IosAsyncSettings : AsyncSettings { defaults.objectForKey(key) != null } - override suspend fun clear() = withContext(Dispatchers.Default) { - val domain = defaults.persistentDomainForName(defaults.bundleIdentifier() ?: "") + override suspend fun clear(): Unit = withContext(Dispatchers.Default) { + val bundleId = NSBundle.mainBundle.bundleIdentifier ?: "" + val domain = defaults.persistentDomainForName(bundleId) domain?.keys?.forEach { key -> defaults.removeObjectForKey(key as String) } @@ -96,4 +98,4 @@ class IosAsyncSettings : AsyncSettings { } } -actual val asyncSettings: AsyncSettings = IosAsyncSettings() +actual val asyncSettings: AsyncSettings = IosAsyncSettings() \ No newline at end of file