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
8 changes: 6 additions & 2 deletions SwiftUI-WorkoutApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
671B4AE92D4F623100286996 /* ModernPickedImagesGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671B4AE82D4F623100286996 /* ModernPickedImagesGrid.swift */; };
671B4AEB2D4F683E00286996 /* ImagePickerViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671B4AEA2D4F683E00286996 /* ImagePickerViews.swift */; };
671D7DEC28210D2F0068E728 /* EmptyContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671D7DEB28210D2F0068E728 /* EmptyContentView.swift */; };
674000402D55E97900E5CB06 /* BlackListScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6740003F2D55E97900E5CB06 /* BlackListScreen.swift */; };
67419ACF282E70B9004F5339 /* ParksListScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67419ACE282E70B9004F5339 /* ParksListScreen.swift */; };
6747575628113419002F0A24 /* ChangePasswordScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6747575528113419002F0A24 /* ChangePasswordScreen.swift */; };
6747575928128603002F0A24 /* ParkDetailScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6747575828128603002F0A24 /* ParkDetailScreen.swift */; };
Expand Down Expand Up @@ -103,6 +104,7 @@
671B4AE82D4F623100286996 /* ModernPickedImagesGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModernPickedImagesGrid.swift; sourceTree = "<group>"; };
671B4AEA2D4F683E00286996 /* ImagePickerViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePickerViews.swift; sourceTree = "<group>"; };
671D7DEB28210D2F0068E728 /* EmptyContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyContentView.swift; sourceTree = "<group>"; };
6740003F2D55E97900E5CB06 /* BlackListScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlackListScreen.swift; sourceTree = "<group>"; };
67419ACE282E70B9004F5339 /* ParksListScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParksListScreen.swift; sourceTree = "<group>"; };
6747575528113419002F0A24 /* ChangePasswordScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangePasswordScreen.swift; sourceTree = "<group>"; };
6747575828128603002F0A24 /* ParkDetailScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParkDetailScreen.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -408,6 +410,7 @@
674D061A28280A63007E75C6 /* FriendRequestsView.swift */,
674D0622282A9896007E75C6 /* SearchUsersScreen.swift */,
6765B2572D4544C8006164AB /* MainUserProfileScreen.swift */,
6740003F2D55E97900E5CB06 /* BlackListScreen.swift */,
);
path = Profile;
sourceTree = "<group>";
Expand Down Expand Up @@ -624,6 +627,7 @@
6798AA40280AEDC900DB76F1 /* RootScreen.swift in Sources */,
671B4AE92D4F623100286996 /* ModernPickedImagesGrid.swift in Sources */,
675EC64F2814126800C2E229 /* TextEntryScreen.swift in Sources */,
674000402D55E97900E5CB06 /* BlackListScreen.swift in Sources */,
674D0623282A9896007E75C6 /* SearchUsersScreen.swift in Sources */,
67A4710D2AEED8F8004D341D /* PastEventStorage.swift in Sources */,
675FB8DB2ADDB87200C9671F /* ParksMapScreen+LocationSettingReminderView.swift in Sources */,
Expand Down Expand Up @@ -856,7 +860,7 @@
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 10;
CURRENT_PROJECT_VERSION = 12;
DEVELOPMENT_ASSET_PATHS = "SwiftUI-WorkoutApp/Preview\\ Content/PreviewContent.swift SwiftUI-WorkoutApp/Preview\\ Content";
DEVELOPMENT_TEAM = CR68PP2Z3F;
ENABLE_PREVIEWS = YES;
Expand Down Expand Up @@ -907,7 +911,7 @@
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 10;
CURRENT_PROJECT_VERSION = 12;
DEVELOPMENT_ASSET_PATHS = "SwiftUI-WorkoutApp/Preview\\ Content/PreviewContent.swift SwiftUI-WorkoutApp/Preview\\ Content";
DEVELOPMENT_TEAM = CR68PP2Z3F;
ENABLE_PREVIEWS = YES;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ extension Data {
let json = String(data: jsonData, encoding: .utf8) {
json
} else {
""
"отсутствует"
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import Foundation

public struct ErrorResponse: Codable {
public let errors: [String]?
public let name, message: String?
public let code, status: Int?
public let type: String?
struct ErrorResponse: Codable {
let errors: [String]?
let name, message: String?
let code, status: Int?
let type: String?

public var realCode: Int {
var realCode: Int {
if let code, code != 0 {
code
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
public struct HTTPHeaderField: Equatable {
struct HTTPHeaderField: Equatable {
let key: String
let value: String

public init(key: String, value: String) {
init(key: String, value: String) {
self.key = key
self.value = value
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ extension SWNetworkService: SWNetworkProtocol {
guard let decodedResult = try? decoder.decode(T.self, from: data) else {
throw log(
APIError.decodingError,
code: response.statusCode,
request: request,
data: data,
response: response
Expand All @@ -52,10 +51,8 @@ extension SWNetworkService: SWNetworkProtocol {
return decodedResult
default:
let errorInfo = try decoder.decode(ErrorResponse.self, from: data)
let apiError = APIError(errorInfo, response.statusCode)
throw log(
apiError,
code: response.statusCode,
APIError(errorInfo, response.statusCode),
request: request,
data: data,
response: response
Expand All @@ -80,15 +77,12 @@ extension SWNetworkService: SWNetworkProtocol {
return true
}
if let errorInfo = try? decoder.decode(ErrorResponse.self, from: data) {
let apiError = APIError(errorInfo, response.statusCode)
log(
apiError,
code: response.statusCode,
throw log(
APIError(errorInfo, response.statusCode),
request: request,
data: data,
response: response
)
throw apiError
}
return false
} catch {
Expand Down Expand Up @@ -135,19 +129,18 @@ private extension SWNetworkService {
return error
}

@discardableResult
func log(
_ error: Error,
code: Int,
request: URLRequest,
data: Data,
response _: HTTPURLResponse?
response: HTTPURLResponse
) -> Error {
logger.error(
"""
Код ответа: \(code, privacy: .public)
Код ответа: \(response.statusCode, privacy: .public)
\(error.localizedDescription, privacy: .public)
\nURL запроса: \(request.urlString, privacy: .public)
\nответ (response): \(response)
\nJSON в ответе: \(data.prettyJson, privacy: .public)
"""
)
Expand All @@ -161,22 +154,21 @@ private extension SWNetworkService {
/// - error: Исходная ошибка
/// - request: Запрос, упавший в ошибку
/// - Returns: Новая ошибка
@discardableResult
func handleUrlSession(_ error: Error, _ request: URLRequest) -> Error {
let errorCode = (error as NSError).code
if errorCode == -999 {
guard errorCode != -999 else {
let message = "Запрос отменён! Код ошибки: -999. URL запроса: \(request.urlString)"
logger.error("\(message)")
} else {
logger.error(
"""
Ошибка!
\(error.localizedDescription, privacy: .public)
Код ошибки: \(errorCode, privacy: .public)
\nURL запроса: \(request.urlString, privacy: .public)
"""
)
return CancellationError()
}
logger.error(
"""
Ошибка!
\(error.localizedDescription, privacy: .public)
Код ошибки: \(errorCode, privacy: .public)
\nURL запроса: \(request.urlString, privacy: .public)
"""
)
guard let urlError = error as? URLError else {
return error
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,32 @@ struct DataTests {
#expect(prettyJson == expectedResult)
}

@Test
func validJSON_producesFormattedString() throws {
let jsonObject = ["name": "Test", "value": 42] as [String: Any]
let data = try JSONSerialization.data(withJSONObject: jsonObject)
let result = data.prettyJson
#expect(!result.isEmpty)
#expect(result != "отсутствует")
#expect(result.contains("\n"))
#expect(result.contains(" "))
}

@Test(arguments: [Data(), Data([0x00, 0x01, 0x02])])
func emptyOrInvalidData(_ data: Data) {
#expect(data.prettyJson == "отсутствует")
}

@Test
func minimalValidJSON_keepsContentIntegrity() throws {
let originalJson = "{\"key\":\"value\"}"
let data = try #require(originalJson.data(using: .utf8))
let prettyJson = data.prettyJson
#expect(prettyJson.contains("\"key\" : \"value\""))
#expect(prettyJson.contains("{"))
#expect(prettyJson.contains("}"))
}

@Test
func appendString() throws {
var data = Data()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,15 @@
import Testing

struct StatusCodeGroupTests {
@Test
func isSuccess() {
let notSuccessCodes: [StatusCodeGroup] = [
.info, .redirect, .clientError, .serverError, .unknown
]
notSuccessCodes.forEach { #expect(!$0.isSuccess) }
@Test(arguments: [StatusCodeGroup.info, .redirect, .clientError, .serverError, .unknown])
func isSuccess(notSuccessCode: StatusCodeGroup) {
#expect(!notSuccessCode.isSuccess)
#expect(StatusCodeGroup.success.isSuccess)
}

@Test
func isError() {
let notErrorCodes: [StatusCodeGroup] = [
.success, .info, .redirect, .unknown
]
notErrorCodes.forEach { #expect(!$0.isError) }
@Test(arguments: [StatusCodeGroup.success, .info, .redirect, .unknown])
func isError(notErrorCode: StatusCodeGroup) {
#expect(!notErrorCode.isError)
#expect(StatusCodeGroup.clientError.isError)
#expect(StatusCodeGroup.serverError.isError)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ public final class SWAlert {
///
/// Игнорирует `CancellationError`
public func presentDefaultUIKit(_ error: Error) {
guard (error as NSError).code != -999 else { return }
guard type(of: error) != CancellationError.self else {
// Баг в NavigationView + searchable приводит к ошибке отмены,
// если сначала нажать на поле поиска, а следующий модальный
// экран закрыть свайпом вниз. Будет исправлено переходом
// на iOS 16 min + NavigationStack
return
}
presentDefaultUIKit(message: error.localizedDescription)
}

Expand Down
14 changes: 1 addition & 13 deletions SwiftUI-WorkoutApp/Screens/Common/UsersListScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ extension UsersListScreen {
case eventParticipants(list: [UserResponse])
/// Тренирующиеся на площадке
case parkParticipants(list: [UserResponse])
/// Черный список основного пользователя
case blacklist
}
}

Expand All @@ -71,8 +69,6 @@ private extension UsersListScreen.Mode {
"Участники мероприятия"
case .parkParticipants:
"Здесь тренируются"
case .blacklist:
"Черный список"
}
}
}
Expand Down Expand Up @@ -119,7 +115,7 @@ private extension UsersListScreen {
} label: {
userRowView(with: model)
}
case .friends, .eventParticipants, .parkParticipants, .blacklist:
case .friends, .eventParticipants, .parkParticipants:
NavigationLink {
UserDetailsScreen(for: model)
.navigationBarTitleDisplayMode(.inline)
Expand Down Expand Up @@ -185,14 +181,6 @@ private extension UsersListScreen {
}
case let .eventParticipants(list), let .parkParticipants(list):
users = list
case .blacklist:
if !users.isEmpty, !refresh { return }
if !refresh { isLoading = true }
users = try await client.getBlacklist()
try? defaults.saveBlacklist(users)
if users.isEmpty {
dismiss()
}
}
} catch {
SWAlert.shared.presentDefaultUIKit(error)
Expand Down
Loading