expect/actual ایک Kotlin Multiplatform میکانزم ہے جو مشترک کوڈ میں پلیٹ فارم پر منحصر API کا اعلان کرنے کی اجازت دیتا ہے۔ expect کلیڈ ورڈ commonMain میں ایک فنکشن، کلاس یا پراپرٹی کا معاہدہ تیار کرتا ہے، جبکہ actual کلیڈ ورڈ ہر پلیٹ فارم کے لیے ایک ٹھوس نفاذ فراہم کرتا ہے۔ کامپائلر تصدیق کرتا ہے کہ تمام ہدف پلیٹ فارموں پر ہر expect اعلان کے مطابق actual نفاذ موجود ہے۔ JetBrains, 2025 کے مطابق، یہ میکانزم 80% KMM مناصبوں میں پلیٹ فارم کاروباری منطق کے نفاذ کے لیے استعمال ہوتا ہے۔
اہم نکات
expect/actual پلیٹ فارم پر مبنی پروگرامنگ کے نفاذ کے لیے Kotlin Multiplatform کا ایک اعلانی میکانزم ہے۔ یہ مشترک ماڈیول (expect) میں ایک بار API کی وضاحت کرنے اور اسے ہر پلیٹ فارم (actual) کے لیے علیحدہ نفاذ کرنے کی اجازت دیتا ہے۔ انٹرفیسز کے برعکس، expect/actual ورچوئل کالز نہیں بناتا — کامپائلر کامپائل ٹائم پر expect اور actual کے اعلانوں کو منسلک کرتا ہے، جو ڈائنامک ڈسپیچ کے اورہیڈ کو ختم کرتا ہے۔
expect/actual کی تاریخ 2017 میں Kotlin Multiplatform کے متعارف کے ساتھ شروع ہوئی۔ شروع میں، اس میکانزم کو expect/actual declarations کہا جاتا تھا اور یہ تجرباتی تھا۔ Kotlin 1.2 میں expect اینوٹیشنز شامل کیے گئے، اور Kotlin 1.3 میں expect/actual کلاسز اور فنکشنز کے لیے مستحکم ہو گا۔ وقت گزرنے کے ساتھ میکانزم وسیع ہوا: Kotlin 1.6 نے companion آبجیکٹس کے لیے expect/actual کی حمایت شامل کی، Kotlin 1.7 نے enum کلاسز کے لیے، اور Kotlin 2.0 نے typealias کے لیے۔
expect/actual کی کلیڈ خصوصیت کامپائل ٹائم کی حفاظت ہے۔ اگر کوئی ڈیولپر commonMain میں expect اعلان شامل کرتا ہے لیکن iOS کے لیے actual نفاذ فراہم کرنا بھول جاتا ہے، تو کامپائلر ایک غلطی پیدا کرے گا۔ یہ ریفلیکشن یا پلیٹ فارم کوڈ کے ڈائنامک لوڈنگ استعمال کرنے والے طریقوں میں ام رن ٹائم کی ناکامیوں کو روکتا ہے۔
expect/actual کا میکانزم source set کی سطح پر کام کرتا ہے — Kotlin Multiplatform کا ماڈیول نظام۔ تمام پلیٹ فارموں کے لیے دستیاب مشترک کوڈ commonMain source set میں موجود ہے۔ پلیٹ فارم پر منحصر کوڈ iosMain, androidMain, macosMain وغیرہ میں موجود ہے۔ commonMain میں expect کلیڈ ورڈ ایک API کا اعلان کرتا ہے، جبکہ پلیٹ فارم source set میں actual کلیڈ ورڈ نفاذ فراہم کرتا ہے۔ کامپائلر کوڈ جینریشن کے مرحلے پر انہیں منسلک کرتا ہے، ہدف پلیٹ فارم کے لیے expect فنکشن کال کو مطابق actual نفاذ سے بدل دیتا ہے۔
ایک معمولی KMM منصوبے میں source set کا درجہ بندی اس طرح ہے: commonMain میں expect اعلانیات ہوتی ہیں، iosMain اور androidMain میں actual نفاذ ہوتا ہے۔ iOS کے لیے کامپائل کرتے وقت iosMain سے actual استعمال ہوتا ہے، Android کے لیے کامپائل کرتے وقت androidMain سے actual استعمال ہوتا ہے۔ Source set درمیانی ہو سکتے ہیں (مثلاً، ایک مخصوص آرکیٹیکچر کے لیے iosArm64Main)، جو مختلف آلات کے لیے نفاذوں کو بہتر کرنے کی اجازت دیتا ہے۔
// commonMain — expect declaration
expect fun getPlatformName(): String
// androidMain — actual for Android
actual fun getPlatformName(): String = "Android"
// iosMain — actual for iOS
actual fun getPlatformName(): String = "iOS"
Kotlin کامپائلر expect/actual کے ساتھ کام کرتے وقت کئی شرائط کی جانچ کرتا ہے۔ ہر فعال پلیٹ فارم کے لیے ہر expect اعلان کا actual نفاذ ہونا چاہیے۔ actual اعلان کے دستخت expect دستخت سے مطابق ہونا چاہیے (@OptionalExpectation اینوٹیشن اس شرط کو نرم کر سکتا ہے)۔ رسائی ماڈیفائرز، واپسی قسم اور پیرامیٹرز یکساں ہونے چاہییں۔ کامپائلر expect اور actual اعلانوں کے درمیان چکری انحصاریات کی غیر موجودگی بھی چیک کرتا ہے۔
expect/actual کئی قسموں کے اعلانوں کی حمایت کرتا ہے۔ سب سے زیادہ استعمال ہونے والے ہیں پلیٹ فارم کے آپریشنز کے لیے expect/actual فنکشنز، مقامی نفاذ کی ضرورت والی آبجیکٹس کے لیے expect/actual کلاسز، اور مستقلات اور ترتیبات کے لیے expect/actual پراپرٹیز۔ ہر قسم کے اپنے استعمال کے قواعد اور حدود ہیں۔
Expect/actual فنکشنز سب سے سادہ اور مامولی قسم ہیں۔ وہ وقت حاصل کرنا، فائلیں پڑھنا یا HTTP درخواستیں بھیجنا جیسے پلیٹ فارم API کو کال کرنے کے لیے استعمال ہوتے ہیں۔ Expect/actual کلاسز آبجیکٹس بنانے کے لیے استعمال ہوتے ہیں جو مقامی کوڈ کے ساتھ براہِ راست ایفعال کرتے ہیں (مثلاً، کیمرا، جیولکیشن یا کی سٹوریج تک رسائی کے لیے)۔ Expect/actual پراپرٹیز (val) پلیٹ فارم مستقلات کے لیے موزوں ہیں — OS کا نام، SDK وریشن یا سسٹم ڈائرکٹری کا پاب۔
| اعلان کی قسم | کلیڈ ورڈز | استعمال کی مثال |
|---|---|---|
| فنکشن | expect fun / actual fun | ایک منفرد آلہ شناختی کوڈ حاصل کرنا |
| کلاس | expect class / actual class | SecureStorage تک رسائی (Keychain / EncryptedSharedPreferences) |
| پراپرٹی | expect val / actual val | موجودہ پلیٹ فارم (iOS / Android) |
| Enum کلاس | expect enum / actual enum | دستیاب ایپ اجازتوں کی فہرست |
| Typealias | expect typealias / actual typealias | پلیٹ فارم مخصوص نیٹ ورک جواب کی قسم |
Kotlin کے تمام تعمیرات expect/actual کے ساتھ استعمال نہیں کیئے جا سکتے۔ ایک expect اعلان میں بوڈی نہیں ہو سکتی — صرف ایک دستخت۔ ایک expect کلاس میں پیرامیٹرز کے ساتھ کنسٹرکٹر نہیں ہو سکتا (اسکا ایک خالی پرائمری کنسٹرکٹر ہونا چاہیے)۔ enum expect/actual کے لیے، تمام مستقلات expect اور actual دونوں میں یکساں ہونی چاہییں۔ Expect پراپرٹیز val (وار نہیں) ہونی چاہییں، کیونکہ پلیٹ فارم پراپرٹیز کے لیے مشترک ماڈیول میں حالت محفوظ کرنا بے معنی ہے۔
آئیے سادہ فنکشنز سے لے کر مکمل کلاسز تک expect/actual کی عملی مثالیں دیکھیں۔ بنیادی معاملہ UI میں استعمال کے لیے پلیٹ فارم کا نام حاصل کرنا ہے۔ زیادہ پیچیدہ مثالوں میں مقامی سٹوریج تک رسائی اور پلیٹ فارم تھریڈز کے ساتھ کام شامل ہے۔
// commonMain — expect class for secure storage
expect class PlatformStorage {
fun save(key: String, value: String)
fun get(key: String): String?
fun remove(key: String)
}
// androidMain — actual on Android
actual class PlatformStorage {
private val prefs = AppContext.getSharedPreferences("secure", 0)
actual fun save(key: String, value: String) { prefs.edit().putString(key, value).apply() }
actual fun get(key: String): String? = prefs.getString(key, null)
actual fun remove(key: String) { prefs.edit().remove(key).apply() }
}
اس مثال میں، expect کلاس PlatformStorage ایک سادہ کی-ویلیو سٹوریج کا معاہدہ متعین کرتا ہے۔ Android پر، نفاذ SharedPreferences استعمال کرتا ہے، جبکہ iOS پر Keychain یا NSUserDefaults استعمال کرتا ہے۔ expect/actual کی بدولت، commonMain میں کاروباری منطق پلیٹ فارم نفاذ کے بارے میں جانے بغیر save/get/remove کو کال کرتا ہے۔
// iosMain — actual on iOS with Keychain
actual class PlatformStorage {
actual fun save(key: String, value: String) {
val query = mapOf<String, Any>(
kSecClass to kSecClassGenericPassword,
kSecAttrAccount to key,
kSecValueData to value.encodeToByteArray()
)
SecItemAdd(query, null)
}
actual fun get(key: String): String? {
val query = mapOf<String, Any>(
kSecClass to kSecClassGenericPassword,
kSecAttrAccount to key,
kSecReturnData to true
)
val result = mutableMapOf<String, Any>()
return if (SecItemCopyMatching(query, result) == errSecSuccess)
result[kSecValueData]?.toString()
else null
}
actual fun remove(key: String) {
val query = mapOf<String, Any>(
kSecClass to kSecClassGenericPassword,
kSecAttrAccount to key
)
SecItemDelete(query)
}
}
expect/actual API ڈزائن کرتے وقت، کئی اصولوں پر عمل کرنا چاہیے۔ expect اعلانوں کی تعداد کو کم سے کم رکھیں — جتنا زیادہ مشترک کوڈ، دیکھ بھال اتنا ہی آسان۔ expect/actual کا استعمال صرف ان API کے لیے کریں جو واقعی میں پلیٹ فارموں کے درمیان مختلف ہوں۔ باقی کوڈ کے لیے، فیکٹریز یا ڈیپنڈینسی انجیکشن کے ساتھ انٹرفیسز استعمال کریں، جو ٹیسٹنگ کو آسان بناتا ہے۔
expect اعلانوں کو ایک فائل میں مکس کرنے کے بجائے موضوعی ماڈیولز میں گروپ کرنا بہتر ہے۔ مثلاً، Storage.kt سٹوریج سے متعلق expect اعلانوں کے لیے، Platform.kt OS کے ساتھ کام کرنے والے expect فنکشنز کے لیے، اور Analytics.kt تجزیہ expect کلاسز کے لیے۔ یہ KMM منصوبے کے پلیٹ فارم سطح کی نیویگیشن اور سمجھ کو آسان بناتا ہے۔ ہر actual فائل متبادل source set میں ہونی چاہیے: androidMain, iosMain, desktopMain وغیرہ۔
expect fun کے ساتھ actual fun جہاں actual مشترک کوڈ استعمال کرتا ہے، کے ذریعے پھلی سے طے شدہ نفاذ ایک ام انٹی پیٹرن ہے۔ اگر پلیٹ فارم کا نفاذ پھلی سے مقرر شدہ سے مختلف نہیں ہے، تو expect/actual کی ضرورت نہیں ہے۔ ایسے معاملوں میں، commonMain میں ایک سادہ فنکشن استعمال کریں۔ ساتھ ہی، مامولی getters کے لیے expect/actual سے بچیں — مستقلات کے ساتھ expect val استعمال کریں۔
expect/actual کوڈ کا مناسب چھٹا منصوبے کی خواندگی کے لیے بہت اہم ہے۔ ہر expect/actual ماڈیول کا ایک واحد داخلی نقطہ ہونا چاہیے۔ تنظیم کی مثال: commonMain/kotlin/com/project/platform میں expect اعلانیات ہیں، androidMain/kotlin/com/project/platform میں Android کے لیے actual ہے، iosMain/kotlin/com/project/platform میں iOS کے لیے actual ہے۔ فائل اور پیکیج کے نام expect اور actual کے لیے مطابق ہونے چاہییں، تاکہ ایک ڈیولپر متبادل نفاذ کو جلدی حاصل کر سکے۔
پلیٹ فارم فیکٹری کے ساتھ انٹرفیسز expect/actual کا اهم متبادل ہیں۔ ایک expect کلاس کے بجائے، آپ commonMain میں ایک انٹرفیس اعلان کر سکتے ہیں اور پلیٹ فارم ماڈیولز میں ٹھوس کلاسز بنا سکتے ہیں۔ ایک فیکٹری یا DI کنٹینر رن ٹائم پر صحیح نفاذ فراہم کرتا ہے۔ یہ طریقہ ٹیسٹنگ کے لیے بہتر ہے، کیونکہ انٹرفیس کو ماک کیا جا سکتا ہے۔
ڈیپنڈینسی انجیکشن (Koin, Kodein) ایک زیادہ لچیلا لیکن کم مؤثر طریقہ ہے۔ ایک DI کنٹینر ہر پلیٹ فارم کے لیے علیحدہ ترتیب دیا جاتا ہے اور مشترک کوڈ کو پلیٹ فارم کی انحصاریات فراہم کرتا ہے۔ expect/actual کے برعکس، انجیکشن رن ٹائم پر ہوتا ہے، جو ٹیسٹنگ کے لیے نفاذ کو بدلنے کی اجازت دیتا ہے۔ دوسری جانب، DI ترتیب کی غلطیاں صرف رن ٹائم پر پکڈی جاتی ہیں، کامپائل ٹائم پر نہیں۔
| طریقہ | کامپائل ٹائم جانچ | ٹیسٹنگ لچیلائی | رن ٹائم کا اورہیڈ |
|---|---|---|---|
| expect/actual | مکمل | کم (actual کو ماک نہیں کیا جا سکتا) | صفر (کامپائل ٹائم بینڈنگ) |
| انٹرفیسز + فیکٹری | جزئی | زیادہ (ماک کیا جا سکتا ہے) | حد ادنی (ورچوئل کال) |
| ڈیپنڈینسی انجیکشن | نہیں (رن ٹائم) | زیادہ | درمیانہ (DI پراکسیز) |
expect/actual اور متبادلوں کے درمیان انتخاب سیاق پر منحصر کرتا ہے۔ کارکردگی کے لیے اہم کوڈ (گیم انجنز، ریال ٹائم پروسیسنگ) کے لیے، صفر اورہیڈ کی وجہ سے expect/actual بہتر ہے۔ کاروباری منطق (ریپازٹریز، یوز کیسز) کے لیے، ٹیسٹنگ کو آسان بنانے کے لیے DI کے ساتھ انٹرفیسز استعمال کرنا بہتر ہے۔ ایک مشترک طریقہ — نشیبی پلیٹ فارم کے آپریشنز کے لیے expect/actual اور کاروباری منطق کی پرت کے لیے انٹرفیسز — کی اکثر پیشہ واران KMM مناصبوں میں استعمال ہوتا ہے۔
اکثر پوچے جانے والے سوالات
expect/actual ورچوئل کالز کے بغیر کامپائل ٹائم پر نفاذ کو باندھتا ہے، جبکہ انٹرفیسز رن ٹائم پر باندھتے ہیں۔ expect/actual تمام پلیٹ فارموں کے لیے نفاذ کی ضمانت دیتا ہے، انٹرفیسز کو رن ٹائم جانچ کی ضرورت ہے۔
جی ہاں، expect enum Kotlin 1.7 سے معاون ہے۔ expect اور actual enum دونوں میں تمام مستقلات یکساں ہونی چاہییں۔ مختلف پلیٹ فارموں پر مختلف مستقل قیمتیں ایک کامپائل غلطی ہے۔
کامپائلر ہر اس پلیٹ فارم کے لیے ایک غلطی پیدا کرے گا جہاں actual نفاذ غائب ہے۔ جب تک تمام expect اعلانوں کے لیے متبادل actual نفاذ شامل نہیں کیے جاتے، منصوبہ نہیں بنے گا۔
نہیں، expect اور actual مختلف source setس میں ہونے چاہییں۔ expect commonMain یا ایک درمیانی source set میں، actual ایک پلیٹ فارم source set میں۔ expect اور actual کو ایک ہی source set میں رکھنا ایک کامپائل غلطی ہے۔
expect/actual کو ٹیسٹ کرنے کے لیے، پلیٹ فارم ٹیسٹ ٹنگ source setس کے ساتھ commonTest استعمال کریں۔ commonTest میں expect ٹیسٹ اور ہر پلیٹ فارم کے لیے actual ٹیسٹ لکھیں۔ انضمامی ٹیسٹ ہر ہدف پلیٹ فارم پر علیحدہ چلائے جاتے ہیں۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں