From 529277d098729300247b0b8f8ea3b01c24701fbf Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Thu, 25 May 2023 18:17:49 +0300 Subject: [PATCH] feat: use DecodingError instead of untyped error in TINetworking decoding --- .../Contents.swift | 2 +- .../ApplicationJsonResponseContent.swift | 12 ++++++++++-- .../ResponseContent/EmptyResponseContent.swift | 4 ++-- .../ResponseContent/MapResponseContent.swift | 10 +++++----- .../ResponseContent/ResponseContent.swift | 2 +- .../TextPlainResponseContent.swift | 9 ++++++--- .../Response/ResponseType+Decoding.swift | 18 +++++++++++++----- docs/tikeychainutils/singlevaluestorage.md | 2 +- 8 files changed, 39 insertions(+), 20 deletions(-) diff --git a/TIKeychainUtils/TIKeychainUtils.app/Contents/MacOS/TIKeychainUtils.playground/Pages/SingleValueStorage.xcplaygroundpage/Contents.swift b/TIKeychainUtils/TIKeychainUtils.app/Contents/MacOS/TIKeychainUtils.playground/Pages/SingleValueStorage.xcplaygroundpage/Contents.swift index 3d6b2e97..fbc3640c 100644 --- a/TIKeychainUtils/TIKeychainUtils.app/Contents/MacOS/TIKeychainUtils.playground/Pages/SingleValueStorage.xcplaygroundpage/Contents.swift +++ b/TIKeychainUtils/TIKeychainUtils.app/Contents/MacOS/TIKeychainUtils.playground/Pages/SingleValueStorage.xcplaygroundpage/Contents.swift @@ -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 // ... diff --git a/TINetworking/Sources/Mapping/ResponseContent/ApplicationJsonResponseContent.swift b/TINetworking/Sources/Mapping/ResponseContent/ApplicationJsonResponseContent.swift index 37a6497f..f01834f7 100644 --- a/TINetworking/Sources/Mapping/ResponseContent/ApplicationJsonResponseContent.swift +++ b/TINetworking/Sources/Mapping/ResponseContent/ApplicationJsonResponseContent.swift @@ -33,8 +33,16 @@ open class ApplicationJsonResponseContent: BaseContent, Respon // MARK: - ResponseContent - public func decodeResponse(data: Data) throws -> Model { - try jsonDecoder.decode(Model.self, from: data) + public func decodeResponse(data: Data) -> Result { + 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))) + } } } diff --git a/TINetworking/Sources/Mapping/ResponseContent/EmptyResponseContent.swift b/TINetworking/Sources/Mapping/ResponseContent/EmptyResponseContent.swift index 301b7358..beefa435 100644 --- a/TINetworking/Sources/Mapping/ResponseContent/EmptyResponseContent.swift +++ b/TINetworking/Sources/Mapping/ResponseContent/EmptyResponseContent.swift @@ -23,7 +23,7 @@ import Foundation public final class EmptyResponseContent: BaseContent, ResponseContent { - public func decodeResponse(data: Data) throws { - () + public func decodeResponse(data: Data) -> Result { + .success(()) } } diff --git a/TINetworking/Sources/Mapping/ResponseContent/MapResponseContent.swift b/TINetworking/Sources/Mapping/ResponseContent/MapResponseContent.swift index 7e38fabb..0a8f6d1e 100644 --- a/TINetworking/Sources/Mapping/ResponseContent/MapResponseContent.swift +++ b/TINetworking/Sources/Mapping/ResponseContent/MapResponseContent.swift @@ -24,11 +24,11 @@ import Foundation import TISwiftUtils public final class MapResponseContent: BaseContent, ResponseContent { - private let decodeClosure: ThrowableClosure + private let decodeClosure: Closure> public init(responseContent: C, transform: @escaping Closure) { 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: BaseContent, ResponseContent { // MARK: - ResponseContent - public func decodeResponse(data: Data) throws -> Model { - try decodeClosure(data) + public func decodeResponse(data: Data) -> Result { + decodeClosure(data) } } @@ -54,7 +54,7 @@ public extension JSONDecoder { responseContent().map(tranfsorm) } - func decoding(to tranfsorm: @escaping Closure) -> ThrowableClosure { + func decoding(to tranfsorm: @escaping Closure) -> Closure> { responseContent(tranfsorm).decodeResponse } } diff --git a/TINetworking/Sources/Mapping/ResponseContent/ResponseContent.swift b/TINetworking/Sources/Mapping/ResponseContent/ResponseContent.swift index 91cdd16c..999e4d1a 100644 --- a/TINetworking/Sources/Mapping/ResponseContent/ResponseContent.swift +++ b/TINetworking/Sources/Mapping/ResponseContent/ResponseContent.swift @@ -25,5 +25,5 @@ import Foundation public protocol ResponseContent: Content { associatedtype Model - func decodeResponse(data: Data) throws -> Model + func decodeResponse(data: Data) -> Result } diff --git a/TINetworking/Sources/Mapping/ResponseContent/TextPlainResponseContent.swift b/TINetworking/Sources/Mapping/ResponseContent/TextPlainResponseContent.swift index c26cd2ff..f1a2143f 100644 --- a/TINetworking/Sources/Mapping/ResponseContent/TextPlainResponseContent.swift +++ b/TINetworking/Sources/Mapping/ResponseContent/TextPlainResponseContent.swift @@ -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 { 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) } } diff --git a/TINetworking/Sources/Response/ResponseType+Decoding.swift b/TINetworking/Sources/Response/ResponseType+Decoding.swift index da78f1ac..d63e6532 100644 --- a/TINetworking/Sources/Response/ResponseType+Decoding.swift +++ b/TINetworking/Sources/Response/ResponseType+Decoding.swift @@ -24,18 +24,26 @@ import Foundation import TISwiftUtils public extension ResponseType { - typealias DecodingClosure = ThrowableClosure + typealias DecodingClosure = Closure> func decode(mapping: [KeyValueTuple>]) -> Result { + 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)) } diff --git a/docs/tikeychainutils/singlevaluestorage.md b/docs/tikeychainutils/singlevaluestorage.md index 33600ad0..2fe36c8e 100644 --- a/docs/tikeychainutils/singlevaluestorage.md +++ b/docs/tikeychainutils/singlevaluestorage.md @@ -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 // ...