Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 9 additions & 64 deletions SwiftUI-WorkoutApp.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 0 additions & 11 deletions SwiftUI-WorkoutApp/Libraries/SWAlert/Package.swift

This file was deleted.

3 changes: 0 additions & 3 deletions SwiftUI-WorkoutApp/Libraries/SWAlert/README.md

This file was deleted.

4 changes: 2 additions & 2 deletions SwiftUI-WorkoutApp/Libraries/SWModels/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ let package = Package(
products: [
.library(name: "SWModels", targets: ["SWModels"])
],
dependencies: [.package(path: "../Utils")],
dependencies: [.package(path: "../SWUtils")],
targets: [
.target(name: "SWModels", dependencies: ["Utils"]),
.target(name: "SWModels", dependencies: ["SWUtils"]),
.testTarget(name: "SWModelsTest", dependencies: ["SWModels"])
]
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Foundation
import Utils
import SWUtils

/// Модель с информацией о диалоге
public struct DialogResponse: Codable, Identifiable, Sendable {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Foundation
import Utils
import SWUtils

/// Форма для отправки создании/изменении мероприятия
public struct EventForm: Codable, Sendable, Equatable {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Foundation
import Utils
import SWUtils

/// Модель со всей информацией о мероприятии
public struct EventResponse: Codable, Identifiable, Equatable, Sendable {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Foundation
import Utils
import SWUtils

/// Модель с информацией о записи в дневнике
public struct JournalEntryResponse: Codable, Identifiable, Sendable {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Foundation
import Utils
import SWUtils

/// Модель с информацией о дневнике
public struct JournalResponse: Codable, Identifiable, Equatable, Sendable {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Foundation

/// Модель для экрана авторизации
public struct LoginCredentials: Equatable {
public var login: String
public var password: String
let minPasswordSize: Int

public init(
login: String = "",
password: String = "",
minPasswordSize: Int = Constants.minPasswordSize
) {
self.login = login
self.password = password
self.minPasswordSize = minPasswordSize
}

public var isReady: Bool {
!login.isEmpty && password.trueCount >= minPasswordSize
}

public var canRestorePassword: Bool { !login.isEmpty }

public func canLogIn(isError: Bool, isNetworkConnected: Bool) -> Bool {
isReady && !isError && isNetworkConnected
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Foundation
import Utils
import SWUtils

/// Форма для отправки при регистрации или изменении данных профиля
public struct MainUserForm: Codable, Equatable, Sendable {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Foundation
import Utils
import SWUtils

/// Модель сообщения в диалоге
public struct MessageResponse: Codable, Identifiable, Hashable, Sendable {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation
import MapKit.MKGeometry
import Utils
import SWUtils

/// Модель данных спортивной площадки
public struct Park: Codable, Identifiable, Hashable, Sendable {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Foundation
import Utils
import SWUtils

/// Модель данных пользователя со всеми доступными свойствами
public struct UserResponse: Codable, Identifiable, Hashable, Sendable {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
@testable import SWModels
import Testing

struct LoginCredentialsTests {
@Test
func testInitializationWithDefaultValues() {
let credentials = LoginCredentials()
#expect(credentials.login == "")
#expect(credentials.password == "")
#expect(credentials.minPasswordSize == Constants.minPasswordSize)
}

@Test
func testInitializationWithCustomParameters() {
let credentials = LoginCredentials(
login: "[email protected]",
password: "qwerty",
minPasswordSize: 5
)
#expect(credentials.login == "[email protected]")
#expect(credentials.password == "qwerty")
#expect(credentials.minPasswordSize == 5)
}

// MARK: - isReady

@Test
func testIsReady_AllFieldsEmpty() {
let credentials = LoginCredentials()
#expect(!credentials.isReady)
}

@Test
func testIsReady_LoginNotEmptyPasswordTooShort() {
let credentials = LoginCredentials(login: "user", password: "12345")
#expect(!credentials.isReady)
}

@Test
func testIsReady_ValidLoginAndExactMinPassword() {
let credentials = LoginCredentials(login: "user", password: "123456")
#expect(credentials.isReady)
}

@Test
func testIsReady_PasswordWithSpacesMeetingMinLength() {
let credentials = LoginCredentials(login: "user", password: "12 345 6")
#expect(credentials.isReady)
}

@Test
func testIsReady_PasswordWithSpacesBelowMinLength() {
let credentials = LoginCredentials(login: "user", password: "123 45")
#expect(!credentials.isReady)
}

@Test
func testIsReady_CustomMinPasswordSizeValidation() {
let credentials = LoginCredentials(
login: "user",
password: "1234",
minPasswordSize: 4
)
#expect(credentials.isReady)

let credentials2 = LoginCredentials(
login: "user",
password: "123",
minPasswordSize: 4
)
#expect(!credentials2.isReady)
}

// MARK: - canRestorePassword

@Test
func testCanRestorePassword_EmptyLogin() {
let credentials = LoginCredentials(login: "")
#expect(!credentials.canRestorePassword)
}

@Test
func testCanRestorePassword_NonEmptyLogin() {
let credentials = LoginCredentials(login: " ")
#expect(credentials.canRestorePassword)

let credentials2 = LoginCredentials(login: "[email protected]")
#expect(credentials2.canRestorePassword)
}

// MARK: - canLogIn

@Test
func testCanLogIn_AllConditionsMet() {
let credentials = LoginCredentials(login: "user", password: "123456")
#expect(credentials.canLogIn(isError: false, isNetworkConnected: true))
}

@Test
func testCanLogIn_WhenNotReady() {
let credentials = LoginCredentials(login: "user", password: "123")
#expect(!credentials.canLogIn(isError: false, isNetworkConnected: true))
}

@Test
func testCanLogIn_WithError() {
let credentials = LoginCredentials(login: "user", password: "123456")
#expect(!credentials.canLogIn(isError: true, isNetworkConnected: true))
}

@Test
func testCanLogIn_NoNetwork() {
let credentials = LoginCredentials(login: "user", password: "123456")
#expect(!credentials.canLogIn(isError: false, isNetworkConnected: false))
}

@Test
func testCanLogIn_MultipleIssues() {
let credentials = LoginCredentials(login: "user", password: "123")
#expect(!credentials.canLogIn(isError: true, isNetworkConnected: false))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
import PackageDescription

let package = Package(
name: "Utils",
name: "SWUtils",
platforms: [.iOS(.v15)],
products: [
.library(name: "Utils", targets: ["Utils"])
.library(name: "SWUtils", targets: ["SWUtils"])
],
targets: [
.target(name: "Utils", dependencies: []),
.testTarget(name: "UtilsTests", dependencies: ["Utils"])
.target(name: "SWUtils", dependencies: []),
.testTarget(name: "SWUtilsTests", dependencies: ["SWUtils"])
]
)
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import Foundation

enum FeedbackSender {
public enum FeedbackSender {
/// Открывает диплинк `mailto` для создания письма
/// - Parameters:
/// - subject: Тема письма
/// - messageBody: Тело письма
/// - recipients: Получатели
@MainActor
static func sendFeedback(subject: String, messageBody: String, recipients: [String]) {
public static func sendFeedback(
subject: String,
messageBody: String,
recipients: [String]
) {
let encodedSubject = subject.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? "Feedback"
let encodedBody = messageBody.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? ""
if let firstRecipient = recipients.first {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Foundation
import Network

public final class NetworkStatus: ObservableObject {
private let monitor = NWPathMonitor()
private let queue = DispatchQueue.global(qos: .background)

/// `true` - there is a network connection, `false` - no network connection
@Published public private(set) var isConnected = false

public init() {
monitor.pathUpdateHandler = { path in
Task { @MainActor in
self.isConnected = path.status == .satisfied
}
}
monitor.start(queue: queue)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Foundation

/// Обертка над `FileManager`
public struct SWFileManager {
private var documentDirectoryURL: URL {
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
}

private let fileName: String

/// Инициализатор
/// - Parameter fileName: Название файла с расширением, например `MyFile.json`
public init(fileName: String) {
self.fileName = fileName
}

/// Проверяет существование сохраненного файла
public var documentExists: Bool {
let path: String = if #available(iOS 16.0, *) {
documentDirectoryURL.appendingPathComponent(fileName).path()
} else {
documentDirectoryURL.appendingPathComponent(fileName).path
}
return FileManager().fileExists(atPath: path)
}

/// Сохраняет `Encodable`-объект
public func save(_ object: some Encodable) throws {
let encodedData = try JSONEncoder().encode(object)
let jsonString = String(decoding: encodedData, as: UTF8.self)
let url = documentDirectoryURL.appendingPathComponent(fileName)
try jsonString.write(to: url, atomically: true, encoding: .utf8)
}

/// Загружает данные из ранее сохраненного файла
public func get<T: Decodable>() throws -> T {
let url = documentDirectoryURL.appendingPathComponent(fileName)
let data = try Data(contentsOf: url)
return try JSONDecoder().decode(T.self, from: data)
}

/// Удаляет сохраненный файл
///
/// Если файл не существовал перед удалением, выбросит ошибку
public func removeFile() throws {
try FileManager.default.removeItem(at: documentDirectoryURL.appendingPathComponent(fileName))
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import UIKit
import UIKit.UIApplication

enum URLOpener {
public enum URLOpener {
@MainActor
static func open(_ url: URL?) {
public static func open(_ url: URL?) {
if let url, UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
Expand Down
Loading