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