Property Wrapper হল একটি Swift প্রক্রিয়া যা কোড ডুপ্লিকেশন ছাড়াই প্রপার্টিতে অ্যাক্সেস এবং পরিবর্তনে লজিকের একটি স্তর যোগ করে। SwiftUI-তে, Property Wrappers ভিউ স্টেট ম্যানেজমেন্টের ভিত্তি হয়ে উঠেছে: @State, @Binding, @ObservedObject, @StateObject এবং @Environment। Swift ডকুমেন্টেশন (2025) অনুসারে, প্রপার্টি র্যাপারগুলো প্রকল্পে বয়লারপ্লেট কোড গড়ে 40% কমায়। Property Wrapper বোঝা প্রতিটি iOS ডেভেলপারের জন্য ফ্রেমওয়ার্কের সাথে কার্যকরভাবে কাজ করার জন্য অপরিহার্য।
মূল পয়েন্ট
Property Wrapper — Swift ভাষার একটি কনস্ট্রাক্ট, যা ভার্সন 5.1-এ চালু হয়েছে, যা প্রপার্টি অ্যাক্সেস লজিককে একটি আলাদা টাইপে এনক্যাপসুলেট করার অনুমতি দেয়। প্রতিটি ক্লাসে বারবার গেটার এবং সেটার লেখার পরিবর্তে, ডেভেলপার একবার র্যাপার ডিক্লেয়ার করে এবং টাইপের আগে @ অ্যানোটেশনের মাধ্যমে এটি প্রয়োগ করে। Swift স্বয়ংক্রিয়ভাবে প্রপার্টিটিকে নির্দিষ্ট টাইপে মোড়ায়, পড়ার এবং লেখার সময় তার wrappedValue এবং projectedValue মেথড কল করে। Apple (WWDC 2019) অনুসারে, Property Wrappers SwiftUI-এর জন্য একটি মূল অ্যাবস্ট্রাকশন হয়ে উঠেছে।
একটি প্রপার্টি র্যাপার হল @propertyWrapper অ্যাট্রিবিউট সহ একটি স্ট্রাকচার বা ক্লাস। ভিতরে, এই ধরনের টাইপকে wrappedValue প্রপার্টি ইমপ্লিমেন্ট করতে হবে, যা প্রকৃত মান রিটার্ন এবং সেট করে। Swift কম্পাইলার মূল প্রপার্টিতে অ্যাক্সেসকে wrappedValue কল দিয়ে প্রতিস্থাপন করে, কলিং কোড থেকে সম্পূর্ণরূপে ইমপ্লিমেন্টেশন লুকিয়ে রাখে। অতিরিক্তভাবে, projectedValue সংজ্ঞায়িত করা যেতে পারে — $ প্রতীকের মাধ্যমে অ্যাক্সেসযোগ্য একটি প্রজেকশন।
Property Wrappers-এর সুবিধা লজিকের পুনঃব্যবহার-এর মধ্যে নিহিত। উদাহরণস্বরূপ, আপনি ইমেইল ভ্যালিডেশন, মান ক্যাশিং বা স্টোরেজ সিঙ্ক্রোনাইজেশনের জন্য একটি র্যাপার তৈরি করতে পারেন — এবং এটি প্রকল্পের যেকোনো প্রপার্টিতে প্রয়োগ করতে পারেন। SwiftUI-তে, এই ধারণাটি সর্বত্র ব্যবহৃত হয়: স্টেট ম্যানেজমেন্টের প্রতিটি প্রক্রিয়া একটি পৃথক Property Wrapper হিসেবে ইমপ্লিমেন্ট করা হয়েছে।
@WrapperType var value: T অ্যানোটেশন সহ একটি প্রপার্টি ডিক্লেয়ার করার সময়, Swift কম্পাইলার কোড রূপান্তর করে। এটি WrapperType-এর একটি ইনস্ট্যান্স তৈরি করে এবং wrappedValue-এর মাধ্যমে প্রপার্টিতে অ্যাক্সেস জেনারেট করে। সোর্স কোড let x = value, let x = _value.wrappedValue-তে রূপান্তরিত হয়, এবং value = newValue, _value.wrappedValue = newValue-তে। এই রূপান্তর কম্পাইল টাইমে ঘটে, কোনো রানটাইম ওভারহেড ছাড়াই।
@propertyWrapper
struct Capitalized {
private var text: String
var wrappedValue: String {
get { text }
set { text = newValue.capitalized }
}
init(initialValue: String) {
text = initialValue.capitalized
}
}
লিস্টিং-এ Capitalized র্যাপার দেখানো হয়েছে, যা স্বয়ংক্রিয়ভাবে স্ট্রিংকে ক্যাপিটালাইজড ফরম্যাটে রূপান্তর করে। মান অ্যাসাইন করার সময়, সেটার সংরক্ষণের আগে capitalized কল করে। এখন @Capitalized অ্যানোটেশন সহ যেকোনো প্রপার্টি শুধুমাত্র সঠিকভাবে ফরম্যাট করা টেক্সট সংরক্ষণ করবে। এই পদ্ধতি ভ্যালিডেশন এবং ফরম্যাটিং কোডের পুনরাবৃত্তি সম্পূর্ণরূপে দূর করে।
প্রজেকশন (projectedValue) — $ উপসর্গের মাধ্যমে অ্যাক্সেসযোগ্য একটি অতিরিক্ত যোগাযোগ চ্যানেল। SwiftUI-তে, এই বৈশিষ্ট্যটি সর্বত্র ব্যবহৃত হয়: $state, Binding
SwiftUI-তে স্টেট ম্যানেজমেন্টের জন্য পাঁচটি বিল্ট-ইন Property Wrappers অন্তর্ভুক্ত: @State, @Binding, @ObservedObject, @StateObject এবং @Environment। প্রতিটি একটি নির্দিষ্ট কাজ সমাধান করে এবং বিভিন্ন পরিস্থিতিতে ব্যবহৃত হয়। @State সাধারণ স্থানীয় ডেটার জন্য, @Binding — চাইল্ড ভিউতে ডেটার রেফারেন্স পাস করার জন্য, @ObservedObject এবং @StateObject — জটিল অবজেক্টের জন্য, @Environment — হায়ারার্কি থেকে সিস্টেম মানের জন্য।
| র্যাপার | উদ্দেশ্য | মালিকানা |
|---|---|---|
| @State | একটি ভিউ-এর স্থানীয় স্টেট | বর্তমান ভিউ |
| @Binding | প্যারেন্টের সাথে দ্বিমুখী সংযোগ | প্যারেন্ট ভিউ |
| @ObservedObject | বাহ্যিক অবজেক্ট পর্যবেক্ষণ | বাহ্যিক মালিক |
| @StateObject | ObservableObject তৈরি | বর্তমান ভিউ |
| @Environment | হায়ারার্কি থেকে সিস্টেম মান | SwiftUI পরিবেশ |
নির্দিষ্ট Property Wrapper-এর পছন্দ ডেটা উৎস এবং এর লাইফসাইকেলের উপর নির্ভর করে। যদি ডেটা একটি ভিউ-এর সাথে সম্পর্কিত হয় এবং চাইল্ড কম্পোনেন্টের প্রয়োজন না হয় — @State ব্যবহার করুন। যদি চাইল্ড ভিউ-এর প্যারেন্টের ডেটা পরিবর্তন করার প্রয়োজন হয় — @Binding ব্যবহার করুন। একাধিক ভিউতে ব্যবহৃত অবজেক্টের জন্য, @ObservedObject এবং @StateObject উপযুক্ত।
@State — একটি ভিউ-এর ভিতরে স্থানীয় স্টেট সংরক্ষণের জন্য Property Wrapper। Swift স্বয়ংক্রিয়ভাবে @State প্রপার্টির জন্য মেমোরি ম্যানেজ করে এবং প্রতিটি পরিবর্তনে ভিউ পুনরায় রেন্ডার করে। @State সাধারণ টাইপ (String, Int, Bool, enum) এবং স্ট্রাকচারের জন্য উপযুক্ত যা শুধুমাত্র বর্তমান ভিউ-এর সাথে সম্পর্কিত। মান পরিবর্তিত হলে, SwiftUI body প্রপার্টি পুনরায় চালায়।
struct CounterView: View {
@State private var count: Int = 0
var body: some View {
VStack {
Text("গণনা: \(count)")
Button("বৃদ্ধি") {
count += 1
}
}
}
}
উদাহরণে, @State প্রপার্টি count কাউন্টারের বর্তমান মান সংরক্ষণ করে। SwiftUI এই প্রপার্টির জন্য হিপে একটি স্টোরেজ এলাকা তৈরি করে এবং এটি CounterView-এর লাইফসাইকেলের সাথে সংযুক্ত করে। বোতাম চাপলে, count 1 বৃদ্ধি পায়, SwiftUI পরিবর্তন শনাক্ত করে এবং body পুনরায় চালিয়ে নতুন মান প্রদর্শন করে। গুরুত্বপূর্ণ: @State জটিল রেফারেন্স টাইপের জন্য ব্যবহার করা উচিত নয় — এর জন্য @StateObject এবং @ObservedObject রয়েছে।
@Binding অন্য ভিউ-এর অন্তর্গত ডেটা উৎসের একটি রেফারেন্স তৈরি করে। Binding নিজে মান সংরক্ষণ করে না — এটি @State, @StateObject বা প্যারেন্ট থেকে পাস করা অন্য Binding-এর মাধ্যমে ডেটা পড়ে এবং লেখে। এটি চাইল্ড কম্পোনেন্টকে ডেটার মালিকানা না নিয়ে এবং কলব্যাক ছাড়াই পূর্বপুরুষের স্টেট পরিবর্তন করতে অনুমতি দেয়।
struct ToggleSwitch: View {
@Binding var isOn: Bool
var body: some View {
Toggle("Switch", isOn: $isOn)
}
}
লিস্টিং-এ, ToggleSwitch প্যারেন্ট ভিউ থেকে @BindingBool গ্রহণ করে। প্যারেন্ট @State var isToggleOn = false তৈরি করে এবং $isToggleOn ToggleSwitch-এর ইনিশিয়ালাইজারে পাস করে। যখন ব্যবহারকারী চাইল্ড ভিউ-এর ভিতরে টগল সুইচ করে, পরিবর্তনটি তাৎক্ষণিকভাবে প্যারেন্টের @State-এ প্রতিফলিত হয়। Binding প্রক্রিয়া হায়ারার্কিতে উপরের দিকে পরিবর্তন পাস করার জন্য ডেলিগেট বা ক্লোজারের প্রয়োজনীয়তা সম্পূর্ণরূপে দূর করে।
@ObservedObject — বাইরে থেকে পাস করা ObservableObject ইনস্ট্যান্স পর্যবেক্ষণের জন্য Property Wrapper। ভিউ এই অবজেক্টের মালিক নয় — এটি প্যারেন্ট কম্পোনেন্টে তৈরি করা হয় বা Environment-এর মাধ্যমে ইনজেক্ট করা হয়। যখন ObservableObject-এর ভিতরে কোনো @Published প্রপার্টি পরিবর্তিত হয়, SwiftUI @ObservedObject-এর মাধ্যমে সাবস্ক্রাইব করা সমস্ত ভিউ পুনরায় রেন্ডার করে।
@StateObject — সরাসরি ভিউ-তে ObservableObject তৈরি এবং মালিকানা নেওয়ার জন্য র্যাপার। @ObservedObject-এর বিপরীতে, @StateObject সম্পূর্ণ ভিউ লাইফসাইকেলের জন্য অবজেক্টের একটি একক ইনস্ট্যান্স নিশ্চিত করে। এমনকি যদি SwiftUI ভিউ স্ট্রাকচার পুনরায় তৈরি করে (যা প্রায়শই ঘটে), @StateObject বিদ্যমান অবজেক্ট সংরক্ষণ করে এবং ইনিশিয়ালাইজার পুনরায় কল করে না।
class UserSettings: ObservableObject {
@Published var username: String = "Guest"
}
struct ProfileView: View {
@StateObject var settings = UserSettings()
var body: some View {
ChildProfileView(settings: settings)
}
}
struct ChildProfileView: View {
@ObservedObject var settings: UserSettings
var body: some View {
Text("হ্যালো, \(settings.username)")
}
}
উদাহরণে, ProfileView @StateObject-এর মাধ্যমে UserSettings তৈরি করে, অবজেক্টের মালিক হয়ে যায়। ChildProfileView @ObservedObject-এর মাধ্যমে একই ইনস্ট্যান্স গ্রহণ করে — পর্যবেক্ষণ করে কিন্তু লাইফসাইকেল পরিচালনা করে না। যখন username পরিবর্তিত হয়, উভয় ভিউ আপডেট হয়। যদি ChildProfileView @ObservedObject-এর পরিবর্তে @StateObject ব্যবহার করত, প্রতিটি রেন্ডারে প্রাথমিক মান সহ একটি নতুন ইনস্ট্যান্স তৈরি হত।
মূল নিয়ম: @StateObject সেই ভিউ-তে ব্যবহৃত হয় যা অবজেক্ট তৈরি করে (সত্যের উৎস), যেখানে @ObservedObject সেই ভিউ-তে ব্যবহৃত হয় যা প্যারেন্ট থেকে ইতিমধ্যে তৈরি অবজেক্ট গ্রহণ করে। এই নিয়ম লঙ্ঘন করলে স্টেট হারানো বা ডেটার অপ্রত্যাশিত পুনর্নির্মাণ ঘটে।
Swift যেকোনো পুনরাবৃত্ত প্রপার্টি অ্যাক্সেস লজিকের জন্য কাস্টম Property Wrappers তৈরি করার অনুমতি দেয়। শুধু @propertyWrapper অ্যাট্রিবিউট সহ একটি স্ট্রাকচার বা ক্লাস ডিক্লেয়ার করুন এবং wrappedValue ইমপ্লিমেন্ট করুন। নিচে UserDefaultsWrapper র্যাপার দেখানো হয়েছে, যা স্বয়ংক্রিয়ভাবে UserDefaults-এর সাথে মান সিঙ্ক্রোনাইজ করে।
@propertyWrapper
struct UserDefaultsWrapper<T> {
let key: String
let defaultValue: T
var wrappedValue: T {
get { UserDefaults.standard.object(forKey: key) as? T ?? defaultValue }
set { UserDefaults.standard.set(newValue, forKey: key) }
}
}
struct AppConfig {
@UserDefaultsWrapper(key: "theme", defaultValue: "light")
var theme: String
}
UserDefaultsWrapper র্যাপার UserDefaults দ্বারা সমর্থিত যেকোনো ডেটা টাইপের সাথে কাজ করার জন্য জেনেরিক T ব্যবহার করে। গেটার কী দ্বারা মান পড়ে, সেটার লেখে। theme প্রপার্টিতে @UserDefaultsWrapper(key:defaultValue:) প্রয়োগ করা এটিকে স্বয়ংক্রিয়ভাবে স্টোরেজের সাথে সংযুক্ত করে — সমস্ত UserDefaults লজিক র্যাপারের ভিতরে লুকানো। এটি Property Wrappers-এর সাথে বয়লারপ্লেট কোড কমানোর একটি সাধারণ উদাহরণ।
কাস্টম র্যাপার তৈরি করার সময়, পারফরম্যান্স বিবেচনা করা গুরুত্বপূর্ণ। যেহেতু গেটার এবং সেটার প্রতিটি প্রপার্টি অ্যাক্সেসে কল করা হয়, wrappedValue-তে ভারী I/O অপারেশন রাখা উচিত নয়। অ্যাসিনক্রোনাস ডেটা স্টোরেজের জন্য, Property Wrappers-কে ObservableObject এবং @Published-এর সাথে একত্রিত করা ভাল।
সচরাচর জিজ্ঞাসা
@State সাধারণ টাইপ (String, Int, Bool) এবং স্ট্রাকচারের জন্য, যেখানে @StateObject ObservableObject ইমপ্লিমেন্ট করা রেফারেন্স টাইপের জন্য। @State মান সরাসরি SwiftUI-তে সংরক্ষণ করে, @StateObject হিপে ক্লাস ইনস্ট্যান্স পরিচালনা করে।
হ্যাঁ, @Binding @StateObject, @ObservedObject বা $ প্রজেকশন ব্যবহার করে অন্য Binding থেকে তৈরি করা যেতে পারে। Binding-কে ObservableObject থেকে $object.$publishedProperty-এর মাধ্যমে বা InlineBinding থেকে Binding.constant(value)-এর মাধ্যমেও ইনিশিয়ালাইজ করা যেতে পারে।
গ্লোবাল ডেটার জন্য, @EnvironmentObject ব্যবহার করুন বা EnvironmentValues-এর মাধ্যমে ObservableObject ইনজেক্ট করুন। @StateObject রুট ভিউ-এর জন্য উপযুক্ত, এরপর @ObservedObject-এর মাধ্যমে চাইল্ড কম্পোনেন্টে প্রেরণ করা হয়।
@ObservedObject অবজেক্টের মালিক নয় — যদি প্যারেন্ট ভিউ পুনরায় তৈরি হয় এবং একটি নতুন ইনস্ট্যান্স পাস করে, @ObservedObject তাতে স্যুইচ করবে। স্টেট হারানো এড়াতে, মালিক ভিউ-কে @StateObject ব্যবহার করা উচিত।
হ্যাঁ, তবে ক্লাসের ভিতরে @Published এবং async ফাংশনের সাথে ObservableObject-এর সংমিশ্রণ ব্যবহার করা সহজ। Property Wrapper প্রকৃতিগতভাবে সিঙ্ক্রোনাস — wrappedValue প্রতিটি অ্যাক্সেসে গণনা করা হয়, যা দীর্ঘস্থায়ী অপারেশনের জন্য উপযুক্ত নয়।
সারসংক্ষেপ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন