मेरे पास APIRequest
नाम का एक प्रोटोकॉल है जिसका नाम ResponseType
और एक डिकोड फ़ंक्शन है। यह उदाहरण पूर्ण नहीं है, लेकिन मेरा मानना है कि प्रश्न के लिए ये एकमात्र प्रासंगिक भाग हैं।
ArrayResponse
नाम की एक संरचना भी होती है जो यह दर्शाती है कि जब नेटवर्क प्रतिक्रिया विभिन्न वस्तुओं के items
की एक सरणी के रूप में लौटती है (विशिष्ट APIRequest
के ResponseType
के आधार पर, साथ ही साथ totalItems
।
protocol APIRequest {
associatedtype ResponseType: Codable
/// Decodes the request result into the ResponseType
func decode(_: Result<Data, APIError>) throws -> ResponseType
}
struct ArrayResponse<T>: Codable where T: Codable {
let items: [T]
let totalItems: Int
}
यहां एक ऐसी संरचना का उदाहरण दिया गया है जो APIRequest
प्रोटोकॉल का पालन करती है और इसे ResponseType
को Brand
के रूप में निर्दिष्ट करती है, जो एक Codable
संरचना है जो सर्वर से लौटाए जा रहे ब्रांड डेटा का प्रतिनिधित्व करती है।
struct BrandRequest: APIRequest {
typealias ResponseType = Brand
}
struct Brand: Codable {
var brandID: Int
var brandName: String?
}
BrandRequest
का उपयोग सर्वर से एकल Brand
लाने के लिए किया जाता है, लेकिन मैं Brand
की एक सरणी भी प्राप्त कर सकता हूं (ऊपर ArrayResponse
द्वारा दर्शाया गया है, क्योंकि ब्रांड है कई अलग-अलग प्रकारों में से एक, जो सभी एक ही पैटर्न का पालन करते हैं), BrandsRequest
का उपयोग करते हुए, जो इसे ResponseType
को Brand
s की एक सरणी के रूप में निर्दिष्ट करता है।
struct BrandsRequest: APIRequest {
typealias ResponseType = [Brand]
}
APIRequest
का पालन करने वाली प्रत्येक संरचना में एक decode
फ़ंक्शन प्रदान करने के बजाय, मैंने प्रोटोकॉल एक्सटेंशन में एक डिफ़ॉल्ट कार्यान्वयन करने का निर्णय लिया है, क्योंकि वे सभी एक ही डिकोडिंग का पालन करते हैं।
इस पर निर्भर करते हुए कि ResponseType
एक सरणी है (जैसे [Brand]
, या एक ही आइटम, जैसे ब्रांड
, मैं एक भिन्न संस्करण का उपयोग करता हूं decode
फ़ंक्शन का। यह एकल आइटम के लिए अच्छी तरह से काम करता है, लेकिन आइटम की सरणी के लिए, मैं ऐरे में देखना चाहता हूं, इसके तत्वों के प्रकार की खोज करना चाहता हूं, और इसका उपयोग यह जांचने के लिए करता हूं कि क्या result.decoded()
को उस विशेष प्रकार के ArrayResponse<>
के रूप में डिकोड किया जाता है।
इसलिए, उदाहरण के लिए, यदि मैं एक BrandsRequest
बनाता हूं, तो मुझे यह शीर्ष decode
फ़ंक्शन चाहिए जो को वापस करने के लिए Array को डिकोड करता है (result.decoded() के रूप में प्रयास करें ArrayResponse
ब्रांड
के साथ एक अलग संरचना (जैसे उत्पाद
, ग्राहक
, आदि) के आधार पर इस फ़ंक्शन को प्राप्त होने वाले सरणी में तत्व का प्रकार। इस उदाहरण में elementType
प्राप्त करने के मेरे प्रयास के रूप में कुछ गैर-संकलन कोड है और इसे एक सामान्य तर्क के रूप में उपयोग करें, लेकिन निश्चित रूप से यह काम नहीं करता है। मैं भी सामान्य तर्क के रूप में कोडेबल
पास नहीं कर सकता, क्योंकि संकलक मुझे बताता है: प्रोटोकॉल प्रकार का मान 'कोडेबल' (उर्फ 'डिकोडेबल और एनकोडेबल') 'डिकोडेबल' के अनुरूप नहीं हो सकता है; केवल संरचना/enum/वर्ग प्रकार प्रोटोकॉल के अनुरूप हो सकते हैं
।
तो मेरे प्रश्न हैं:
- क्या
ArrayResponse<insert type here>
में उपयोग करने के लिए ऐरे में तत्व के प्रकार को कैप्चर करने का कोई तरीका है? - क्या
decode
नेटवर्क प्रतिक्रियाओं के लिए एक बेहतर तरीका है जोArrayResponse
जैसी दिखने वाली वस्तुओं की सरणी देता है बनाम एकल आइटम प्रतिक्रिया जैसेBrand
?
extension APIRequest where ResponseType == Array<Codable> {
func decode(_ result: Result<Data, APIError>) throws -> ResponseType {
let elementType = type(of: ResponseType.Element.self)
print(elementType)
return (try result.decoded() as ArrayResponse<elementType>).items
}
}
extension APIRequest {
func decode(_ result: Result<Data, APIError>) throws -> ResponseType {
return try result.decoded() as ResponseType
}
}
परिशिष्ट: एक अन्य दृष्टिकोण जिसके बारे में मैंने सोचा था कि तत्व प्रकार के बजाय टी को सरणी प्रकार के रूप में उपयोग करने के लिए ArrayResponse<>
को बदलना है:
struct ArrayResponse<T>: Codable where T: Codable {
let items: T
let totalItems: Int
}
और उसके बाद सरणी डीकोड को सरल बनाने के लिए:
extension APIRequest where ResponseType == Array<Codable> {
func decode(_ result: Result<Data, APIError>) throws -> ResponseType {
return (try result.decoded() as ArrayResponse<ResponseType>).items
}
}
हालाँकि, संकलक मुझे ये 2 त्रुटियाँ देता है: 'ArrayResponse' requires that 'Decodable & Encodable' conform to 'Encodable'
तथा Value of protocol type 'Decodable & Encodable' cannot conform to 'Encodable'; only struct/enum/class types can conform to protocols
परिशिष्ट 2: मैं सब कुछ काम कर रहा हूं और संकलित कर सकता हूं, अगर मैं सरणी के भीतर तत्व के प्रकार को परिभाषित करने के लिए APIRequest
में एक और संबद्ध प्रकार जोड़ता हूं:
protocol APIRequest {
associatedtype ResponseType: Codable
associatedtype ElementType: Codable
/// Decodes the request result into the ResponseType
func decode(_: Result<Data, APIError>) throws -> ResponseType
}
और फिर Codable
के बजाय ElementType
का उपयोग करने के लिए मेरे सरणी decode
फ़ंक्शन को बदलें:
extension APIRequest where ResponseType == Array<ElementType> {
func decode(_ result: Result<Data, APIError>) throws -> ResponseType {
return (try result.decoded() as ArrayResponse<ResponseType>).items
}
}
लेकिन फिर मुझे प्रत्येक संरचना में ElementType
की आपूर्ति करनी होगी जो APIRequest
के अनुरूप है, जिसमें एकल अनुरोध शामिल हैं जहां यह ResponseType
के लिए बेमानी है और इसका उपयोग नहीं किया जाता है। सरणी अनुरोधों के लिए, यह केवल सरणी ResponseType
के अंदर का मान है, जो दोहराव भी महसूस करता है:
struct BrandRequest: APIRequest {
typealias ResponseType = Brand
typealias ElementType = Brand
}
struct BrandsRequest: APIRequest {
typealias ResponseType = [Brand]
typealias ElementType = Brand
}
मेरी समस्या की जड़ यह है कि मैं Brand
प्रकार को [Brand]
सरणी में खोजना चाहता हूं, और इसे ArrayResponse
डिकोडिंग के लिए उपयोग करना चाहता हूं।
2 जवाब
यह एक ArrayRequest
बनाने और फिर ElementType
का डिफ़ॉल्ट कार्यान्वयन प्रदान करने लायक हो सकता है जैसे:
protocol ArrayRequest: APIRequest where ResponseType: Collection {
associatedtype ElementType = ResponseType.Element
}
Collection
अतिरिक्त बाधाओं से बचने में मदद कर सकता है।
मुझे संदेह है कि यह प्रोटोकॉल का दुरुपयोग है। PAT (संबद्ध प्रकार वाले प्रोटोकॉल) सभी मौजूदा प्रकारों में अधिक सुविधाएँ जोड़ने के बारे में हैं, और यह स्पष्ट नहीं है कि यह ऐसा करता है। इसके बजाय, मेरा मानना है कि आपको जेनरिक समस्या है।
पहले की तरह, आपके पास एक ArrayResponse
है, क्योंकि यह आपके API में एक खास बात है:
struct ArrayResponse<Element: Codable>: Codable {
let items: [Element]
let totalItems: Int
}
अब, प्रोटोकॉल के बजाय, आपको एक सामान्य संरचना की आवश्यकता है:
struct Request<Response: Codable> {
// You need some way to fetch this, so I'm going to assume there's an URLRequest
// hiding in here.
let urlRequest: URLRequest
// Decode single values
func decode(_ result: Result<Data, APIError>) throws -> Response {
return try JSONDecoder().decode(Response.self, from: result.get())
}
// Decode Arrays. This would be nice to put in a constrained extension instead of here,
// but that's not currently possible in Swift
func decode(_ result: Result<Data, APIError>) throws -> ArrayResponse<Response> {
return try JSONDecoder().decode(ArrayResponse<Response>.self, from: result.get())
}
}
और अंत में, आपको "BrandRequest" बनाने का एक तरीका चाहिए (लेकिन वास्तव में Request<Brand>
):
struct Brand: Codable {
var brandID: Int
var brandName: String?
}
// You want "BrandRequest", but that's just a particular URLRequest for Request<Brand>.
// I'm going to make something up for the API:
extension Request where Response == Brand {
init(brandName: String) {
self.urlRequest = URLRequest(url: URL(string: "https://example.com/api/v1/brands/(\brandName)")!)
}
}
उस ने कहा, मैं शायद इसे समायोजित कर दूंगा और अलग-अलग Request
एक्सटेंशन बनाउंगा जो अनुरोध के आधार पर सही डिकोडर (तत्व बनाम सरणी) संलग्न करता है। आपके प्रोटोकॉल के आधार पर वर्तमान डिज़ाइन, कॉल करने वाले को डिकोड समय पर यह तय करने के लिए मजबूर करता है कि क्या एक या अधिक तत्व हैं, लेकिन यह तब पता चलता है जब अनुरोध बनाया जाता है। तो मैं शायद इन पंक्तियों के साथ और अधिक अनुरोध बनाउंगा, और Response
स्पष्ट रूप से ArrayResponse
बनाउंगा:
struct Request<Response: Codable> {
// You need some way to fetch this, so I'm going to assume there's an URLRequest
// hiding in here.
let urlRequest: URLRequest
let decoder: (Result<Data, APIError>) throws -> Response
}
(और फिर init
में उपयुक्त डिकोडर असाइन करें)
आपके द्वारा लिंक किए गए कोड को देखते हुए, हाँ, यह वर्ग विरासत को फिर से बनाने के लिए प्रोटोकॉल का उपयोग करने का एक बहुत अच्छा उदाहरण है। APIRequest एक्सटेंशन सामान्य एल्गोरिदम को लागू करने के बजाय डिफ़ॉल्ट कार्यान्वयन बनाने के बारे में है, और यह आमतौर पर "विरासत और ओवरराइड" OOP मानसिकता का सुझाव देता है। APIRequest के अनुरूप अलग-अलग structs के समूह के बजाय, मुझे लगता है कि यह एक APIRequest जेनेरिक स्ट्रक्चर (जैसा ऊपर वर्णित है) के रूप में बेहतर काम करेगा।
लेकिन आप अभी भी सभी मूल कोड को फिर से लिखे बिना वहां पहुंच सकते हैं। उदाहरण के लिए, आप एक सामान्य "सरणी" मैपिंग बना सकते हैं:
struct ArrayRequest<Element: Codable>: APIRequest {
typealias ResponseType = [Element]
typealias ElementType = Element
}
typealias BrandsRequest = ArrayRequest<Brand>
और निश्चित रूप से आप इसे एक परत ऊपर धकेल सकते हैं:
struct ElementRequest<Element: Codable>: APIRequest {
typealias ResponseType = Element
typealias ElementType = Element
}
typealias BrandRequest = ElementRequest<Brand>
और आपके सभी मौजूदा APIRequest सामान अभी भी काम करते हैं, लेकिन आपका सिंटैक्स बहुत आसान हो सकता है (और टाइपियालेस बनाने की कोई वास्तविक आवश्यकता नहीं है; ElementRequest<Brand>
शायद अपने आप ठीक है)।
संबंधित सवाल
नए सवाल
arrays
एक सरणी एक आदेशित रैखिक डेटा संरचना है जिसमें तत्वों (मूल्यों, चर, या संदर्भों) का एक संग्रह होता है, प्रत्येक को एक या अधिक अनुक्रमित द्वारा पहचाना जाता है। जब सरणियों के विशिष्ट प्रकारों के बारे में पूछा जाता है, तो इसके बजाय इन संबंधित टैगों का उपयोग करें: [वेक्टर], [सरणी सूची], [मैट्रिक्स]। इस टैग का उपयोग करते समय, एक सवाल जो प्रोग्रामिंग भाषा के लिए विशिष्ट होता है, उस प्रश्न को उस प्रोग्रामिंग भाषा के साथ टैग करें जिसका उपयोग किया जा रहा है।