feat: use DecodingError instead of untyped error in TINetworking decoding

This commit is contained in:
Ivan Smolin 2023-05-25 18:17:49 +03:00
parent ecfb83bafa
commit 529277d098
8 changed files with 39 additions and 20 deletions

View File

@ -32,7 +32,7 @@ extension StorageKey {
let apiTokenKeychainStorage = StringValueKeychainStorage(keychain: keychain, storageKey: .apiToken)
if apiTokenKeychainStorage.hasStoredValue() {
// app wasn't reinstalled, open auth user flow, perform requests
// open auth user flow, perform requests
} else {
// show login screen
// ...

View File

@ -33,8 +33,16 @@ open class ApplicationJsonResponseContent<Model: Decodable>: BaseContent, Respon
// MARK: - ResponseContent
public func decodeResponse(data: Data) throws -> Model {
try jsonDecoder.decode(Model.self, from: data)
public func decodeResponse(data: Data) -> Result<Model, DecodingError> {
do {
return .success(try jsonDecoder.decode(Model.self, from: data))
} catch let decodingError as DecodingError {
return .failure(decodingError)
} catch {
return .failure(.dataCorrupted(.init(codingPath: [],
debugDescription: .init(),
underlyingError: error)))
}
}
}

View File

@ -23,7 +23,7 @@
import Foundation
public final class EmptyResponseContent: BaseContent, ResponseContent {
public func decodeResponse(data: Data) throws {
()
public func decodeResponse(data: Data) -> Result<Void, DecodingError> {
.success(())
}
}

View File

@ -24,11 +24,11 @@ import Foundation
import TISwiftUtils
public final class MapResponseContent<Model>: BaseContent, ResponseContent {
private let decodeClosure: ThrowableClosure<Data, Model>
private let decodeClosure: Closure<Data, Result<Model, DecodingError>>
public init<C: ResponseContent>(responseContent: C, transform: @escaping Closure<C.Model, Model>) {
decodeClosure = {
transform(try responseContent.decodeResponse(data: $0))
responseContent.decodeResponse(data: $0).map(transform)
}
super.init(mediaTypeName: responseContent.mediaTypeName)
@ -36,8 +36,8 @@ public final class MapResponseContent<Model>: BaseContent, ResponseContent {
// MARK: - ResponseContent
public func decodeResponse(data: Data) throws -> Model {
try decodeClosure(data)
public func decodeResponse(data: Data) -> Result<Model, DecodingError> {
decodeClosure(data)
}
}
@ -54,7 +54,7 @@ public extension JSONDecoder {
responseContent().map(tranfsorm)
}
func decoding<T: Decodable, R>(to tranfsorm: @escaping Closure<T, R>) -> ThrowableClosure<Data, R> {
func decoding<T: Decodable, R>(to tranfsorm: @escaping Closure<T, R>) -> Closure<Data, Result<R, DecodingError>> {
responseContent(tranfsorm).decodeResponse
}
}

View File

@ -25,5 +25,5 @@ import Foundation
public protocol ResponseContent: Content {
associatedtype Model
func decodeResponse(data: Data) throws -> Model
func decodeResponse(data: Data) -> Result<Model, DecodingError>
}

View File

@ -38,11 +38,14 @@ public final class TextPlainResponseContent: BaseContent, ResponseContent {
// MARK: - ResponseContent
public func decodeResponse(data: Data) throws -> String {
public func decodeResponse(data: Data) -> Result<String, DecodingError> {
guard let plainText = String(data: data, encoding: encoding) else {
throw StringDecodingError(data: data, encoding: encoding)
let context = DecodingError.Context(codingPath: [],
debugDescription: .init(),
underlyingError: StringDecodingError(data: data, encoding: encoding))
return .failure(.typeMismatch(String.self, context))
}
return plainText
return .success(plainText)
}
}

View File

@ -24,18 +24,26 @@ import Foundation
import TISwiftUtils
public extension ResponseType {
typealias DecodingClosure<R> = ThrowableClosure<Data, R>
typealias DecodingClosure<R> = Closure<Data, Result<R, DecodingError>>
func decode<R>(mapping: [KeyValueTuple<StatusCodeMimeType, DecodingClosure<R>>]) -> Result<R, ErrorType> {
var decodingErrors: [DecodingError] = []
for (statusCodesMimeType, decodeClosure) in mapping
where statusCodesMimeType.statusCode == statusCode && statusCodesMimeType.mimeType == mimeType {
do {
return .success(try decodeClosure(data))
} catch {
return .failure(objectMappingError(underlyingError: error))
switch decodeClosure(data) {
case let .success(result):
return .success(result)
case let .failure(decodingError):
decodingErrors.append(decodingError)
}
}
if let firstDecodingError = decodingErrors.first {
return .failure(objectMappingError(underlyingError: firstDecodingError))
}
guard mapping.contains(where: { $0.key.statusCode == statusCode }) else {
return .failure(unsupportedStatusCodeError(statusCode: statusCode))
}

View File

@ -30,7 +30,7 @@ extension StorageKey {
let apiTokenKeychainStorage = StringValueKeychainStorage(keychain: keychain, storageKey: .apiToken)
if apiTokenKeychainStorage.hasStoredValue() {
// app wasn't reinstalled, open auth user flow, perform requests
// open auth user flow, perform requests
} else {
// show login screen
// ...