হ্যালো সবাই! এই আর্টিকেলে আমরা শিখব কীভাবে iOS অ্যাপগুলিতে UISearchController ব্যবহার করতে হয়।
আমরা কী তৈরি করব?
আমরা একটি মুভি সার্চ অ্যাপ্লিকেশন তৈরি করব যা TMDB API ব্যবহার করে মুভির তথ্য আনবে এবং ব্যবহারকারীর সার্চ কোয়েরি অনুযায়ী UICollectionView-এ প্রদর্শন করবে।
প্রজেক্ট সেটআপ
Xcode খুলুন এবং একটি নতুন ব্ল্যাঙ্ক iOS অ্যাপ প্রজেক্ট তৈরি করুন — নিশ্চিত করুন যে আপনি UIKit নির্বাচন করেছেন, SwiftUI নয়।
এই অ্যাপে আমরা MVC প্যাটার্ন ব্যবহার করব, তাই প্রজেক্টটি সংগঠিত করার জন্য নিচের গ্রুপগুলো এবং Swift ফাইলগুলো তৈরি করুন:
এখন আপনার Xcode প্রজেক্ট বন্ধ করুন। টার্মিনাল খুলুন এবং আপনার প্রজেক্ট ডিরেক্টরিতে যান। এখানে আমাদের SD WebImage CocoaPods যোগ করতে হবে যাতে মুভি পোস্টার ছবিগুলো অ্যাসিঙ্ক্রোনাসলি ডাউনলোড এবং ক্যাশ করা যায়।
টার্মিনালে নিচের কমান্ডটি টাইপ করুন:
pod init
এখন ডিরেক্টরির কন্টেন্ট লিস্ট করলে দেখবেন একটি নতুন Podfile তৈরি হয়েছে। যেকোনো টেক্সট এডিটরে (এখানে আমি Vim ব্যবহার করেছি) ফাইলটি খুলুন। Podfileটি নিচের ছবির মতো এডিট করুন। সংরক্ষণ করে Podfile বন্ধ করুন।
এখন যেহেতু আমরা SD WebImage নির্দিষ্ট করেছি, নিচের কমান্ড চালাতে ডিপেন্ডেন্সি ইনস্টল করুন:
pod install
আপনি দেখতে পাচ্ছেন, আমরা সফলভাবে আমাদের iOS প্রজেক্টে SD WebImage pod যোগ করেছি। এখন নিচের কমান্ড চালাতে আমাদের প্রজেক্ট Xcode-এ খুলবে।
open PROJECT_NAME.xcworkspace
Xcode খোলার পর, Command+B দবায় প্রজেক্টটি বিল্ড করুন।
UIKit এবং প্রোগ্রাম্যাটিক UI ব্যবহার করে ইউজার ইন্টারফেস ডিজাইন কীভাবে করবেন
আমাদের অ্যাপের তিনটি UI এলিমেন্ট দরকার: সার্চ বার ধারণ করার জন্য নেভিগেশন বার,實際 সার্চের জন্য UISearchBarController, এবং সার্চ রেজাল্ট প্রদর্শনের জন্য UICollectionView।
আপনার SceneDelegate.swift ফাইলটি খুলুন এবং সেশন মেথডের সাথে সংযোগের জন্য নিচের কোডটি যোগ করুন:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let scene = (scene as? UIWindowScene) else { return } window = UIWindow(windowScene: scene) window?.rootViewController=UINavigationController(rootViewController:HomeVC()) window?.makeKeyAndVisible() }
যেহেতু আমরা প্রোগ্রাম্যাটিক UI ব্যবহার করছি, প্রথমে আমাদের রুট ভিউ কন্ট্রোলার উল্লেখ করতে হবে — অর্থাৎ, ব্যবহারকারী অ্যাপ চালু করলে প্রথমে যে স্ক্রিনটি দেখবেন।
এই অ্যাপে আমরা শুধু একটি ভিউ কন্ট্রোলার ব্যবহার করছি, তাই এটিকে UINavigationController-এর ভিতরে র্যাপ করেছি। এটি একটি নেভিগেশন বার প্রদান করে যেখানে আমরা আমাদের UISearchController রাখব।
HomeVC.swift ফাইলটি খুলুন এবং নিচের প্রপার্টিগুলো যোগ করুন:
private var SearchBar: UISearchController = { let sb = UISearchController() sb.searchBar.placeholder = "Enter the movie name" sb.searchBar.searchBarStyle = .minimal return sb }() private var MovieCollectionView: UICollectionView = { let layout = UICollectionViewFlowLayout() layout.scrollDirection = .vertical layout.itemSize = CGSize(width: UIScreen.main.bounds.width/3 - 10, height: 200) let cv = UICollectionView(frame: .zero, collectionViewLayout: layout) cv.register(MovieCell.self, forCellWithReuseIdentifier: MovieCell.ID) return cv }()
প্রথমে আমরা আমাদের UISearchController তৈরি করি এবং এর প্রপার্টিগুলো কনফিগার করি যেমন প্লেসহোল্ডার টেক্সট এবং স্টাইল।
তারপর আমরা একটি UICollectionView তৈরি করি এবং নির্দিষ্ট করি আমাদের কালেকশন ভিউ কোন টাইপের লেআউট ব্যবহার করবে। এখানে এটি UICollectionViewFlowLayout এবং অন্যান্য প্রপার্টি যেমন স্ক্রোল ডায়рекশন, আইটেম সাইজ, এবং একটি কাস্টম CollectionView সেল ক্লাস নির্দিষ্ট করা হয়েছে যা পরে প্রজেক্টে তৈরি করব।
HomeVC ক্লাসের ভিতরে একটি নতুন ফাংশন তৈরি করুন এবং UICollectionView-এর জন্য প্রোগ্রাম্যাটিকলি অটো-লেআউট কনস্ট্রেইন্ট কনফিগার করার জন্য নিচের কোডটি যোগ করুন:
//MARK: - HELPERS func configureUI(){ MovieCollectionView.translatesAutoresizingMaskIntoConstraints = false MovieCollectionView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true MovieCollectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true MovieCollectionView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true MovieCollectionView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true }
প্রথমে আমরা বলি যে অটো রিসাইজিং মাস্ক কনস্ট্রেইন্টে কনভার্ট করার দরকার নেই। তারপর আমরা আমাদের কালেকশন ভিউকে ভিউ কন্ট্রোলারের চারপাশে পিন করি।
viewDidLoad() মেথডের ভিতরে নিচের কোড লাইনগুলো যোগ করুন:
override func viewDidLoad() { super.viewDidLoad() navigationItem.title = "Movie Search" view.backgroundColor = .systemBackground SearchBar.searchResultsUpdater = self navigationItem.searchController = SearchBar view.addSubview(MovieCollectionView) MovieCollectionView.delegate = self MovieCollectionView.dataSource = self configureUI() }
এখানে আমরা প্রথমে আমাদের ভিউ কন্ট্রোলারের জন্য টাইটেল নির্দিষ্ট করি এবং তারপর ব্যাকগ্রাউন্ড কালার যা systemBackground কালার। যদি ডিভাইস লাইট মোডে থাকে তবে সাদা ব্যাকগ্রাউন্ড দেখাবে। ডার্ক মোডে থাকলে গাঢ় রঙের ব্যাকগ্রাউন্ড দেখাবে।
তারপর আমরা করেন্ট ভিউ কন্ট্রোলারকে সার্চ রেজাল্ট আপডেটার হিসেবে সেট করি, তারপর আমাদের SearchController নেভিগেশন বারে যোগ করি, UICollectionView ভিউ কন্ট্রোলারে যোগ করি, এবং ডেলিগেট ও ডেটাসোর্স সেটআপ করি। চূড়ান্তভাবে অটো-লেআউট ব্যবহার করে UICollectionView পিন করি।
HomeVC-এর জন্য একটি এক্সটেনশন তৈরি করুন এবং UISearchResultsUpdating প্রোটোকল এবং এর স্টাব মেথড updateSearchResults ইমপ্লিমেন্ট করুন।
extension HomeVC: UISearchResultsUpdating{ func updateSearchResults(for searchController: UISearchController) { guard let query = searchController.searchBar.text else{return} } } }
updateSearchResults() মেথডটি সার্চ বারে টেক্সট পরিবর্তন হলে বা ব্যবহারকারী কীবোর্ডের সার্চ বাটনে ট্যাপ করলে কল হবে।
এরপর আমাদের কাস্টম UICollectionView সেলটি তৈরি করতে হবে। MovieCell.swift ফাইলে নিচের কোডটি যোগ করুন:
import Foundation import UIKit import SDWebImage class MovieCell: UICollectionViewCell{ static let ID = "MovieCell" private var MoviePosterImageView: UIImageView = { let imageView = UIImageView() imageView.contentMode = .scaleAspectFit // imageView.image = UIImage(systemName: "house") return imageView }() override init(frame: CGRect) { super.init(frame: frame) addSubview(MoviePosterImageView) configureUI() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } extension MovieCell{ func configureUI(){ MoviePosterImageView.translatesAutoresizingMaskIntoConstraints = false MoviePosterImageView.topAnchor.constraint(equalTo: topAnchor).isActive = true MoviePosterImageView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true MoviePosterImageView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true MoviePosterImageView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true } func updateCell(posterURL: String?){ if let posterURL = posterURL { guard let CompleteURL = URL(string: "https://image.tmdb.org/t/p/w500/\(posterURL)") else {return} self.MoviePosterImageView.sd_setImage(with: CompleteURL) } } }
এখানে আমরা UICollectionView ক্লাস সাবক্লাস করে আমাদের কাস্টম কালেকশন ভিউ সেল তৈরি করি এবং init() ফাংশনগুলো ইমপ্লিমেন্ট করি।
মুভি পোস্টার ছবি প্রদর্শনের জন্য একটি UIImageView তৈরি করি এবং তার জন্য অটো-লেআউট কনস্ট্রেইন্ট সেটআপ করি। তারপর একটি ইউজার-ডিফাইনড ফাংশন তৈরি করি যা মুভি পোস্টার URL স্ট্রিং প্যারামিটার হিসেবে নেয় এবং SD WebImage CocoaPod ব্যবহার করে (যেটি আমরা আগে যোগ করেছিলাম) UI থ্রেড/মেইন থ্রেডকে প্রভাবিত না করে অ্যাসিঙ্ক্রোনাসলি ডাউনলোড করে।
আমাদের API সেটআপ কীভাবে করবেন
আগে যাওয়ার পূর্বে, আপনাকে TMDB API-এর জন্য একটি অ্যাকাউন্ট তৈরি করে (এটি ফ্রি) আপনার API কি প্রাপ্ত করতে হবে। আমরা API-এর Movie Search এন্ডপয়েন্ট ব্যবহার করব যা API Key এবং মুভির নাম প্যারামিটার হিসেবে নেয়।
https://api.themoviedb.org/3/search/movie?api_key=API_KEY_HERE&query=batman
আপনি Postman-এ রান করে API রেসপন্স পরীক্ষা করতে পারেন।
API রেসপন্সের জন্য মডেল কীভাবে তৈরি করবেন
এখন আমরা API থেকে JSON রেসপন্স পাই। আমাদের এগুলো Swift-এ ডিকোড করতে হবে যা Codable প্রোটোকল ইমপ্লিমেন্ট করে একটি মডেল স্ট্রাক্ট তৈরি করে করা যায়।
JSON থেকে Swift মডেল স্ট্রাক্ট সহজে জেনারেট করা যায় JSON to Swift ওয়েবসাইটগুলো ব্যবহার করে। এখানে API রেসপন্সের জন্য মডেল কোডটি দেওয়া হলো — আপনি কপি করে Model.swift ফাইলে পেস্ট করতে পারেন:
import Foundation struct TrendingTitleResponse: Codable { let results: [Title] } struct Title: Codable { let id: Int let media_type: String? let original_name: String? let original_title: String? let poster_path: String? let overview: String? let vote_count: Int let release_date: String? let vote_average: Double } struct YoutubeSearchResponse: Codable { let items: [VideoElement] } struct VideoElement: Codable { let id: IdVideoElement } struct IdVideoElement: Codable { let kind: String let videoId: String }
Swift ব্যবহার করে HTTP রিকোয়েস্ট কীভাবে করবেন
এখন আমাদের কিছু Swift কোড লিখতে হবে যাতে HTTP GET রিকোয়েস্ট পারফর্ম করে API-এর JSON রেসপন্স ফেরত দেয়।
Swift একটি URLSession ক্লাস প্রদান করে যা থার্ড-পার্টি লাইব্রেরি যেমন AFNetworking, AlamoFire ইত্যাদি ছাড়া নেটওয়ার্কিং কোড লেখা সহজ করে দেয়।
APIService.swift খুলুন এবং নিচের কোডটি যোগ করুন:
import Foundation class APIService{ static var shared = APIService() let session = URLSession(configuration: .default) func getMovies(for Query: String,completion:@escaping([Title]?,Error?)->Void){ guard let FormatedQuery = Query.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) else{return} guard let SEARCH_URL = URL(string: "https://api.themoviedb.org/3/search/movie?api_key=API_KEY_HERE&query=\(FormatedQuery)") else {print("INVALID") return} let task = session.dataTask(with: SEARCH_URL) { data, response, error in if let error = error { print(error.localizedDescription) completion(nil,error) } if let data = data { do{ let decodedData = try JSONDecoder().decode(TrendingTitleResponse.self, from: data) // print(decodedData) completion(decodedData.results,nil) } catch{ print(error) } } } task.resume() } }
এখানে আমরা APIService নামে একটি ক্লাস তৈরি করেছি সিংলটন প্যাটার্ন দিয়ে যাতে এই ক্লাসের একটি ইনস্ট্যান্স ক্লাসের স্ট্যাটিক মেম্বার হিসেবে থাকে। তারপর ডিফল্ট কনফিগারেশন দিয়ে আমাদের নেটওয়ার্কিং টাস্কের জন্য একটি সেশন তৈরি করেছি, এবং getMovies() নামে একটি ইউজার-ডিফাইনড মেথড।
তারপর আমাদের নেটওয়ার্কিং টাস্ক তৈরি করেছি — এই কেসে আমাদের HTTP GET রিকোয়েস্ট পারফর্ম করতে হবে যা URLSession ক্লাসের dataTask() মেথড দিয়ে করা যায়। এটি URL প্যারামিটার হিসেবে নেয় এবং একটি কমপ্লিশন হ্যান্ডলার দেয় যাতে API থেকে ফেরত আসা ডেটা, কোনো এরর থাকলে সেটি, এবং HTTP রেসপন্স ইনফরমেশন যেমন স্ট্যাটাস কোড এবং তাদের সংশ্লিষ্ট মেসেজ থাকে।
যদি কোনো এরর থাকে, তবে সেটি প্রিন্ট করে ফাংশন থেকে এরর ডেটা দিয়ে বের হয়ে যায়। না হলে, আমাদের Swift মডেল অনুযায়ী JSON ডেটা ডিকোড করি এবং ডিকোডেড ডেটা দিয়ে ফাংশন থেকে বের হয়ে যায়।
UICollectionView-এ সার্চ রেজাল্ট কীভাবে প্রদর্শন করবেন
HomeVC.swift-এ, একটি প্রাইভেট প্রপার্টি তৈরি করুন যা Title অবজেক্টের একটি অ্যারে। এগুলো API থেকে ফেরত আসা প্রতিটি মুভির ইনফরমেশন ধারণ করবে।
private var Movies = [Title]()
HomeVC.swift-এ HomeVC ক্লাসের জন্য একটি এক্সটেনশন তৈরি করুন এবং UICollectionViewDelegate এবং UICollectionViewDataSource প্রোটোকল ইমপ্লিমেন্ট করুন। তারপর numberOfItemsInSection (যে API থেকে ফেরত আসা মুভির সংখ্যা) এবং cellForItemAt (যে আসলে API রেসপন্স দিয়ে সেল পপুলেট করে, যেমন পোস্টার ছবি ডাউনলোড এবং সেট করা) ইমপ্লিমেন্ট করুন।
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return Movies.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MovieCell.ID, for: indexPath) as? MovieCell{ // cell.backgroundColor = .systemBackground cell.updateCell(posterURL: Movies[indexPath.row].poster_path) return cell } return UICollectionViewCell() }
চূড়ান্তভাবে আমাদের আসল API কলটি করতে হবে যা আমরা আগে ইমপ্লিমেন্ট করা updateSearchResults() ডেলিগেট মেথডের ভিতরে করব। সেই মেথডের ভিতরে নিচের কোডটি যোগ করুন:
func updateSearchResults(for searchController: UISearchController) { guard let query = searchController.searchBar.text else{return} APIService.shared.getMovies(for:query.trimmingCharacters(in: .whitespaces)) { titles, error in if let titles = titles { self.Movies = titles DispatchQueue.main.async { self.MovieCollectionView.reloadData() } } } }
এখানে, যখনই ব্যবহারকারী সার্চ বারে টাইপ করে বা সার্চ বাটন চাপে, আমরা HTTP GET রিকোয়েস্ট করি মুভি আনতে (সার্চ বারে ইনপুট দেওয়া নামের ভিত্তিতে)। তারপর কালেকশন ভিউ রিলোড করি যা কালেকশন ভিউ সেলগুলো মুভি পোস্টার দিয়ে আপডেট করে।
দ্রষ্টব্য: আমাদের এটি মেইন থ্রেড/UI থ্রেডে করতে হবে, কারণ ডিফল্টলি iOS অটোম্যাটিকলি HTTP রিকোয়েস্ট ব্যাকগ্রাউন্ড থ্রেডে করে। এর মানে হচ্ছে আমাদের UI এলিমেন্ট আপডেট করতে UI/মেইন থ্রেড ব্যবহার করতে হবে।
এখন সিমুলেটরে আপনার অ্যাপ রান করুন ফলাফল দেখতে:
অভিনন্দন! আপনি iOS অ্যাপে UISearchController ব্যবহার করা শিখেছেন।