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
13 changes: 13 additions & 0 deletions Mindbox.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
0A3D045A2BC6803E00E1FC52 /* ImageFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A3D04592BC6803E00E1FC52 /* ImageFormat.swift */; };
0E7A224A082FA2DA35706CC7 /* MotionServiceResolvePositionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C8192B8B7043EF74D05B36B /* MotionServiceResolvePositionTests.swift */; };
0E7A224A082FA2DA35706CC8 /* MotionServiceShakeToEditTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C8192B8B7043EF74D05B36C /* MotionServiceShakeToEditTests.swift */; };
1E3BD63AB3F1521C253CB818 /* MBNetworkFetcherResponseHandlingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97FEDDEB5F71A67F1C4C675F /* MBNetworkFetcherResponseHandlingTests.swift */; };
302E35788CBDA959283569F4 /* MotionServiceBehaviorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0DB93A7997961CA7C2BE917 /* MotionServiceBehaviorTests.swift */; };
313B233A25ADEA0F00A1CB72 /* Mindbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 313B233025ADEA0F00A1CB72 /* Mindbox.framework */; };
313B233F25ADEA0F00A1CB72 /* MindboxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 313B233E25ADEA0F00A1CB72 /* MindboxTests.swift */; };
Expand Down Expand Up @@ -1054,6 +1055,7 @@
84FCD3B825CA109E00D1E574 /* MockNetworkFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockNetworkFetcher.swift; sourceTree = "<group>"; };
84FCD3BC25CA10F600D1E574 /* SuccessResponse.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = SuccessResponse.json; sourceTree = "<group>"; };
9778038796A8426ABDED1E97 /* FeatureTogglesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureTogglesModel.swift; sourceTree = "<group>"; };
97FEDDEB5F71A67F1C4C675F /* MBNetworkFetcherResponseHandlingTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = MBNetworkFetcherResponseHandlingTests.swift; sourceTree = "<group>"; };
9B24FAAB28C74B8300F10B5D /* InAppConfigurationRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppConfigurationRepository.swift; sourceTree = "<group>"; };
9B24FAAD28C74BA500F10B5D /* InAppCoreManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppCoreManager.swift; sourceTree = "<group>"; };
9B24FAB028C74BD200F10B5D /* InAppConfigurationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppConfigurationManager.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1613,6 +1615,7 @@
84DC49D525D185A600D5D758 /* Supporting Files */,
313B234025ADEA0F00A1CB72 /* Info.plist */,
73662EFB100A1A3520058D4E /* TrackVisitManager */,
64FD3F7576619DCF106D10B6 /* Network */,
);
path = MindboxTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -2249,6 +2252,15 @@
name = Frameworks;
sourceTree = "<group>";
};
64FD3F7576619DCF106D10B6 /* Network */ = {
isa = PBXGroup;
children = (
97FEDDEB5F71A67F1C4C675F /* MBNetworkFetcherResponseHandlingTests.swift */,
);
name = Network;
path = Network;
sourceTree = "<group>";
};
73662EFB100A1A3520058D4E /* TrackVisitManager */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -4709,6 +4721,7 @@
0E7A224A082FA2DA35706CC7 /* MotionServiceResolvePositionTests.swift in Sources */,
0E7A224A082FA2DA35706CC8 /* MotionServiceShakeToEditTests.swift in Sources */,
302E35788CBDA959283569F4 /* MotionServiceBehaviorTests.swift in Sources */,
1E3BD63AB3F1521C253CB818 /* MBNetworkFetcherResponseHandlingTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
12 changes: 11 additions & 1 deletion Mindbox/Network/Abstract/NetworkFetcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,17 @@ protocol NetworkFetcher {
route: Route,
completion: @escaping ((Result<Void, MindboxError>) -> Void)
)

/// Cancels all ongoing network tasks.
func cancelAllTasks()
}

extension NetworkFetcher {
func request<T>(
type: T.Type,
route: Route,
completion: @escaping ((Result<T, MindboxError>) -> Void)
) where T: Decodable {
request(type: type, route: route, needBaseResponse: true, completion: completion)
}
}
135 changes: 89 additions & 46 deletions Mindbox/Network/MBNetworkFetcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -211,88 +211,131 @@ class MBNetworkFetcher: NetworkFetcher {
needBaseResponse: Bool,
completion: @escaping ((Result<Data, MindboxError>) -> Void)
) {
if context.statusCode == .serverError {
let body = String(data: data, encoding: .utf8)
completion(.failure(internalServerError(httpStatusCode: context.httpResponse.statusCode, networkTimeMs: context.networkTimeMs, responseBody: body)))
return
}

do {
try decodeResponseData(
switch context.statusCode {
case .success:
handleSuccessResponseData(
data,
context: context,
emptyData: emptyData,
needBaseResponse: needBaseResponse,
completion: completion
)
} catch let decodingError {
handleDecodingError(
decodingError,
data: data,
case .clientError:
handleClientErrorResponseData(
data,
context: context,
completion: completion
)
case .serverError:
handleServerErrorResponseData(
data,
context: context,
emptyData: emptyData,
completion: completion
)
case .redirection:
completion(.failure(.invalidResponse(context.response)))
}
}

private func decodeResponseData(
// MARK: - 2xx Success

private func handleSuccessResponseData(
_ data: Data,
context: ResponseContext,
emptyData: Bool,
needBaseResponse: Bool,
completion: @escaping ((Result<Data, MindboxError>) -> Void)
) throws {
) {
if !needBaseResponse {
completion(.success(data))
return
}

let decoder = JSONDecoder()
// Decoding to structure with `status` field
let base = try decoder.decode(BaseResponse.self, from: data)
// Figure out what server returned
switch base.status {
case .success, .transactionAlreadyProcessed:
completion(.success(data))
case .validationError:
let error = try decoder.decode(ValidationError.self, from: data)
completion(.failure(.validationError(error)))
case .protocolError:
let error = try decoder.decode(ProtocolError.self, from: data)
completion(.failure(.protocolError(error)))
case .internalServerError:
let error = try decoder.decode(ProtocolError.self, from: data)
completion(.failure(.serverError(error)))
case .unknown:
completion(.failure(.invalidResponse(context.response)))
do {
let base = try decoder.decode(BaseResponse.self, from: data)
switch base.status {
case .success, .transactionAlreadyProcessed:
completion(.success(data))
case .validationError:
let error = try decoder.decode(ValidationError.self, from: data)
completion(.failure(.validationError(error)))
case .protocolError, .internalServerError, .unknown:
completion(.failure(.invalidResponse(context.response)))
}
} catch {
if emptyData {
completion(.success(data))
} else {
completion(.failure(.internalError(.init(errorKey: .parsing, rawError: error))))
}
}
}

private func handleDecodingError(
_ decodingError: Error,
data: Data,
// MARK: - 4xx Client Error

private func handleClientErrorResponseData(
_ data: Data,
context: ResponseContext,
emptyData: Bool,
completion: @escaping ((Result<Data, MindboxError>) -> Void)
) {
switch context.statusCode {
case .serverError:
let body = String(data: data, encoding: .utf8)
completion(.failure(internalServerError(httpStatusCode: context.httpResponse.statusCode, networkTimeMs: context.networkTimeMs, responseBody: body)))
default:
if emptyData {
completion(.success(data))
} else if context.httpResponse.statusCode == 404 {
let httpCode = context.httpResponse.statusCode
let decoder = JSONDecoder()

do {
let base = try decoder.decode(BaseResponse.self, from: data)
switch base.status {
case .protocolError:
let error = try decoder.decode(ProtocolError.self, from: data)
completion(.failure(.protocolError(error)))
case .validationError:
let error = try decoder.decode(ValidationError.self, from: data)
completion(.failure(.validationError(error)))
default:
completion(.failure(.protocolError(.init(
status: .protocolError,
errorMessage: "Client error",
httpStatusCode: httpCode
))))
}
} catch {
if httpCode == 404 {
completion(.failure(.protocolError(.init(
status: .protocolError,
errorMessage: "Invalid request url",
httpStatusCode: context.httpResponse.statusCode
httpStatusCode: httpCode
))))
} else {
completion(.failure(.internalError(.init(errorKey: .parsing, rawError: decodingError))))
completion(.failure(.protocolError(.init(
status: .protocolError,
errorMessage: "Client error",
httpStatusCode: httpCode
))))
}
}
}

// MARK: - 5xx Server Error

private func handleServerErrorResponseData(
_ data: Data,
context: ResponseContext,
completion: @escaping ((Result<Data, MindboxError>) -> Void)
) {
let decoder = JSONDecoder()
do {
let error = try decoder.decode(ProtocolError.self, from: data)
completion(.failure(.serverError(error)))
} catch {
let body = String(data: data, encoding: .utf8)
completion(.failure(internalServerError(
httpStatusCode: context.httpResponse.statusCode,
networkTimeMs: context.networkTimeMs,
responseBody: body
)))
}
}

private func handleMissingData(
error: Error?,
context: ResponseContext,
Expand Down
2 changes: 1 addition & 1 deletion Mindbox/NetworkRepository/Event/MBEventRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class MBEventRepository: EventRepository {
deviceUUID: deviceUUID
)
let route = makeRoute(wrapper: wrapper)
fetcher.request(type: type, route: route, needBaseResponse: true, completion: { result in
fetcher.request(type: type, route: route, completion: { result in
DispatchQueue.main.async {
switch result {
case let .failure(error):
Expand Down
Loading