यदि आप आईओएस डेवलपर बनना चाहते हैं, तो कुछ बुनियादी कौशल जानने लायक हैं। सबसे पहले, तालिका दृश्य बनाने से परिचित होना महत्वपूर्ण है। दूसरा, आपको पता होना चाहिए कि उन तालिका दृश्यों को डेटा के साथ कैसे पॉप्युलेट किया जाए। तीसरा, यदि आप किसी API से डेटा प्राप्त कर सकते हैं और अपने तालिका दृश्य में इस डेटा का उपयोग कर सकते हैं तो यह बहुत अच्छा है।
तीसरा बिंदु वह है जिसे हम इस लेख में शामिल करेंगे। Codable
. की शुरुआत के बाद से स्विफ्ट 4 में, एपीआई कॉल करना बहुत आसान है। पहले ज्यादातर लोग अलामोफायर और स्विफ्टीजसन जैसे पॉड्स का इस्तेमाल करते थे (आप इसे यहां कैसे करें, इसके बारे में पढ़ सकते हैं)। अब स्विफ्ट तरीका पहले से कहीं बेहतर है, इसलिए पॉड डाउनलोड करने का कोई कारण नहीं है।
आइए कुछ बिल्डिंग ब्लॉक्स के बारे में जानें जिनका उपयोग अक्सर एपीआई कॉल करने के लिए किया जाता है। हम पहले इन अवधारणाओं को शामिल करेंगे, क्योंकि वे एपीआई कॉल करने के तरीके को समझने के लिए महत्वपूर्ण भाग हैं।
- समापन हैंडलर
URLSession
DispatchQueue
- चक्र बनाए रखें
अंत में हम यह सब एक साथ रखेंगे। मैं इस प्रोजेक्ट को बनाने के लिए ओपन सोर्स स्टार वार्स एपीआई का उपयोग करूंगा। आप मेरा पूरा प्रोजेक्ट कोड GitHub पर देख सकते हैं।
अस्वीकरण चेतावनी:मैं कोडिंग के लिए नया हूं और काफी हद तक स्व-शिक्षित हूं। अगर मैं कुछ अवधारणाओं को गलत तरीके से प्रस्तुत करता हूं तो क्षमा करें।
समापन हैंडलर
फ्रेंड्स का वह एपिसोड याद है जहां ग्राहक सेवा के साथ बात करने के लिए फोबे कई दिनों तक फोन से चिपके रहते हैं? कल्पना कीजिए कि उस फोन कॉल की शुरुआत में, पिप नामक एक प्यारे व्यक्ति ने कहा:"कॉल करने के लिए धन्यवाद। मुझे नहीं पता कि आपको कितने समय तक होल्ड पर इंतजार करना होगा, लेकिन जब हम तैयार होंगे तो मैं आपको वापस कॉल करूंगा। तेरे लिए।" यह उतना मज़ेदार नहीं होता, लेकिन पिप फीब के लिए एक पूर्ण हैंडलर बनने की पेशकश कर रहा है।
जब आप जानते हैं कि फ़ंक्शन को पूरा होने में कुछ समय लगेगा, तो आप किसी फ़ंक्शन में एक पूर्ण हैंडलर का उपयोग करते हैं। आप नहीं जानते कि कब तक, और आप अपने जीवन को समाप्त होने की प्रतीक्षा में विराम नहीं देना चाहते हैं। तो आप पिप से कहें कि जब वह आपको जवाब देने के लिए तैयार हो तो वह आपको कंधे पर थपथपाए। इस तरह आप अपने जीवन के बारे में जा सकते हैं, कुछ काम चला सकते हैं, एक किताब पढ़ सकते हैं और टीवी देख सकते हैं। जब पिप उत्तर के साथ आपके कंधे पर थपकी देता है, तो आप उसका उत्तर ले सकते हैं और उसका उपयोग कर सकते हैं।
एपीआई कॉल के साथ यही होता है। आप एक सर्वर को एक यूआरएल अनुरोध भेजते हैं, उससे कुछ डेटा मांगते हैं। आप उम्मीद करते हैं कि सर्वर जल्दी से डेटा लौटाएगा, लेकिन आप नहीं जानते कि इसमें कितना समय लगेगा। अपने उपयोगकर्ता को सर्वर द्वारा आपको डेटा देने के लिए धैर्यपूर्वक प्रतीक्षा करने के बजाय, आप एक पूर्ण हैंडलर का उपयोग करते हैं। इसका मतलब है कि आप अपने ऐप को बंद होने और बाकी पेज लोड करने जैसे अन्य काम करने के लिए कह सकते हैं।
आप कंप्लीशन हैंडलर से कहते हैं कि जब आपके पास वह जानकारी हो जाए तो वह आपके ऐप को कंधे पर टैप कर दे। आप निर्दिष्ट कर सकते हैं कि वह जानकारी क्या है। इस तरह, जब आपका ऐप कंधे पर टैप किया जाता है, तो यह कंप्लीशन हैंडलर से जानकारी ले सकता है और इसके साथ कुछ कर सकता है। आम तौर पर आप जो करेंगे वह तालिका दृश्य को फिर से लोड करना है ताकि उपयोगकर्ता को डेटा दिखाई दे।
एक पूरा करने वाला हैंडलर कैसा दिखता है इसका एक उदाहरण यहां दिया गया है। पहला उदाहरण एपीआई कॉल को ही सेट कर रहा है:
func fetchFilms(completionHandler: @escaping ([Film]) -> Void) {
// Setup the variable lotsOfFilms
var lotsOfFilms: [Film]
// Call the API with some code
// Using data from the API, assign a value to lotsOfFilms
// Give the completion handler the variable, lotsOfFilms
completionHandler(lotsOfFilms)
}
अब हम फंक्शन को कॉल करना चाहते हैं fetchFilms
. ध्यान देने योग्य कुछ बातें:
- आपको
completionHandler
का संदर्भ देने की आवश्यकता नहीं है जब आप फ़ंक्शन को कॉल करते हैं। केवल एक बार जब आपcompletionHandler
. का संदर्भ देते हैं समारोह घोषणा के अंदर है। - पूर्णता हैंडलर हमें उपयोग करने के लिए कुछ डेटा वापस देगा। हमारे द्वारा ऊपर लिखे गए फ़ंक्शन के आधार पर, हम डेटा की अपेक्षा करना जानते हैं जो कि प्रकार का है
[Film]
. हमें डेटा को नाम देने की आवश्यकता है ताकि हम इसका उल्लेख कर सकें। नीचे मैंfilms
नाम का उपयोग कर रहा हूं , लेकिन यहrandomData
. हो सकता है , या कोई अन्य चर नाम जो मैं चाहूंगा।
कोड कुछ इस तरह दिखेगा:
fetchFilms() { (films) in
// Do something with the data the completion handler returns
print(films)
}
URLSession
URLSession
एक टीम के प्रबंधक की तरह है। प्रबंधक अपने आप कुछ नहीं करता है। उसका काम अपनी टीम के लोगों के साथ काम साझा करना है, और वे काम पूरा कर लेंगे। उनकी टीम हैं dataTasks
. हर बार जब आपको कुछ डेटा की आवश्यकता हो, तो बॉस को लिखें और URLSession.shared.dataTask
. का उपयोग करें .
आप dataTask
दे सकते हैं अपने लक्ष्य को प्राप्त करने में आपकी सहायता के लिए विभिन्न प्रकार की जानकारी। dataTask
को जानकारी देना आरंभीकरण कहा जाता है। मैं अपना dataTasks
initial आद्याक्षर हूं यूआरएल के साथ। dataTasks
अपने आरंभीकरण के भाग के रूप में पूर्ण करने वाले हैंडलर का भी उपयोग करें। यहां एक उदाहरण दिया गया है:
let url = URL(string: "https://www.swapi.co/api/films")
let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
// your code here
})
task.resume()
dataTasks
पूर्णता हैंडलर का उपयोग करें, और वे हमेशा एक ही प्रकार की जानकारी लौटाते हैं:data
, response
और error
. आप इन डेटा प्रकारों को अलग-अलग नाम दे सकते हैं, जैसे (data, res, err)
या (someData, someResponse, someError)
. परंपरा के लिए, नए चर नामों के साथ दुष्ट होने के बजाय कुछ स्पष्ट रहना सबसे अच्छा है।
आइए error
से शुरू करते हैं . अगर dataTask
एक error
देता है , आप इसे पहले से जानना चाहेंगे। इसका मतलब है कि आप त्रुटि को इनायत से संभालने के लिए अपने कोड को निर्देशित कर सकते हैं। इसका मतलब यह भी है कि आप डेटा को पढ़ने और उसके साथ कुछ करने की कोशिश करने से परेशान नहीं होंगे क्योंकि डेटा वापस करने में कोई त्रुटि है।
नीचे मैं कंसोल में एक त्रुटि प्रिंट करके और फ़ंक्शन से बाहर निकलकर वास्तव में त्रुटि को संभाल रहा हूं। यदि आप चाहें तो कई अन्य तरीके हैं जिनसे आप त्रुटि को संभाल सकते हैं। इस बारे में सोचें कि यह डेटा आपके ऐप के लिए कितना मौलिक है। उदाहरण के लिए, यदि आपके पास एक बैंकिंग ऐप है और यह एपीआई कॉल उपयोगकर्ताओं को उनकी शेष राशि दिखाता है, तो आप उपयोगकर्ता को एक मोडल प्रस्तुत करके त्रुटि को संभालना चाहेंगे जो कहता है, "क्षमा करें, हम अभी एक समस्या का सामना कर रहे हैं। कृपया प्रयास करें बाद में फिर से।"
if let error = error {
print("Error accessing swapi.co: /(error)")
return
}
आगे हम प्रतिक्रिया को देखते हैं। आप प्रतिक्रिया को httpResponse
. के रूप में कास्ट कर सकते हैं . इस तरह आप स्थिति कोड देख सकते हैं और कोड के आधार पर कुछ निर्णय ले सकते हैं। उदाहरण के लिए, यदि स्थिति कोड 404 है, तो आप जानते हैं कि पृष्ठ नहीं मिला था।
नीचे दिया गया कोड guard
. का उपयोग करता है यह जांचने के लिए कि दो चीजें मौजूद हैं। यदि दोनों मौजूद हैं, तो यह कोड को guard
. के बाद अगले कथन पर जारी रखने की अनुमति देता है खंड। यदि कोई भी कथन विफल हो जाता है, तो हम फ़ंक्शन से बाहर निकल जाते हैं। यह guard
. का एक सामान्य उपयोग मामला है खंड। आप उम्मीद करते हैं कि गार्ड क्लॉज के बाद का कोड हैप्पी डेज फ्लो होगा (यानी बिना किसी त्रुटि के आसान फ्लो)।
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
print("Error with the response, unexpected status code: \(response)")
return
}
अंत में आप डेटा को ही संभालते हैं। ध्यान दें कि हमने error
. के लिए कंप्लीशन हैंडलर का उपयोग नहीं किया है या response
. ऐसा इसलिए है क्योंकि पूर्णता हैंडलर एपीआई से डेटा की प्रतीक्षा कर रहा है। यदि यह कोड के डेटा भाग तक नहीं पहुंचता है, तो हैंडलर को बुलाने की कोई आवश्यकता नहीं है।
डेटा के लिए, हम JSONDecoder
. का उपयोग कर रहे हैं डेटा को अच्छे तरीके से पार्स करने के लिए। यह बहुत अच्छा है, लेकिन इसके लिए आवश्यक है कि आपने एक मॉडल स्थापित किया हो। हमारे मॉडल को FilmSummary
. कहा जाता है . अगर JSONDecoder
आपके लिए नया है, तो इसका उपयोग कैसे करें और कैसे उपयोग करें Codable
. के लिए ऑनलाइन देखें . यह स्विफ्ट 3 दिनों की तुलना में स्विफ्ट 4 और इसके बाद के संस्करण में वास्तव में सरल है।
नीचे दिए गए कोड में, हम पहले जाँच कर रहे हैं कि डेटा मौजूद है। हमें पूरा यकीन है कि यह मौजूद होना चाहिए, क्योंकि कोई त्रुटि नहीं है और कोई अजीब HTTP प्रतिक्रिया नहीं है। दूसरा, हम जाँचते हैं कि हम प्राप्त होने वाले डेटा को हमारी अपेक्षा के अनुसार पार्स कर सकते हैं। अगर हम कर सकते हैं, तो हम फिल्म सारांश को पूरा करने वाले हैंडलर को वापस कर देते हैं। अगर एपीआई से वापस आने के लिए कोई डेटा नहीं है, तो हमारे पास खाली एरे की फॉल बैक योजना है।
if let data = data,
let filmSummary = try? JSONDecoder().decode(FilmSummary.self, from: data) {
completionHandler(filmSummary.results ?? [])
}
तो एपीआई कॉल के लिए पूरा कोड इस तरह दिखता है:
func fetchFilms(completionHandler: @escaping ([Film]) -> Void) {
let url = URL(string: domainUrlString + "films/")!
let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
if let error = error {
print("Error with fetching films: \(error)")
return
}
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
print("Error with the response, unexpected status code: \(response)")
return
}
if let data = data,
let filmSummary = try? JSONDecoder().decode(FilmSummary.self, from: data) {
completionHandler(filmSummary.results ?? [])
}
})
task.resume()
}
रिटेन साइकिल
एनबी:मैं चक्रों को बनाए रखने को समझने के लिए बेहद नया हूं! मैंने जो ऑनलाइन शोध किया उसका सार यहां दिया गया है।
स्मृति प्रबंधन के लिए समझने के लिए चक्र बनाए रखना महत्वपूर्ण है। मूल रूप से आप चाहते हैं कि आपका ऐप मेमोरी के बिट्स को साफ कर दे, जिसकी अब और आवश्यकता नहीं है। मुझे लगता है कि यह ऐप को और अधिक प्रदर्शनकारी बनाता है।
ऐसे कई तरीके हैं जिनसे स्विफ्ट आपको इसे स्वचालित रूप से करने में मदद करती है। हालांकि ऐसे कई तरीके हैं जिनसे आप गलती से अपने ऐप में साइकिल को कोड बनाए रख सकते हैं। एक रिटेन साइकल का मतलब है कि आपका ऐप हमेशा एक निश्चित कोड के लिए मेमोरी को होल्ड करेगा। आम तौर पर ऐसा तब होता है जब आपके पास दो चीजें होती हैं जो एक दूसरे के लिए मजबूत संकेत देती हैं।
इससे निजात पाने के लिए लोग अक्सर weak
. का उपयोग करते हैं . जब कोड का एक पक्ष weak
हो , आपके पास कोई रिटेन साइकल नहीं है और आपका ऐप मेमोरी को रिलीज़ करने में सक्षम होगा।
हमारे उद्देश्य के लिए, एक सामान्य पैटर्न [weak self]
. का उपयोग करना है एपीआई को कॉल करते समय। यह सुनिश्चित करता है कि एक बार पूरा होने वाला हैंडलर कुछ कोड लौटाता है, तो ऐप मेमोरी को रिलीज़ कर सकता है।
fetchFilms { [weak self] (films) in
// code in here
}
DispatchQueue
एक्सकोड समानांतर में कोड निष्पादित करने के लिए विभिन्न धागे का उपयोग करता है। एकाधिक थ्रेड्स के लाभ का अर्थ है कि आप एक चीज़ के समाप्त होने की प्रतीक्षा में अटके नहीं हैं, इससे पहले कि आप अगली पर जा सकें। उम्मीद है कि आप यहां कंप्लीशन हैंडलर्स के लिंक देखना शुरू कर सकते हैं।
ऐसा लगता है कि इन धागे को प्रेषण कतार भी कहा जाता है। एपीआई कॉल को एक कतार पर संभाला जाता है, आमतौर पर पृष्ठभूमि में एक कतार। एक बार जब आप अपने एपीआई कॉल से डेटा प्राप्त कर लेते हैं, तो सबसे अधिक संभावना है कि आप उस डेटा को उपयोगकर्ता को दिखाना चाहेंगे। इसका मतलब है कि आप अपने टेबल व्यू को रीफ्रेश करना चाहेंगे।
तालिका दृश्य UI का हिस्सा हैं, और सभी UI जोड़तोड़ मुख्य प्रेषण कतार में किए जाने चाहिए। इसका मतलब आपकी व्यू कंट्रोलर फ़ाइल में कहीं है, आमतौर पर viewDidLoad
. के हिस्से के रूप में फ़ंक्शन, आपके पास थोड़ा सा कोड होना चाहिए जो आपके टेबल व्यू को रीफ्रेश करने के लिए कहता है।
एपीआई से कुछ नया डेटा होने के बाद हम केवल टेबल व्यू को रीफ्रेश करना चाहते हैं। इसका मतलब है कि हम एक पूर्ण हैंडलर का उपयोग हमें कंधे पर टैप करने के लिए करेंगे और हमें बताएंगे कि एपीआई कॉल कब समाप्त हो गई है। टेबल को रीफ्रेश करने से पहले हम उस टैप तक प्रतीक्षा करेंगे।
कोड कुछ इस तरह दिखेगा:
fetchFilms { [weak self] (films) in
self.films = films
// Reload the table view using the main dispatch queue
DispatchQueue.main.async {
tableView.reloadData()
}
}
viewDidLoad बनाम viewDidAppear
अंत में आपको यह तय करना होगा कि अपने fetchfilms
. को कहां कॉल करें समारोह। यह एक व्यू कंट्रोलर के अंदर होगा जो एपीआई से डेटा का उपयोग करेगा। दो स्पष्ट स्थान हैं जिनसे आप यह एपीआई कॉल कर सकते हैं। एक viewDidLoad
के अंदर है और दूसरा viewDidAppear
. के अंदर है ।
ये आपके ऐप के लिए दो अलग-अलग राज्य हैं। मेरी समझ viewDidLoad
है पहली बार जब आप उस दृश्य को अग्रभूमि में लोड करते हैं तो उसे कहा जाता है। viewDidAppear
हर बार जब आप उस दृश्य पर वापस आते हैं, उदाहरण के लिए जब आप दृश्य में वापस आने के लिए बैक बटन दबाते हैं तो उसे कॉल किया जाता है।
यदि आप उम्मीद करते हैं कि आपका डेटा उस समय के बीच में बदल जाएगा जब उपयोगकर्ता नेविगेट करेगा और उस दृश्य से, तो आप अपने एपीआई कॉल को viewDidAppear
में डाल सकते हैं। . हालांकि मुझे लगता है कि लगभग सभी ऐप्स के लिए, viewDidLoad
काफी है। Apple viewDidAppear
की अनुशंसा करता है सभी एपीआई कॉल के लिए, लेकिन यह ओवरकिल जैसा लगता है। मुझे लगता है कि यह आपके ऐप को कम प्रदर्शनकारी बना देगा क्योंकि यह कई और एपीआई कॉल कर रहा है जिसकी उसे आवश्यकता है।
सभी चरणों का संयोजन
पहला:एपीआई को कॉल करने वाले फ़ंक्शन को लिखें। ऊपर, यह है fetchFilms
. इसमें एक पूरा करने वाला हैंडलर होगा, जो उस डेटा को वापस कर देगा जिसमें आप रुचि रखते हैं। मेरे उदाहरण में, पूर्णता हैंडलर फिल्मों की एक सरणी देता है।
दूसरा:इस फ़ंक्शन को अपने व्यू कंट्रोलर में कॉल करें। आप इसे यहां इसलिए करते हैं क्योंकि आप एपीआई के डेटा के आधार पर दृश्य को अपडेट करना चाहते हैं। मेरे उदाहरण में, एपीआई द्वारा डेटा लौटाए जाने के बाद, मैं एक टेबल व्यू को रीफ्रेश कर रहा हूं।
तीसरा:तय करें कि आपके व्यू कंट्रोलर में आप फ़ंक्शन को कहां कॉल करना चाहते हैं। मेरे उदाहरण में, मैं इसे viewDidLoad
. में कॉल करता हूं ।
चौथा:तय करें कि एपीआई से डेटा का क्या करना है। मेरे उदाहरण में, मैं एक टेबल व्यू को रीफ्रेश कर रहा हूं।
अंदर NetworkManager.swift
(यदि आप चाहें तो इस फ़ंक्शन को आपके व्यू कंट्रोलर में परिभाषित किया जा सकता है, लेकिन मैं एमवीवीएम पैटर्न का उपयोग कर रहा हूं)।
func fetchFilms(completionHandler: @escaping ([Film]) -> Void) {
let url = URL(string: domainUrlString + "films/")!
let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
if let error = error {
print("Error with fetching films: \(error)")
return
}
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
print("Error with the response, unexpected status code: \(response)")
return
}
if let data = data,
let filmSummary = try? JSONDecoder().decode(FilmSummary.self, from: data) {
completionHandler(filmSummary.results ?? [])
}
})
task.resume()
}
अंदर FilmsViewController.swift
:
final class FilmsViewController: UIViewController {
private var films: [Film]?
override func viewDidLoad() {
super.viewDidLoad()
NetworkManager().fetchFilms { [weak self] (films) in
self?.films = films
DispatchQueue.main.async {
self?.tableView.reloadData()
}
}
}
// other code for the view controller
}
हे भगवान, हमने इसे बनाया! मेरे साथ बने रहने के लिए धन्यवाद।