Initial commit

This commit is contained in:
cheykrym 2025-06-10 05:23:59 +03:00
parent 03590ca86f
commit f448fe77f4
18 changed files with 786 additions and 5 deletions

8
Shared/Models/User.swift Normal file
View File

@ -0,0 +1,8 @@
//
// User.swift
// volnahub (iOS)
//
// Created by cheykrym on 09/06/2025.
//
import Foundation

View File

@ -0,0 +1,29 @@
//
// AuthService.swift
// VolnahubApp
//
// Created by cheykrym on 09/06/2025.
//
import Foundation
class AuthService {
func autoLogin(completion: @escaping (Bool) -> Void) {
// Симуляция проверки токена
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
let success = false // Пока всегда неуспешно, для теста
completion(success)
}
}
func login(username: String, password: String, completion: @escaping (Bool, String?) -> Void) {
// Симуляция запроса
DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
if username == "test" && password == "123123" {
completion(true, nil)
} else {
completion(false, "Неверные учетные данные.")
}
}
}
}

View File

@ -0,0 +1,7 @@
/*
Localizable.strings
volnahub
Created by cheykrym on 10/06/2025.
*/

View File

@ -0,0 +1,14 @@
/*
Localizable.strings
volnahub
Created by cheykrym on 10/06/2025.
*/
"loading_placeholder" = "Загрузка...";
"LoginView_change_language" = "Язык";
"LoginView_title" = "Вход";
"LoginView_login" = "Логин";
"LoginView_password" = "Пароль";
"LoginView_button_login" = "Войти";
"LoginView_button_register" = "Регистрация";

View File

@ -0,0 +1,113 @@
import Foundation
import Security
//let username = "user1"
// Сохраняем токены
//KeychainService.shared.save("access_token_value", forKey: "access_token", service: username)
//KeychainService.shared.save("refresh_token_value", forKey: "refresh_token", service: username)
// Получаем токены
//let accessToken = KeychainService.shared.get(forKey: "access_token", service: username)
//let refreshToken = KeychainService.shared.get(forKey: "refresh_token", service: username)
// получение всех пользователей
//let users = KeychainService.shared.getAllServices()
//print("Все пользователи: \(users)")
// Удаление всех пользователей
//KeychainService.shared.deleteAll()
// удаление по одному
//KeychainService.shared.delete(forKey: "access_token", service: username)
//KeychainService.shared.delete(forKey: "refresh_token", service: username)
class KeychainService {
static let shared = KeychainService()
private init() {}
func save(_ value: String, forKey key: String, service: String) {
guard let data = value.data(using: .utf8) else { return }
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service, // ключ группировки
kSecAttrAccount as String: key,
kSecValueData as String: data
]
SecItemDelete(query as CFDictionary)
let status = SecItemAdd(query as CFDictionary, nil)
if status != errSecSuccess {
print("Error saving to Keychain: \(status)")
}
}
func get(forKey key: String, service: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: key,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var dataTypeRef: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &dataTypeRef)
if status == errSecSuccess,
let data = dataTypeRef as? Data,
let value = String(data: data, encoding: .utf8) {
return value
}
return nil
}
func getAllServices() -> [String] {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecReturnAttributes as String: true,
kSecMatchLimit as String: kSecMatchLimitAll
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
guard status == errSecSuccess, let items = result as? [[String: Any]] else {
return []
}
// Собираем все уникальные service (username)
var services = Set<String>()
for item in items {
if let service = item[kSecAttrService as String] as? String {
services.insert(service)
}
}
return Array(services)
}
func delete(forKey key: String, service: String) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: key
]
SecItemDelete(query as CFDictionary)
}
/// Удалить все записи Keychain, сохранённые этим приложением
func deleteAll() {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword
]
let status = SecItemDelete(query as CFDictionary)
if status != errSecSuccess && status != errSecItemNotFound {
print("Error deleting all Keychain items: \(status)")
}
}
}

View File

@ -0,0 +1,59 @@
//
// LoginViewModel.swift
// VolnahubApp
//
// Created by cheykrym on 09/06/2025.
//
import Foundation
import Combine
class LoginViewModel: ObservableObject {
@Published var username: String = ""
@Published var password: String = ""
@Published var isLoading: Bool = true // сразу true, чтобы вызывался автологин
@Published var showError: Bool = false
@Published var errorMessage: String = ""
@Published var isLoggedIn: Bool = false
private let authService = AuthService()
init() {
autoLogin()
}
func autoLogin() {
authService.autoLogin { [weak self] success in
DispatchQueue.main.async {
self?.isLoading = false
if success {
self?.isLoggedIn = true
}
}
}
}
func login() {
isLoading = true
showError = false
authService.login(username: username, password: password) { [weak self] success, error in
DispatchQueue.main.async {
self?.isLoading = false
if success {
self?.isLoggedIn = true
} else {
self?.errorMessage = error ?? "Неизвестная ошибка"
self?.showError = true
}
}
}
}
func logout() {
username = ""
password = ""
isLoggedIn = false
}
}

View File

@ -0,0 +1,24 @@
//
// ChatsTab.swift
// VolnahubApp
//
// Created by cheykrym on 09/06/2025.
//
import SwiftUI
struct ChatsTab: View {
var body: some View {
NavigationView {
VStack {
Text("Чаты")
.font(.largeTitle)
.bold()
.padding()
Spacer()
}
.navigationTitle("Чаты")
}
}
}

View File

@ -0,0 +1,24 @@
//
// ContactsTab.swift
// VolnahubApp
//
// Created by cheykrym on 09/06/2025.
//
import SwiftUI
struct ContactsTab: View {
var body: some View {
NavigationView {
VStack {
Text("Контакты")
.font(.largeTitle)
.bold()
.padding()
Spacer()
}
.navigationTitle("Контакты")
}
}
}

View File

@ -0,0 +1,153 @@
//
// LoginView.swift
// VolnahubApp
//
// Created by cheykrym on 09/06/2025.
//
import SwiftUI
struct LoginView: View {
@ObservedObject var viewModel: LoginViewModel
@AppStorage("isDarkMode") private var isDarkMode: Bool = true
@State private var isShowingRegistration = false
private var isUsernameValid: Bool {
let pattern = "^[A-Za-z0-9_]{3,32}$"
return viewModel.username.range(of: pattern, options: .regularExpression) != nil
}
private var isPasswordValid: Bool {
return viewModel.password.count >= 6 && viewModel.password.count <= 32
}
var body: some View {
VStack {
HStack {
Button(action: openLanguageSettings) {
Text("🌍 " + NSLocalizedString("LoginView_change_language", comment: ""))
.padding()
}
Spacer()
Button(action: toggleTheme) {
Image(systemName: isDarkMode ? "moon.fill" : "sun.max.fill")
.padding()
}
}
Spacer()
// Text(NSLocalizedString("LoginView_title", comment: ""))
// .font(.largeTitle)
// .bold()
TextField(NSLocalizedString("LoginView_login", comment: ""), text: $viewModel.username)
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
.autocapitalization(.none)
.disableAutocorrection(true)
.onChange(of: viewModel.username) { newValue in
if newValue.count > 32 {
viewModel.username = String(newValue.prefix(32))
}
}
// Показываем ошибку для логина
if !isUsernameValid && !viewModel.username.isEmpty {
Text(NSLocalizedString("LoginView_error_username_invalid", comment: "Неверный логин"))
.foregroundColor(.red)
.font(.caption)
}
// Показываем поле пароля (даже если оно невалидное) только если логин корректен
if isUsernameValid {
SecureField(NSLocalizedString("LoginView_password", comment: ""), text: $viewModel.password)
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
.autocapitalization(.none)
.onChange(of: viewModel.password) { newValue in
if newValue.count > 32 {
viewModel.password = String(newValue.prefix(32))
}
}
// Показываем ошибку для пароля
if !isPasswordValid && !viewModel.password.isEmpty {
Text(NSLocalizedString("LoginView_error_password_invalid", comment: "Неверный пароль"))
.foregroundColor(.red)
.font(.caption)
}
}
if isUsernameValid && isPasswordValid {
Button(action: {
viewModel.login()
}) {
if viewModel.isLoading {
ProgressView()
.progressViewStyle(CircularProgressViewStyle())
.padding()
.frame(maxWidth: .infinity)
.background(Color.gray.opacity(0.6))
.cornerRadius(8)
} else {
Text(NSLocalizedString("LoginView_button_login", comment: ""))
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(Color.blue)
.cornerRadius(8)
}
}
.disabled(viewModel.isLoading)
}
Spacer()
// Кнопка регистрации
Button(action: {
isShowingRegistration = true
}) {
Text(NSLocalizedString("LoginView_button_register", comment: "Регистрация"))
.foregroundColor(.blue)
}
.padding(.top, 10)
.sheet(isPresented: $isShowingRegistration) {
RegistrationView()
}
}
.padding()
.alert(isPresented: $viewModel.showError) {
Alert(
title: Text(NSLocalizedString("LoginView_error", comment: "")),
message: Text(viewModel.errorMessage),
dismissButton: .default(Text("ОК"))
)
}
}
private func toggleTheme() {
isDarkMode.toggle()
}
private func openLanguageSettings() {
guard let url = URL(string: UIApplication.openSettingsURLString) else { return }
UIApplication.shared.open(url)
}
}
struct LoginView_Previews: PreviewProvider {
static var previews: some View {
let viewModel = LoginViewModel()
viewModel.isLoading = false // чтобы убрать спиннер
return LoginView(viewModel: viewModel)
}
}

View File

@ -0,0 +1,38 @@
//
// MainView.swift
// VolnahubApp
//
// Created by cheykrym on 09/06/2025.
//
import SwiftUI
struct MainView: View {
@ObservedObject var viewModel: LoginViewModel
@State private var selectedTab: Int = 1 // Начинаем с Чатов (индекс 1)
var body: some View {
TabView(selection: $selectedTab) {
ContactsTab()
.tabItem {
Image(systemName: "person.2.fill")
Text("Контакты")
}
.tag(0)
ChatsTab()
.tabItem {
Image(systemName: "bubble.left.and.bubble.right.fill")
Text("Чаты")
}
.tag(1)
SettingsTab()
.tabItem {
Image(systemName: "gearshape.fill")
Text("Настройки")
}
.tag(2)
}
}
}

View File

@ -0,0 +1,134 @@
//
// RegistrationView.swift
// VolnahubApp
//
// Created by cheykrym on 09/06/2025.
//
import SwiftUI
struct RegistrationView: View {
@Environment(\.presentationMode) private var presentationMode
@State private var fullName: String = ""
@State private var username: String = ""
@State private var password: String = ""
@State private var confirmPassword: String = ""
@State private var inviteCode: String = ""
@AppStorage("isDarkMode") private var isDarkMode: Bool = true
private var isUsernameValid: Bool {
let pattern = "^[A-Za-z0-9_]{3,32}$"
return username.range(of: pattern, options: .regularExpression) != nil
}
private var isPasswordValid: Bool {
password.count >= 6 && password.count <= 32
}
private var isConfirmPasswordValid: Bool {
confirmPassword == password && !confirmPassword.isEmpty
}
private var isFormValid: Bool {
!fullName.isEmpty && isUsernameValid && isPasswordValid && isConfirmPasswordValid
}
var body: some View {
NavigationView {
VStack {
Text(NSLocalizedString("RegistrationView_title", comment: "Регистрация"))
.font(.largeTitle)
.bold()
// Spacer()
// Полное имя
TextField(NSLocalizedString("RegistrationView_fullname", comment: "Полное имя"), text: $fullName)
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
.autocapitalization(.words)
.disableAutocorrection(true)
// Логин
TextField(NSLocalizedString("RegistrationView_login", comment: "Логин"), text: $username)
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
.autocapitalization(.none)
.disableAutocorrection(true)
if !isUsernameValid && !username.isEmpty {
Text(NSLocalizedString("RegistrationView_error_username_invalid", comment: "Неверный логин"))
.foregroundColor(.red)
.font(.caption)
}
// Пароль
SecureField(NSLocalizedString("RegistrationView_password", comment: "Пароль"), text: $password)
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
.autocapitalization(.none)
if !isPasswordValid && !password.isEmpty {
Text(NSLocalizedString("RegistrationView_error_password_invalid", comment: "Пароль должен быть от 6 до 32 символов"))
.foregroundColor(.red)
.font(.caption)
}
// Подтверждение пароля
SecureField(NSLocalizedString("RegistrationView_confirm_password", comment: "Подтверждение пароля"), text: $confirmPassword)
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
.autocapitalization(.none)
if !isConfirmPasswordValid && !confirmPassword.isEmpty {
Text(NSLocalizedString("RegistrationView_error_confirm_password_invalid", comment: "Пароли не совпадают"))
.foregroundColor(.red)
.font(.caption)
}
// Инвайт-код
TextField(NSLocalizedString("RegistrationView_invite", comment: "Инвайт-код"), text: $inviteCode)
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
.autocapitalization(.none)
.disableAutocorrection(true)
// Кнопка регистрации
Button(action: {
print("Регистрация отправлена")
}) {
Text(NSLocalizedString("RegistrationView_button_register", comment: "Зарегистрироваться"))
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(isFormValid ? Color.blue : Color.gray.opacity(0.6))
.cornerRadius(8)
}
.disabled(!isFormValid)
}
.padding()
.navigationBarItems(trailing:
Button(action: {
presentationMode.wrappedValue.dismiss()
}) {
Text(NSLocalizedString("RegistrationView_close", comment: "Закрыть"))
}
)
}
}
}
struct RegistrationView_Previews: PreviewProvider {
static var previews: some View {
RegistrationView()
}
}

View File

@ -0,0 +1,24 @@
//
// SettingsTab.swift
// VolnahubApp
//
// Created by cheykrym on 09/06/2025.
//
import SwiftUI
struct SettingsTab: View {
var body: some View {
NavigationView {
VStack {
Text("Настройки")
.font(.largeTitle)
.bold()
.padding()
Spacer()
}
.navigationTitle("Настройки")
}
}
}

View File

@ -0,0 +1,13 @@
import SwiftUI
struct SplashScreenView: View {
var body: some View {
VStack {
ProgressView()
.progressViewStyle(CircularProgressViewStyle())
.scaleEffect(1.5)
Text(NSLocalizedString("loading_placeholder", comment: ""))
.padding(.top, 10)
}
}
}

9
Shared/config.swift Normal file
View File

@ -0,0 +1,9 @@
import SwiftUI
struct AppConfig {
static var DEBUG: Bool = false
static let SERVICE = Bundle.main.bundleIdentifier ?? "default.service"
static let PROTOCOL = "https"
static let API_SERVER = "\(PROTOCOL)://api.volnahub.ru"
static let SERVER_TIMEZONE = "GMT+3"
}

View File

@ -7,11 +7,27 @@
import SwiftUI
//@AppStorage("isLoggedIn") var isLoggedIn: Bool = false
//@AppStorage("isDarkMode") private var isDarkMode: Bool = false
//@AppStorage("currentUser") var currentUser: String = ""
@main
struct volnahubApp: App {
@AppStorage("isDarkMode") private var isDarkMode: Bool = true
@StateObject private var viewModel = LoginViewModel()
var body: some Scene {
WindowGroup {
ContentView()
ZStack {
Color(isDarkMode ? .black : .white) // Фон в зависимости от темы
if viewModel.isLoggedIn {
MainView(viewModel: viewModel)
} else {
LoginView(viewModel: viewModel)
}
}
.preferredColorScheme(isDarkMode ? .dark : .light)
}
}
}

View File

@ -17,7 +17,7 @@
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>

View File

@ -8,11 +8,23 @@
/* Begin PBXBuildFile section */
1A79408D2DF77BC3002569DA /* volnahubApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79407A2DF77BC2002569DA /* volnahubApp.swift */; };
1A79408E2DF77BC3002569DA /* volnahubApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79407A2DF77BC2002569DA /* volnahubApp.swift */; };
1A79408F2DF77BC3002569DA /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79407B2DF77BC2002569DA /* ContentView.swift */; };
1A7940902DF77BC3002569DA /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79407B2DF77BC2002569DA /* ContentView.swift */; };
1A7940912DF77BC3002569DA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1A79407C2DF77BC3002569DA /* Assets.xcassets */; };
1A7940922DF77BC3002569DA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1A79407C2DF77BC3002569DA /* Assets.xcassets */; };
1A7940A22DF77DE9002569DA /* AuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7940A12DF77DE9002569DA /* AuthService.swift */; };
1A7940A62DF77DF5002569DA /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7940A52DF77DF5002569DA /* User.swift */; };
1A7940AA2DF77E05002569DA /* LoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7940A92DF77E05002569DA /* LoginViewModel.swift */; };
1A7940B02DF77E26002569DA /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7940AF2DF77E26002569DA /* LoginView.swift */; };
1A7940B62DF77F21002569DA /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7940B52DF77F21002569DA /* MainView.swift */; };
1A7940C62DF7A98E002569DA /* ContactsTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7940C52DF7A98E002569DA /* ContactsTab.swift */; };
1A7940CA2DF7A99B002569DA /* ChatsTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7940C92DF7A99B002569DA /* ChatsTab.swift */; };
1A7940CE2DF7A9AA002569DA /* SettingsTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7940CD2DF7A9AA002569DA /* SettingsTab.swift */; };
1A7940DE2DF7B0D7002569DA /* config.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7940DD2DF7B0D7002569DA /* config.swift */; };
1A7940E22DF7B1C5002569DA /* KeychainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7940E12DF7B1C5002569DA /* KeychainService.swift */; };
1A7940E72DF7B5E5002569DA /* SplashScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7940E62DF7B5E5002569DA /* SplashScreenView.swift */; };
1A7940F02DF7B7A3002569DA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1A7940F22DF7B7A3002569DA /* Localizable.strings */; };
1A79410C2DF7C81D002569DA /* RegistrationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A79410B2DF7C81D002569DA /* RegistrationView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -24,6 +36,20 @@
1A7940892DF77BC3002569DA /* volnahub.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = volnahub.app; sourceTree = BUILT_PRODUCTS_DIR; };
1A79408B2DF77BC3002569DA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
1A79408C2DF77BC3002569DA /* macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = macOS.entitlements; sourceTree = "<group>"; };
1A7940A12DF77DE9002569DA /* AuthService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthService.swift; sourceTree = "<group>"; };
1A7940A52DF77DF5002569DA /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = "<group>"; };
1A7940A92DF77E05002569DA /* LoginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewModel.swift; sourceTree = "<group>"; };
1A7940AF2DF77E26002569DA /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; };
1A7940B52DF77F21002569DA /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
1A7940C52DF7A98E002569DA /* ContactsTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsTab.swift; sourceTree = "<group>"; };
1A7940C92DF7A99B002569DA /* ChatsTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatsTab.swift; sourceTree = "<group>"; };
1A7940CD2DF7A9AA002569DA /* SettingsTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTab.swift; sourceTree = "<group>"; };
1A7940DD2DF7B0D7002569DA /* config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = config.swift; sourceTree = "<group>"; };
1A7940E12DF7B1C5002569DA /* KeychainService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainService.swift; sourceTree = "<group>"; };
1A7940E62DF7B5E5002569DA /* SplashScreenView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashScreenView.swift; sourceTree = "<group>"; };
1A7940F12DF7B7A3002569DA /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
1A7940F72DF7B7EC002569DA /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
1A79410B2DF7C81D002569DA /* RegistrationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegistrationView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -57,9 +83,15 @@
1A7940792DF77BC2002569DA /* Shared */ = {
isa = PBXGroup;
children = (
1A7940FA2DF7B898002569DA /* Resources */,
1A7940E52DF7B341002569DA /* Services */,
1A7940A02DF77DCD002569DA /* Network */,
1A79409F2DF77DC4002569DA /* Models */,
1A79409E2DF77DBD002569DA /* ViewModels */,
1A79409D2DF77DB5002569DA /* Views */,
1A79407A2DF77BC2002569DA /* volnahubApp.swift */,
1A79407B2DF77BC2002569DA /* ContentView.swift */,
1A79407C2DF77BC3002569DA /* Assets.xcassets */,
1A7940DD2DF7B0D7002569DA /* config.swift */,
);
path = Shared;
sourceTree = "<group>";
@ -90,6 +122,61 @@
path = macOS;
sourceTree = "<group>";
};
1A79409D2DF77DB5002569DA /* Views */ = {
isa = PBXGroup;
children = (
1A79407B2DF77BC2002569DA /* ContentView.swift */,
1A7940AF2DF77E26002569DA /* LoginView.swift */,
1A7940B52DF77F21002569DA /* MainView.swift */,
1A7940C52DF7A98E002569DA /* ContactsTab.swift */,
1A7940C92DF7A99B002569DA /* ChatsTab.swift */,
1A7940CD2DF7A9AA002569DA /* SettingsTab.swift */,
1A7940E62DF7B5E5002569DA /* SplashScreenView.swift */,
1A79410B2DF7C81D002569DA /* RegistrationView.swift */,
);
path = Views;
sourceTree = "<group>";
};
1A79409E2DF77DBD002569DA /* ViewModels */ = {
isa = PBXGroup;
children = (
1A7940A92DF77E05002569DA /* LoginViewModel.swift */,
);
path = ViewModels;
sourceTree = "<group>";
};
1A79409F2DF77DC4002569DA /* Models */ = {
isa = PBXGroup;
children = (
1A7940A52DF77DF5002569DA /* User.swift */,
);
path = Models;
sourceTree = "<group>";
};
1A7940A02DF77DCD002569DA /* Network */ = {
isa = PBXGroup;
children = (
1A7940A12DF77DE9002569DA /* AuthService.swift */,
);
path = Network;
sourceTree = "<group>";
};
1A7940E52DF7B341002569DA /* Services */ = {
isa = PBXGroup;
children = (
1A7940E12DF7B1C5002569DA /* KeychainService.swift */,
);
path = Services;
sourceTree = "<group>";
};
1A7940FA2DF7B898002569DA /* Resources */ = {
isa = PBXGroup;
children = (
1A7940F22DF7B7A3002569DA /* Localizable.strings */,
);
path = Resources;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -151,6 +238,7 @@
knownRegions = (
en,
Base,
ru,
);
mainGroup = 1A7940742DF77BC2002569DA;
productRefGroup = 1A7940822DF77BC3002569DA /* Products */;
@ -169,6 +257,7 @@
buildActionMask = 2147483647;
files = (
1A7940912DF77BC3002569DA /* Assets.xcassets in Resources */,
1A7940F02DF7B7A3002569DA /* Localizable.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -187,7 +276,19 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1A7940C62DF7A98E002569DA /* ContactsTab.swift in Sources */,
1A7940E72DF7B5E5002569DA /* SplashScreenView.swift in Sources */,
1A7940B02DF77E26002569DA /* LoginView.swift in Sources */,
1A79410C2DF7C81D002569DA /* RegistrationView.swift in Sources */,
1A7940CE2DF7A9AA002569DA /* SettingsTab.swift in Sources */,
1A7940DE2DF7B0D7002569DA /* config.swift in Sources */,
1A7940B62DF77F21002569DA /* MainView.swift in Sources */,
1A7940E22DF7B1C5002569DA /* KeychainService.swift in Sources */,
1A7940A62DF77DF5002569DA /* User.swift in Sources */,
1A7940A22DF77DE9002569DA /* AuthService.swift in Sources */,
1A79408F2DF77BC3002569DA /* ContentView.swift in Sources */,
1A7940CA2DF7A99B002569DA /* ChatsTab.swift in Sources */,
1A7940AA2DF77E05002569DA /* LoginViewModel.swift in Sources */,
1A79408D2DF77BC3002569DA /* volnahubApp.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -197,17 +298,29 @@
buildActionMask = 2147483647;
files = (
1A7940902DF77BC3002569DA /* ContentView.swift in Sources */,
1A79408E2DF77BC3002569DA /* volnahubApp.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
1A7940F22DF7B7A3002569DA /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
1A7940F12DF7B7A3002569DA /* en */,
1A7940F72DF7B7EC002569DA /* ru */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
1A7940932DF77BC3002569DA /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
@ -267,6 +380,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
@ -321,6 +435,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1337;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = iOS/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
@ -342,6 +457,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1337;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = iOS/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;