Skip to content

Commit da33f9b

Browse files
authored
Рефактор (#279)
* Рефактор - Убрал лишние public-модификаторы доступа - Доработал логирование ошибок в сетевом слое - Добавил комментарий про баг с NavigationView + searchable - Добавил тесты * Переделываю экран черного списка * Убрал лишнюю строчку кода
1 parent 54e68f5 commit da33f9b

File tree

13 files changed

+191
-64
lines changed

13 files changed

+191
-64
lines changed

SwiftUI-WorkoutApp.xcodeproj/project.pbxproj

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
671B4AE92D4F623100286996 /* ModernPickedImagesGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671B4AE82D4F623100286996 /* ModernPickedImagesGrid.swift */; };
1717
671B4AEB2D4F683E00286996 /* ImagePickerViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671B4AEA2D4F683E00286996 /* ImagePickerViews.swift */; };
1818
671D7DEC28210D2F0068E728 /* EmptyContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671D7DEB28210D2F0068E728 /* EmptyContentView.swift */; };
19+
674000402D55E97900E5CB06 /* BlackListScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6740003F2D55E97900E5CB06 /* BlackListScreen.swift */; };
1920
67419ACF282E70B9004F5339 /* ParksListScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67419ACE282E70B9004F5339 /* ParksListScreen.swift */; };
2021
6747575628113419002F0A24 /* ChangePasswordScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6747575528113419002F0A24 /* ChangePasswordScreen.swift */; };
2122
6747575928128603002F0A24 /* ParkDetailScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6747575828128603002F0A24 /* ParkDetailScreen.swift */; };
@@ -103,6 +104,7 @@
103104
671B4AE82D4F623100286996 /* ModernPickedImagesGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModernPickedImagesGrid.swift; sourceTree = "<group>"; };
104105
671B4AEA2D4F683E00286996 /* ImagePickerViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePickerViews.swift; sourceTree = "<group>"; };
105106
671D7DEB28210D2F0068E728 /* EmptyContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyContentView.swift; sourceTree = "<group>"; };
107+
6740003F2D55E97900E5CB06 /* BlackListScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlackListScreen.swift; sourceTree = "<group>"; };
106108
67419ACE282E70B9004F5339 /* ParksListScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParksListScreen.swift; sourceTree = "<group>"; };
107109
6747575528113419002F0A24 /* ChangePasswordScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangePasswordScreen.swift; sourceTree = "<group>"; };
108110
6747575828128603002F0A24 /* ParkDetailScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParkDetailScreen.swift; sourceTree = "<group>"; };
@@ -408,6 +410,7 @@
408410
674D061A28280A63007E75C6 /* FriendRequestsView.swift */,
409411
674D0622282A9896007E75C6 /* SearchUsersScreen.swift */,
410412
6765B2572D4544C8006164AB /* MainUserProfileScreen.swift */,
413+
6740003F2D55E97900E5CB06 /* BlackListScreen.swift */,
411414
);
412415
path = Profile;
413416
sourceTree = "<group>";
@@ -624,6 +627,7 @@
624627
6798AA40280AEDC900DB76F1 /* RootScreen.swift in Sources */,
625628
671B4AE92D4F623100286996 /* ModernPickedImagesGrid.swift in Sources */,
626629
675EC64F2814126800C2E229 /* TextEntryScreen.swift in Sources */,
630+
674000402D55E97900E5CB06 /* BlackListScreen.swift in Sources */,
627631
674D0623282A9896007E75C6 /* SearchUsersScreen.swift in Sources */,
628632
67A4710D2AEED8F8004D341D /* PastEventStorage.swift in Sources */,
629633
675FB8DB2ADDB87200C9671F /* ParksMapScreen+LocationSettingReminderView.swift in Sources */,
@@ -856,7 +860,7 @@
856860
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
857861
CLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES;
858862
CODE_SIGN_STYLE = Automatic;
859-
CURRENT_PROJECT_VERSION = 10;
863+
CURRENT_PROJECT_VERSION = 12;
860864
DEVELOPMENT_ASSET_PATHS = "SwiftUI-WorkoutApp/Preview\\ Content/PreviewContent.swift SwiftUI-WorkoutApp/Preview\\ Content";
861865
DEVELOPMENT_TEAM = CR68PP2Z3F;
862866
ENABLE_PREVIEWS = YES;
@@ -907,7 +911,7 @@
907911
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
908912
CLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES;
909913
CODE_SIGN_STYLE = Automatic;
910-
CURRENT_PROJECT_VERSION = 10;
914+
CURRENT_PROJECT_VERSION = 12;
911915
DEVELOPMENT_ASSET_PATHS = "SwiftUI-WorkoutApp/Preview\\ Content/PreviewContent.swift SwiftUI-WorkoutApp/Preview\\ Content";
912916
DEVELOPMENT_TEAM = CR68PP2Z3F;
913917
ENABLE_PREVIEWS = YES;

SwiftUI-WorkoutApp/Libraries/SWNetwork/Sources/SWNetwork/Data+.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ extension Data {
77
let json = String(data: jsonData, encoding: .utf8) {
88
json
99
} else {
10-
""
10+
"отсутствует"
1111
}
1212
}
1313

SwiftUI-WorkoutApp/Libraries/SWNetwork/Sources/SWNetwork/ErrorResponse.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import Foundation
22

3-
public struct ErrorResponse: Codable {
4-
public let errors: [String]?
5-
public let name, message: String?
6-
public let code, status: Int?
7-
public let type: String?
3+
struct ErrorResponse: Codable {
4+
let errors: [String]?
5+
let name, message: String?
6+
let code, status: Int?
7+
let type: String?
88

9-
public var realCode: Int {
9+
var realCode: Int {
1010
if let code, code != 0 {
1111
code
1212
} else {

SwiftUI-WorkoutApp/Libraries/SWNetwork/Sources/SWNetwork/HTTPHeaderField.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
public struct HTTPHeaderField: Equatable {
1+
struct HTTPHeaderField: Equatable {
22
let key: String
33
let value: String
44

5-
public init(key: String, value: String) {
5+
init(key: String, value: String) {
66
self.key = key
77
self.value = value
88
}

SwiftUI-WorkoutApp/Libraries/SWNetwork/Sources/SWNetwork/SWNetwork.swift

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ extension SWNetworkService: SWNetworkProtocol {
4242
guard let decodedResult = try? decoder.decode(T.self, from: data) else {
4343
throw log(
4444
APIError.decodingError,
45-
code: response.statusCode,
4645
request: request,
4746
data: data,
4847
response: response
@@ -52,10 +51,8 @@ extension SWNetworkService: SWNetworkProtocol {
5251
return decodedResult
5352
default:
5453
let errorInfo = try decoder.decode(ErrorResponse.self, from: data)
55-
let apiError = APIError(errorInfo, response.statusCode)
5654
throw log(
57-
apiError,
58-
code: response.statusCode,
55+
APIError(errorInfo, response.statusCode),
5956
request: request,
6057
data: data,
6158
response: response
@@ -80,15 +77,12 @@ extension SWNetworkService: SWNetworkProtocol {
8077
return true
8178
}
8279
if let errorInfo = try? decoder.decode(ErrorResponse.self, from: data) {
83-
let apiError = APIError(errorInfo, response.statusCode)
84-
log(
85-
apiError,
86-
code: response.statusCode,
80+
throw log(
81+
APIError(errorInfo, response.statusCode),
8782
request: request,
8883
data: data,
8984
response: response
9085
)
91-
throw apiError
9286
}
9387
return false
9488
} catch {
@@ -135,19 +129,18 @@ private extension SWNetworkService {
135129
return error
136130
}
137131

138-
@discardableResult
139132
func log(
140133
_ error: Error,
141-
code: Int,
142134
request: URLRequest,
143135
data: Data,
144-
response _: HTTPURLResponse?
136+
response: HTTPURLResponse
145137
) -> Error {
146138
logger.error(
147139
"""
148-
Код ответа: \(code, privacy: .public)
140+
Код ответа: \(response.statusCode, privacy: .public)
149141
\(error.localizedDescription, privacy: .public)
150142
\nURL запроса: \(request.urlString, privacy: .public)
143+
\nответ (response): \(response)
151144
\nJSON в ответе: \(data.prettyJson, privacy: .public)
152145
"""
153146
)
@@ -161,22 +154,21 @@ private extension SWNetworkService {
161154
/// - error: Исходная ошибка
162155
/// - request: Запрос, упавший в ошибку
163156
/// - Returns: Новая ошибка
164-
@discardableResult
165157
func handleUrlSession(_ error: Error, _ request: URLRequest) -> Error {
166158
let errorCode = (error as NSError).code
167-
if errorCode == -999 {
159+
guard errorCode != -999 else {
168160
let message = "Запрос отменён! Код ошибки: -999. URL запроса: \(request.urlString)"
169161
logger.error("\(message)")
170-
} else {
171-
logger.error(
172-
"""
173-
Ошибка!
174-
\(error.localizedDescription, privacy: .public)
175-
Код ошибки: \(errorCode, privacy: .public)
176-
\nURL запроса: \(request.urlString, privacy: .public)
177-
"""
178-
)
162+
return CancellationError()
179163
}
164+
logger.error(
165+
"""
166+
Ошибка!
167+
\(error.localizedDescription, privacy: .public)
168+
Код ошибки: \(errorCode, privacy: .public)
169+
\nURL запроса: \(request.urlString, privacy: .public)
170+
"""
171+
)
180172
guard let urlError = error as? URLError else {
181173
return error
182174
}

SwiftUI-WorkoutApp/Libraries/SWNetwork/Tests/SWNetworkTests/DataTests.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,32 @@ struct DataTests {
1616
#expect(prettyJson == expectedResult)
1717
}
1818

19+
@Test
20+
func validJSON_producesFormattedString() throws {
21+
let jsonObject = ["name": "Test", "value": 42] as [String: Any]
22+
let data = try JSONSerialization.data(withJSONObject: jsonObject)
23+
let result = data.prettyJson
24+
#expect(!result.isEmpty)
25+
#expect(result != "отсутствует")
26+
#expect(result.contains("\n"))
27+
#expect(result.contains(" "))
28+
}
29+
30+
@Test(arguments: [Data(), Data([0x00, 0x01, 0x02])])
31+
func emptyOrInvalidData(_ data: Data) {
32+
#expect(data.prettyJson == "отсутствует")
33+
}
34+
35+
@Test
36+
func minimalValidJSON_keepsContentIntegrity() throws {
37+
let originalJson = "{\"key\":\"value\"}"
38+
let data = try #require(originalJson.data(using: .utf8))
39+
let prettyJson = data.prettyJson
40+
#expect(prettyJson.contains("\"key\" : \"value\""))
41+
#expect(prettyJson.contains("{"))
42+
#expect(prettyJson.contains("}"))
43+
}
44+
1945
@Test
2046
func appendString() throws {
2147
var data = Data()

SwiftUI-WorkoutApp/Libraries/SWNetwork/Tests/SWNetworkTests/HTTPStatusCodeGroupTests.swift

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,15 @@
22
import Testing
33

44
struct StatusCodeGroupTests {
5-
@Test
6-
func isSuccess() {
7-
let notSuccessCodes: [StatusCodeGroup] = [
8-
.info, .redirect, .clientError, .serverError, .unknown
9-
]
10-
notSuccessCodes.forEach { #expect(!$0.isSuccess) }
5+
@Test(arguments: [StatusCodeGroup.info, .redirect, .clientError, .serverError, .unknown])
6+
func isSuccess(notSuccessCode: StatusCodeGroup) {
7+
#expect(!notSuccessCode.isSuccess)
118
#expect(StatusCodeGroup.success.isSuccess)
129
}
1310

14-
@Test
15-
func isError() {
16-
let notErrorCodes: [StatusCodeGroup] = [
17-
.success, .info, .redirect, .unknown
18-
]
19-
notErrorCodes.forEach { #expect(!$0.isError) }
11+
@Test(arguments: [StatusCodeGroup.success, .info, .redirect, .unknown])
12+
func isError(notErrorCode: StatusCodeGroup) {
13+
#expect(!notErrorCode.isError)
2014
#expect(StatusCodeGroup.clientError.isError)
2115
#expect(StatusCodeGroup.serverError.isError)
2216
}

SwiftUI-WorkoutApp/Libraries/SWUtils/Sources/SWUtils/SWAlert.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,13 @@ public final class SWAlert {
3838
///
3939
/// Игнорирует `CancellationError`
4040
public func presentDefaultUIKit(_ error: Error) {
41-
guard (error as NSError).code != -999 else { return }
41+
guard type(of: error) != CancellationError.self else {
42+
// Баг в NavigationView + searchable приводит к ошибке отмены,
43+
// если сначала нажать на поле поиска, а следующий модальный
44+
// экран закрыть свайпом вниз. Будет исправлено переходом
45+
// на iOS 16 min + NavigationStack
46+
return
47+
}
4248
presentDefaultUIKit(message: error.localizedDescription)
4349
}
4450

SwiftUI-WorkoutApp/Screens/Common/UsersListScreen.swift

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,6 @@ extension UsersListScreen {
5757
case eventParticipants(list: [UserResponse])
5858
/// Тренирующиеся на площадке
5959
case parkParticipants(list: [UserResponse])
60-
/// Черный список основного пользователя
61-
case blacklist
6260
}
6361
}
6462

@@ -71,8 +69,6 @@ private extension UsersListScreen.Mode {
7169
"Участники мероприятия"
7270
case .parkParticipants:
7371
"Здесь тренируются"
74-
case .blacklist:
75-
"Черный список"
7672
}
7773
}
7874
}
@@ -119,7 +115,7 @@ private extension UsersListScreen {
119115
} label: {
120116
userRowView(with: model)
121117
}
122-
case .friends, .eventParticipants, .parkParticipants, .blacklist:
118+
case .friends, .eventParticipants, .parkParticipants:
123119
NavigationLink {
124120
UserDetailsScreen(for: model)
125121
.navigationBarTitleDisplayMode(.inline)
@@ -185,14 +181,6 @@ private extension UsersListScreen {
185181
}
186182
case let .eventParticipants(list), let .parkParticipants(list):
187183
users = list
188-
case .blacklist:
189-
if !users.isEmpty, !refresh { return }
190-
if !refresh { isLoading = true }
191-
users = try await client.getBlacklist()
192-
try? defaults.saveBlacklist(users)
193-
if users.isEmpty {
194-
dismiss()
195-
}
196184
}
197185
} catch {
198186
SWAlert.shared.presentDefaultUIKit(error)

0 commit comments

Comments
 (0)