我编写了一个简单的FETCH客户端来处理请求.代码如下:
请求
public protocol 请求Parameter: Encodable {
var queryItems: [URLQueryItem] { get }
}
public extension 请求Parameter {
var queryItems: [URLQueryItem] {
var this = [URLQueryItem]()
let mirror = Mirror(reflecting: self)
for child in mirror.children {
guard let label = child.label, let value = child.value as? CustomStringConvertible else {
continue
}
this.append(.init(name: label, value: value.description))
}
return this
}
}
extension Dictionary: 请求Parameter where Key == String, Value: CustomStringConvertible & Encodable {
public var queryItems: [URLQueryItem] {
var this = [URLQueryItem]()
for (key, value) in self {
this.append(.init(name: key, value: value.description))
}
return this
}
}
public protocol 请求<Parameter, Response> {
associatedtype Parameter: 请求Parameter
associatedtype Response: Decodable
var method: HTTPMethod { get }
var path: String { get }
var parameter: Parameter? { get }
var headers: HTTPHeaders? { get }
}
public extension 请求 {
func build(_ urlString: String, encoder: JSONEncoder? = nil) -> URL请求 {
var components = URLComponents(string: urlString)
components?.path = path
if method == .get {
components?.queryItems = parameter?.queryItems
}
guard let url = components?.url else {
fatalError("url can't be nil")
}
var url请求 = URL请求(url: url)
url请求.httpMethod = method.rawValue
if let headers {
url请求.headers = headers
}
if method == .post, let parameter, let encoder {
do {
url请求.httpBody = try encoder.encode(parameter)
} catch {
fatalError("`parameter cannot be encoded`")
}
}
return url请求
}
}
客户端
public protocol 客户端 {
func send<请求Type: 请求>(_ request: 请求Type, encoder: JSONEncoder?, decoder: JSONDecoder) async throws -> 请求Type.Response
var urlString: String { get }
static var shared: Self { get }
}
public extension 客户端 {
func send<请求Type: 请求>(_ request: 请求Type, encoder: JSONEncoder? = nil, decoder: JSONDecoder = .init()) async throws -> 请求Type.Response {
let (data, urlResponse) = try await URLSession.shared.data(for: request.build(urlString, encoder: encoder))
guard let httpResponse = urlResponse as? HTTPURLResponse else {
throw #InvalidResponse
}
guard 200 ... 299 ~= httpResponse.statusCode else {
throw #NetworkFailure(httpResponse.statusCode)
}
return try decoder.decode(请求Type.Response.self, from: data)
}
}
因此,一个简单且运行正常的网络请求代码如下所示:
struct Foo请求: 请求 {
typealias Parameter = FooParameter
typealias Response = Foo
var method: HTTPMethod = .get
var path: String = ""
var parameter: FooParameter? = FooParameter()
var headers: HTTPHeaders? = nil
}
struct FooParameter: 请求Parameter {}
struct Foo: Decodable {}
struct Foo客户端: 客户端 {
var urlString: String = "fooURLString.com"
static var shared: Foo客户端 = Self()
}
let result = try await Foo客户端.shared.send(Foo请求())
但有时一个简单的GET请求可能不需要参数.为了避免重复代码var parameter: FooParameter? = nil
或var parameter: BarParameter? = nil
,我添加了一个NeverParameter
类型:
public struct NeverParameter: 请求Parameter {
private init() {}
}
public extension 请求 where Parameter == NeverParameter {
var parameter: Parameter? { nil }
}
在这一点上,问题出现了.我认为对于NeverParameter
,请求应该是这样的:
struct Foo请求: 请求 {
typealias Parameter = NeverParameter
typealias Response = Foo
var method: HTTPMethod = .get
var path: String = ""
var headers: HTTPHeaders? = nil
}
但实际上,注释掉typealias Parameter = NeverParameter
仍然可以编译:
struct Foo请求: 请求 {
// typealias Parameter = NeverParameter
typealias Response = Foo
var method: HTTPMethod = .get
var path: String = ""
var headers: HTTPHeaders? = nil
}
(lldb) po type(of: parameter)
Swift.Optional<请求.NeverParameter>
为什么编译器可以推断参数的类型是NeverParameter
?即使只在WHERE子句NeverParameter
下添加了缺省值nil?
并添加了具有相同实现的另一个NeverParameter1
,Xcode最终抛出了一个错误:
public struct NeverParameter: 请求Parameter {
private init() {}
}
public extension 请求 where Parameter == NeverParameter {
var parameter: Parameter? { nil }
}
public struct NeverParameter1: 请求Parameter {
private init() {}
}
public extension 请求 where Parameter == NeverParameter1 {
var parameter: Parameter? { nil }
}
错误:
Type 'Foo请求' does not conform to protocol '请求'