編輯:我正在盡我最大的努力使我的問題更簡單,在這里我想要解決的是,我有一個 API,如果我的資料有效,API 將給出正確的回應,我需要對此進行解碼快速使用相應的結構。
此外,如果我的資料錯誤,API 將失敗,并且會產生一個不同結構的錯誤回應。
使用組合,我只能解碼一個結構型別。
那么如何讓我的解碼接受任何型別?泛型是我希望解決的一種方法,但在這里我需要實作的協議是一個我認為限制我使用泛型的問題。
感謝您試一試。
// MARK: - ResponseStruct 模型
struct ResponseStruct: Codable {
}
//MARK: -協議
public protocol RequestProtocol{
associatedtype ResponseOutput
func fetchFunction() -> AnyPublisher<ResponseOutput, Error>
}
//標記: - 實作
struct RequestStruct: Codable, RequestProtocol {
typealias ResponseOutput = ResponseStruct
func fetchFunction() -> AnyPublisher<ResponseOutput, Error> {
let networkManager = NetworkManager()
do {
return try networkManager.apiCall(url: url, method: .post, body: JSONEncoder().encode(self))
.decode(type: ResponseStruct.self, decoder: JSONDecoder())
.eraseToAnyPublisher()
} catch {
}
}
}
Above this is the code, and this is fine if the API call works but if the call fails I will get an error response, so how to decode that struct in a combined way? I don't want to write another call for that and I am hoping to get something to do with Failure in the combine. or CAN I MAKE THE associated type (see protocol) generic?
uj5u.com熱心網友回復:
我請求你的耐心。我想我理解這個問題,但我很難將它與你給出的代碼對齊。你fetchFunction特別奇怪,我不明白你的協議試圖完成什么。
讓我從問題陳述開始并探索解決方案。我會一步一步地做,所以這將是一個很長的回應。tl; dr最后是一個游樂場。
I have an API and if my data is valid the API will give the correct response, for which I need to decode with the respective struct in swift. If my data is wrong the API will fail and it will produce an error response which is a different struct.
所以我們需要兩個結構。一個代表成功,一個代表失敗。我補一下:
struct SuccessfulResult : Decodable {
let interestingText : String
}
struct FailedResult : Decodable {
let errorCode : Int
let failureReason : String
}
基于此,對網路的請求可以:
- 回傳要解碼的成功資料
SuccessfulResult - 回傳要解碼的失敗資料
FailedResult - 由于低級錯誤(例如網路不可達)而失敗。
讓我們為“網路作業得很好,給了我成功資料或失敗資料”創建一個型別:
enum NetworkData {
case success(Data)
case failure(Data)
}
我將Error用于低級錯誤。
使用這些型別,API 請求可以表示為該型別的發布者AnyPublisher<NetworkData, Error>
但這不是你要求的。您想將資料決議為SuccessfulResultor FailedResult。這也增加了 JSON 決議失敗的可能性,我也將在泛型Error.
我們需要一個資料型別來表示 的決議變體NetworkData:
enum ParsedNetworkData {
case success(SuccessfulResult)
case failure(FailedResult)
}
這意味著您要求的真實網路請求型別是該型別的發布者AnyPublisher<ParsedNetworkData,Error>
我們可以撰寫一個函式來將Data承載網路請求 ,AnyPublisher<NetworkData,Error>轉換為AnyPublisher<ParsedNetworkData,Error>.
撰寫該函式的一種方法是:
func transformRawNetworkRequest(_ networkRequest: AnyPublisher<NetworkData,Error>) -> AnyPublisher<ParsedNetworkData, Error> {
let decoder = JSONDecoder()
return networkRequest
.tryMap { networkData -> ParsedNetworkData in
switch(networkData) {
case .success(let successData):
return ParsedNetworkData.success(try decoder.decode(SuccessfulResult.self, from: successData))
case .failure(let failureData):
return ParsedNetworkData.failure(try decoder.decode(FailedResult.self, from: failureData))
}
}
.eraseToAnyPublisher()
}
To exercise the code we can write a function to create a fake network request and add some code that tries things out. Putting it all together into a playground you get:
import Foundation
import Combine
struct SuccessfulResult : Decodable {
let interestingText : String
}
struct FailedResult : Decodable {
let errorCode : Int
let failureReason : String
}
enum NetworkData {
case success(Data)
case failure(Data)
}
enum ParsedNetworkData {
case success(SuccessfulResult)
case failure(FailedResult)
}
func transformRawNetworkRequest(_ networkRequest: AnyPublisher<NetworkData,Error>) -> AnyPublisher<ParsedNetworkData, Error> {
let decoder = JSONDecoder()
return networkRequest
.tryMap { networkData -> ParsedNetworkData in
switch(networkData) {
case .success(let successData):
return ParsedNetworkData.success(try decoder.decode(SuccessfulResult.self, from: successData))
case .failure(let failureData):
return ParsedNetworkData.failure(try decoder.decode(FailedResult.self, from: failureData))
}
}
.eraseToAnyPublisher()
}
func fakeNetworkRequest(shouldSucceed: Bool) -> AnyPublisher<NetworkData,Error> {
let successfulBody = """
{ "interestingText" : "This is interesting!" }
""".data(using: .utf8)!
let failedBody = """
{
"errorCode" : -4242,
"failureReason" : "Bogus! Stuff went wrong."
}
""".data(using: .utf8)!
return Future<NetworkData,Error> { fulfill in
let delay = Set(stride(from: 100, to: 600, by: 100)).randomElement()!
DispatchQueue.global(qos: .background).asyncAfter(
deadline: .now() .milliseconds(delay)) {
if(shouldSucceed) {
fulfill(.success(NetworkData.success(successfulBody)))
} else {
fulfill(.success(NetworkData.failure(failedBody)))
}
}
}.eraseToAnyPublisher()
}
var subscriptions = Set<AnyCancellable>()
let successfulRequest = transformRawNetworkRequest(fakeNetworkRequest(shouldSucceed: true))
successfulRequest
.sink(receiveCompletion:{ debugPrint($0) },
receiveValue:{ debugPrint("Success Result \($0)") })
.store(in: &subscriptions)
let failedRequest = transformRawNetworkRequest(fakeNetworkRequest(shouldSucceed: false))
failedRequest
.sink(receiveCompletion:{ debugPrint($0) },
receiveValue:{ debugPrint("Failure Result \($0)") })
.store(in: &subscriptions)
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/439486.html
標籤:swift generics swiftui combine
上一篇:Kotlin中的通用資料類
