আপনি যদি iOS ডেভেলপার হতে চান, তবে কিছু মৌলিক স্কিল জানা প্রয়োজন। প্রথমে, টেবিল ভিউ তৈরি করা জানতে হবে। দ্বিতীয়ে, সেসব টেবিল ভিউ ডেটা দিয়ে পপুলেট করা জানতে হবে। তৃতীয়ে, API থেকে ডেটা ফেচ করে সেটি টেবিল ভিউতে ব্যবহার করা পারলে খুব ভালো।
এই আর্টিকেলে আমরা তৃতীয় পয়েন্টটি কভার করব। Swift 4-এ Codable চালু হয়ে যাওয়ায় API কল করা অনেক সহজ হয়েছে। এর আগে অধিকাংশ লোক Alamofire এবং SwiftyJson-এর মতো পড ব্যবহার করত (এখানে ক্লিক করে জানতে পারেন)। এখন Swift-এর নিজস্ব পদ্ধতি বক্সের বাইরে থেকে অনেক ভালো, তাই পড ডাউনলোড করার কোনো কারণ নেই।
চলুন API কল করতে ব্যবহৃত কিছু বিল্ডিং ব্লক নিয়ে আলোচনা করি। এগুলো বুঝতে গুরুত্বপূর্ণ।
- কম্পলিশন হ্যান্ডলার
URLSessionDispatchQueue- রিটেইন সাইকেল
শেষে সবকुछ একসাথে করব। আমি ওপেন সোর্স Star Wars API ব্যবহার করে এই প্রজেক্টটি বানাব। আমার পূর্ণ প্রজেক্ট কোড GitHub-এ পাওয়া যাবে।
ডিসক্লেমার: আমি কোডিংয়ে নতুন এবং অধিকাংশত স্ব-শিক্ষিত। কোনো ধারণা ভুল উপস্থাপন করলে ক্ষমা চাই।
কম্পলিশন হ্যান্ডলার

ফ্রেন্ডস সিরিজের সেই এপিসোডটি মনে আছে যেখানে ফিবি দিন După দিন ফোনে আটকে रहে কাস্টমার সার্ভিসের সাথে কথা বলার জন্য? কল্পনা করুন, কল শুরুতেই একটি সুন্দর ব্যক্তি পিপ বলে: "কল করার জন্য ধন্যবাদ। আমি নিশ্চিতভাবে বলতে পারি না আপনাকে কতক্ষণ অপেক্ষা করতে হবে, কিন্তু আমরা প্রস্তুত হলে আপনাকে ফোন করে দেব।" এটা তত হাস্যজনক হত না, কিন্তু পিপ ফিবির জন্য একটি কম্পলিশন হ্যান্ডলার হিসেবে কাজ করছিল।
আপনি একটি ফাংশনে কম্পলিশন হ্যান্ডলার ব্যবহার করেন যখন আপনি জানেন সে ফাংশনটি সম্পূর্ণ হওয়ায় কিছু সময় লাগবে। আপনি জানেন না কতক্ষণ লাগবে, এবং আপনি আপনার জীবন থামিয়ে রাখতে চান না। তাই আপনি পিপকে অনুরোধ করেন যে সে আপনার কাঁধে হালকা ধक्कা দেবে যখন সে উত্তর দিতে প্রস্তুত। তখন আপনি আপনার কাজ করতে পারেন, বাইরে যেতে পারেন, বই পড়তে পারেন, টিভি দেখতে পারেন। পিপ যখন আপনার কাঁধে ধक्कা দেবে উত্তরদিয়ে, আপনি সেটি নিয়ে কাজ করতে পারবেন।
API কলের সাথে একই ঘটনা ঘটে। আপনি একটি সার্ভারে URL রিকোয়েস্ট পাঠান কিছু ডেটার জন্য। আপনি আশা করেন সার্ভার দ্রুত ডেটা ফেরত দেবে, কিন্তু জানেন না কতক্ষণ লাগবে। ইউজারকে বসে অপেক্ষা করিয়ে রাখার বদলে আপনি কম্পলিশন হ্যান্ডলার ব্যবহার করেন। এর মানে আপনি আপনার অ্যাপকে বলেন অন্য কাজগুলো করতে — যেমন পেজের বাকি অংশ লোড করা।
আপনি কম্পলিশন হ্যান্ডলারকে বলেন যে আপনার কাঙ্খিত তথ্য পাওয়া যাওয়ার সাথে সাথে আপনার অ্যাপকে জানিয়ে দেবে। আপনি নির্দিষ্ট করতে পারেন সে তথ্য কী। তখন যখন আপনার অ্যাপকে ধੱਕা পড়বে, সেটি কম্পলিশন হ্যান্ডলার থেকে তথ্য নিয়ে সেটি ব্যবহার করবে। সাধারণত আপনি টেবিল ভিউ রিলোড করবেন যাতে ডেটা ইউজারের সামনে আসে।
এখানে একটি কম্পলিশন হ্যান্ডলারের উদাহরণ দেওয়া হলো। প্রথম উদাহরণটি হলো API কল সেটআপ করা:
func fetchFilms(completionHandler: @escaping ([Film]) -> Void) { // lotsOfFilms ভেরিয়েবল সেটআপ করুন var lotsOfFilms: [Film] // কিছু কোড দিয়ে API কল করুন // API-র ডেটা ব্যবহার করে lotsOfFilms-এ মান অ্যাসাইন করুন // কম্পলিশন হ্যান্ডলারকে ভেরিয়েবলটি দিন completionHandler(lotsOfFilms) }এখন fetchFilms ফাংশনটি কল করতে চাই। কিছু বিষয় লক্ষ্য করুন:
- ফাংশন কল করার সময়
completionHandlerরেফারেন্স করার প্রয়োজন নেই। শুধু ফাংশন ডিক্লেয়ারেশনের ভেতরেcompletionHandlerরেফারেন্স করা হয়। - কম্পলিশন হ্যান্ডলার আমাদের কিছু ডেটা ফেরত দেবে। উপরের ফাংশন অনুযায়ী, আমরা জানি
[Film]টাইপের ডেটা ফেরত আসবে। ডেটাকে একটি নাম দিতে হবে যাতে সেটি রেফারেন্স করা যায়। নিচে আমিfilmsনাম ব্যবহার করেছি, কিন্তুrandomDataবা অন্য কোনো নামও হতে পারে।
কোডটি নিচের মত হবে:
fetchFilms() { (films) in // কম্পলিশন হ্যান্ডলার যে ডেটা ফেরত দিয়েছে সেটি দিয়ে কিছু করুন print(films) }URLSession
URLSession একটি টিম ম্যানেজারের মতো। ম্যানেজার নিজে কিছু করে না। তার কাজ হলো টিমের সদস্যদের মধ্যে কাজ বাঁটিয়ে দেয়া, এবং তারা কাজটি সম্পন্ন করবে। তার টিম হলো dataTasks। আপনি যেকোনো সময় ডেটা প্রয়োজন হলে, বসকে লিখুন এবং URLSession.shared.dataTask ব্যবহার করুন।
আপনি dataTask-এ বিভিন্ন ধরনের তথ্য দিতে পারেন আপনার লক্ষ্য পূরণের জন্য। dataTask-এ তথ্য দেয়াকে ইনিশিয়ালাইজেশন বলা হয়। আমি URL দিয়ে আমার dataTasks-কে ইনিশিয়ালাইজ করি। dataTasks তাদের ইনিশিয়ালাইজেশনের অংশ হিসেবে কম্পলিশন হ্যান্ডলারও ব্যবহার করে। একটি উদাহরণ:
let url = URL(string: "https://www.swapi.co/api/films") let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in // আপনার কোড এখানে }) task.resume()dataTasks কম্পলিশন হ্যান্ডলার ব্যবহার করে, এবং সেগুলো সবসময় একই ধরনের তিনটি তথ্য ফেরত দেয়: data, response এবং error। আপনি এই টাইপগুলোকে ভিন্ন নাম দিতে পারেন, যেমন (data, res, err) বা (someData, someResponse, someError)। কনভেনশনের জন্য সুস্পষ্ট নাম ব্যবহার করা ভালো।
error দিয়ে শুরু করা যাক। যদি dataTask একটি error ফেরত দেয়, তবে আপনি সেটি অভिज্ঞতা পেতে চাবেন। এর মানে আপনি আপনার কোডকে এরর হ্যান্ডল করার নির্দেশ দিতে পারেন। এছাড়া আপনি ডেটা পড়তে এবং সেটি ব্যবহার করতে চাইবেন না কারণ ডেটা রিটার্ন করতে সমস্যা হয়েছে।
নিচে আমি খুব সহজে এরর হ্যান্ডল করছি — কনসোলে এরর প্রিন্ট করে ফাংশন থেকে রিটার্ন করছি। এরর হ্যান্ডল করার আরও অনেক পদ্ধতি আছে। চিন্তা করুন এই ডেটা আপনার অ্যাপের জন্য কতটুকু মৌলিক। উদাহরণস্বরূপ, আপনার একটি ব্যাংকিং অ্যাপ থাকলে এবং এই API কল ইউজারের ব্যালেন্স দেখাতে হলে, আপনি একটি মডেল প্রেজেন্ট করতে পারেন যা বলে: "দুঃখিত, এই মুহূর্তে আমরা একটি সমস্যায় আছি। পরে আবার চেষ্টা করুন।"
if let error = error { print("Error accessing swapi.co: \\(error)") return }এরপর রেসপন্সটি দেখি। রেসপন্সকে httpResponse-এ কাস্ট করা যায়। তখন স্ট্যাটাস কোড দেখে কিছু সিদ্ধান্ত নেওয়া যায়। উদাহরণস্বরূপ, যদি স্ট্যাটাস কোড ৪০৪ হয়, তবে পেজটি পাওয়া যায়নি।
নিচের কোডে একটি 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 এর জন্য কম্পলিশন হ্যান্ডলার ব্যবহার করিনি। কারণ কম্পলিশন হ্যান্ডলার API থেকে ডেটার জন্য অপেক্ষা করে। যদি কোড ডেটা পার্ট পর্যন্ত না পৌঁছায়, তবে হ্যান্ডলার ইনভোক করার প্রয়োজন নেই।
ডেটার জন্য আমরা JSONDecoder ব্যবহার করছি যাতে ডেটা সুন্দরভাবে পার্স হয়। এটি খুব কার্যকর, কিন্তু এর জন্য একটি মডেল থাকতে হবে। আমাদের মডেলের নাম FilmSummary। যদি JSONDecoder আপনার জন্য নতুন হয়, তবে অনলাইন থেকে জানতে পারেন কিভাবে এটি এবং Codable ব্যবহার করতে হয়। Swift 4 বা তার উপরে এটি Swift 3-এর চেয়ে অনেক সহজ।
নিচের কোডে, আমরা প্রথমে চেক করি ডেটা আছে কিনা। আমরা নিশ্চিত হলেও এটি থাকা উচিত, কারণ কোনো এরর নেই এবং কোনো অস্বাভাবিক HTTP রেসপন্স নেই। দ্বিতীয়ত, আমরা চেক করি যে আমরা আশা অনুযায়ী ডেটা পার্স করতে পারছি কিনা। পার্স করলে ফিল্ম সামারি কম্পলিশন হ্যান্ডলারকে ফেরত দিই। অপ্রত্যাশিতভাবে API থেকে কোনো ডেটা না আসলে ফলব্যাক হিসেবে খালি অ্যারে থাকে।
if let data = data, let filmSummary = try? JSONDecoder().decode(FilmSummary.self, from: data) { completionHandler(filmSummary.results ?? []) }তাহলে API কলের পূর্ণ কোডটি:
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() }রিটেইন সাইকেল
নোট: আমি রিটেইন সাইকেল বুঝতে খুব নতুন! অনলাইন থেকে যা রিসার্চ করেছি তার সারাংশ নিচে।
রিটেইন সাইকেল মেমরি ম্যানেজমেন্টের জন্য বুঝতে গুরুত্বপূর্ণ। মূলত আপনি চান আপনার অ্যাপ আর প্রয়োজন নেই এমন মেমরির টুকরো ক্লিয়ার করে দেয়। আমি অনুমান করি এতে অ্যাপ আরও পারফরম্যান্ট হয়।
Swift আপনাকে এটি অটোমেটিক করতে অনেকভাবে সাহায্য করে। কিন্তু আপনি অনিচ্ছাকৃতভাবেই আপনার অ্যাপে রিটেইন সাইকেল কোড করতে পারেন অনেকভাবে। একটি রিটেইন সাইকেল মানে আপনার অ্যাপ কোনো নির্দিষ্ট কোডের মেমরি সবসময় ধরে রাখবে। সাধারণত এটি ঘটে যখন দুটি জিনিস একে অপরের প্রতি স্ট্রং পয়েন্টার রাখে।
এটি এড়াতে মানুষ প্রায়শই weak ব্যবহার করে। যখন কোডের একপাশ weak হয়, তবে রিটেইন সাইকেল থাকে না এবং অ্যাপ মেমরি রিলিজ করতে পারে।
আমাদের উদ্দেশ্যে, একটি সাধারণ প্যাটার্ন হলো API কল করার সময় [weak self] ব্যবহার করা। এতে নিশ্চিত হয় যে কম্পলিশন হ্যান্ডলার কোড ফেরত দিলে অ্যাপ মেমরি রিলিজ করতে পারবে।
fetchFilms { [weak self] (films) in // এখানে কোড }DispatchQueue
Xcode কোড এক্সিকিউট করার জন্য ভিন্ন থ্রেড ব্যবহার করে প্যারালালি। একাধিক থ্রেডের সুবিধা হলো আপনি একটা জিনিস শেষ হওয়ার জন্য অপেক্ষা না করেই পরবর্তীটা করতে পারেন। আশা করি কম্পলিশন হ্যান্ডলারের সাথে লিঙ্কটি বুঝতে পারছেন।
এই থ্রেডগুলোকে ডিসপ্যাচ কিউও বলা হয়। API কলসহitamente ব্যাকগ্রাউন্ডের একটি কিউতে হ্যান্ডল হয়। একবার আপনি API কল থেকে ডেটা পেলে, সবচেয়ে সম্ভবত আপনি সেটি ইউজারকে দেখাতে চাবেন। তার মানে আপনার টেবিল ভিউ রিফ্রেশ করতে হবে।
টেবিল ভিউ UI-এর অংশ, এবং সকল UI ম্যানিপুলেশন মেইন ডিসপ্যাচ কিউতে করা উচিত। এর মানে আপনার ভিউ কন্ট্রোলার ফাইলে কোথাও, সাধারণত viewDidLoad ফাংশনের অংশ হিসেবে, কিছু কোড থাকবে যা আপনার টেবিল ভিউকে রিফ্রেশ করতে বলে।
আমরা টেবিল ভিউ রিফ্রেশ করতে চাই শুধু তখনযখন API থেকে নতুন ডেটা পাওয়া যাবে। এর মানে আমরা কম্পলিশন হ্যান্ডলার ব্যবহার করব যা আমাদের কাঁধে ধੱਕা দিয়ে বলে দেবে API কলটি শেষ হয়েছে। আমরা সেই ধੱਕা পাওয়া পর্যন্ত অপেক্ষা করব, তারপর টেবিল রিফ্রেশ করব।
কোডটি নিচের মত হবে:
fetchFilms { [weak self] (films) in self.films = films // মেইন ডিসপ্যাচ কিউ ব্যবহার করে টেবিল ভিউ রিলোড করুন DispatchQueue.main.async { tableView.reloadData() } }viewDidLoad vs viewDidAppear
শেষে আপনাকে ডিসাইড করতে হবে আপনার fetchFilms ফাংশনটি কোথায় কল করবেন। এটি একটি ভিউ কন্ট্রোলারের ভেতরে থাকবে যা API থেকে ডেটা ব্যবহার করবে। এর জন্য দুটি স্বাভাবিক জায়গা আছে। একটি হলো viewDidLoad এবং অন্যটি viewDidAppear।
এই দুটি আপনার অ্যাপের ভিন্ন স্টেট। আমার বোঝা অনুযায়ী viewDidLoad সেটি প্রথমবার কল হয় যখন আপনি ভিউটি ফোরগ্রাউন্ডে লোড করেন। viewDidAppear প্রতিবার কল হয় যখন আপনি সেটি ভিউতে ফিরে আসেন, যেমন ব্যাক বাটন চাপায় ভিউতে ফিরে আসার সময়।
আপনি যদি আশা করেন ইউজার সেই ভিউতে নেভিগেট করার মাঝপথে ডেটা পরিবর্তন হতে পারে, তবে আপনি আপনার API কল viewDidAppear-এ রাখতে পারেন। কিন্তু আমি মনে করি প্রায় সব অ্যাপের জন্য viewDidLoad পর্যাপ্ত। অ্যাপল সব API কলের জন্য viewDidAppear রিকমেন্ড করে, কিন্তু আমার মতে সেটি অতিরিক্ত। কল্পনা করুন এটি আপনার অ্যাপকে কম পারফরম্যান্ট করবে কারণ এটি প্রয়োজনের চেয়ে অনেক বেশি API কল করবে।
সব ধাপ একসাথে করা
প্রথম: API কল করার ফাংশনটি লিখুন। উপরে এটি fetchFilms। এতে একটি কম্পলিশন হ্যান্ডলার থাকবে যা আপনার কাঙ্খিত ডেটা ফেরত দেবে। আমার উদাহরণে, কম্পলিশন হ্যান্ডলার ফিল্মের একটি অ্যারে ফেরত দেয়।
দ্বিতীয়: এই ফাংশনটি আপনার ভিউ কন্ট্রোলারে কল করুন। আপনি এখানে করবেন কারণ আপনি API থেকে ডেটা পাওয়ার ভিত্তিতে ভিউ আপডেট করতে চান। আমার উদাহরণে, API ডেটা ফেরত দিলে আমি একটি টেবিল ভিউ রিফ্রেশ করছি।
তৃতীয়: আপনার ভিউ কন্ট্রোলারের কোথায় ফাংশনটি কল করবেন সেটি ডিসাইড করুন। আমার উদাহরণে, আমি viewDidLoad-এ কল করছি।
চতুর্থ: API থেকে আসা ডেটার সাথে কী করবেন সেটি ডিসাইড করুন। আমার উদাহরণে, আমি টেবিল ভিউ রিফ্রেশ করছি।
NetworkManager.swift-এর ভেতরে (এই ফাংশন আপনার ভিউ কন্ট্রোলারেও ডিফাইন করা যেতে পারে, কিন্তু আমি MVVM প্যাটার্ন ব্যবহার করছি):
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() } } } // ভিউ কন্ট্রোলারের অন্য কোড }বাপ রে, আমরা করলাম! ধৈর্য ধরে পড়ার জন্য ধন্যবাদ।