Error Propagation: یہ کیا ہے، غلطی کے پھیلاؤ کا طریقہ کار اور موبائل ڈویلپمنٹ میں یہ کیسے کام کرتا ہے

مصنف: IT Sectr اشاعت: 2026-05-26 مطالعے کا وقت: 9 منٹ

Error Propagation ایک طریقہ کار ہے جو غلطی کو وقوع کے مقام سے کال اسٹیک میں اوپر ہینڈلر تک پھیلاتا ہے۔ جب کوئی فنکشن خود غلطی کو ہینڈل نہیں کر سکتا، تو وہ اسے استثنیٰ (exception)، throws اعلان یا Return Type کے ذریعے کال کرنے والے فریق کو منتقل کرتا ہے۔ موبائل ایپلیکیشنز کے استحکام کے لیے propagation کا درست نفاذ بہت اہم ہے: غیر ہینڈل یا غلط طریقے سے منتقل کردہ غلطیاں کریش کا سبب بنتی ہیں۔ Apple Swift Documentation (2026) کے مطابق، Swift میں throws کے ذریعے خودکار propagation بغیر بوائلر پلیٹ کوڈ کے کسی بھی سطح پر غلطی منتقل کرنے کی اجازت دیتا ہے۔

اہم نکات

  • Error Propagation — غلطی کو وقوع کے مقام سے اسٹیک میں اوپر ہینڈلر تک منتقل کرنا، درمیانی فنکشنز کو چھوڑتے ہوئے
  • خودکار propagation Swift میں throws کے ذریعے ہر اسٹیک سطح پر واضح کوڈ کے بغیر غلطی منتقل کرتا ہے
  • دستی propagation Kotlin اور Dart میں ہر سطح پر واضح try-catch یا Result کنٹینر میں منتقلی کی ضرورت ہوتی ہے
  • چیکڈ استثنیات Java میں دستخط میں throws کے ذریعے propagation کو مجبور کرتی ہیں، ان چیکڈ کو نظر انداز کیا جا سکتا ہے
  • Result Type — استثنیات کا متبادل جہاں غلطی اسٹیک کو کھولے بغیر ایک قدر کے طور پر منتقل کی جاتی ہے

Error Propagation کیا ہے؟

Error Propagation (غلطی کا پھیلاؤ) غلطی آبجیکٹ کو اس فنکشن سے جہاں یہ واقع ہوا کال چین میں اوپر قریب ترین موزوں ہینڈلر تک منتقل کرنے کا عمل ہے۔ کال اسٹیک کا تصور کریں: ViewController ViewModel کو کال کرتا ہے، ViewModel Repository کو کال کرتا ہے، Repository API کو کال کرتا ہے۔ اگر API نیٹ ورک کی غلطی لوٹاتا ہے، تو اسے Repository اور ViewModel کے ذریعے ViewController تک جانا ہوگا، جو صارف کو پیغام دکھائے گا۔ ہر درمیانی فنکشن فیصلہ کرتا ہے: غلطی کو ہینڈل کریں یا آگے منتقل کریں (propagate)۔

Propagation کے دو طریقے ہیں: خودکار اور دستی۔ خودکار طریقے (Swift throws، Java چیکڈ استثنیات) میں کمپائلر ڈویلپر کو یا تو غلطی ہینڈل کرنے یا دستخط میں propagation اعلان کرنے پر مجبور کرتا ہے۔ دستی طریقے (Result Type، Kotlin Try) میں غلطی ایک قدر کے طور پر منتقل کی جاتی ہے — ڈویلپر واضح طور پر غلطی کو منتقل کرنے یا تبدیل کرنے کے لیے کوڈ لکھتا ہے۔ Kotlin Result Docs (2026) کے مطابق، Kotlin میں Result<T> براہ راست فنکشن حدود کے پار propagation کے لیے ڈیزائن نہیں کیا گیا — اسے ہر سطح پر تبدیل یا ہینڈل کرنے کی ضرورت ہوتی ہے، جو propagation کو زیادہ باشعور لیکن زیادہ لفاظی والا بناتا ہے۔

طریقے کا انتخاب ایپلیکیشن آرکیٹیکچر اور زبان پر منحصر ہے۔ Swift میں throws کے ذریعے خودکار propagation غالب ہے، Kotlin میں استثنیات (غیر متوقع غلطیوں کے لیے) اور Result جیسے کنٹینر (متوقع غلطیوں کے لیے) کا مرکب استعمال ہوتا ہے۔ یہ سمجھنا ضروری ہے: propagation کوئی مقصد نہیں، بلکہ ایک ضرورت ہے۔ ایک مثالی آرکیٹیکچر propagation کی گہرائی کو کم کرتا ہے، غلطیوں کو کم سے کم ممکنہ سطح پر ہینڈل کرتا ہے جہاں فیصلہ کرنے کے لیے کافی سیاق و سباق ہو۔

Swift میں Throws کے ذریعے Propagation

Swift میں، throws کے ذریعے propagation خودکار ہے: اگر فنکشن A (throws کے ساتھ) فنکشن B (throws کے ساتھ) کو کال کرتا ہے، اور A do-catch میں B کی غلطی کو ہینڈل نہیں کرتا، تو غلطی خود بخود A کے کالر کو منتقل ہو جاتی ہے۔ یہ Java چیکڈ استثنیات کے عام بوائلر پلیٹ کوڈ کو ختم کرتا ہے، جہاں زنجیر کے ہر طریقہ میں throws اعلان کرنا ضروری ہوتا ہے۔ Swift اصول استعمال کرتا ہے «زنجیر میں ایک throws فنکشن = پوری زنجیر throws بن جاتی ہے، اگر درمیانی سطحوں پر ہینڈل نہ کیا جائے»۔

swift
struct UserRepository {
    func fetchUser(id: Int) throws -> User {
        let data = try networkService.request(path: "/users/\(id)")
        return try parseUser(from: data)
    }
}

class UserViewModel {
    let repo = UserRepository()

    func loadUser(id: Int) throws -> User {
        return try repo.fetchUser(id: id)
    }
}

// ViewController — حتمی ہینڈلر
func onButtonTap() {
    let vm = UserViewModel()
    do {
        let user = try vm.loadUser(id: 42)
        updateUI(user)
    } catch {
        showError("صارف لوڈ کرنے میں ناکام")
    }
}

Propagation زنجیر: networkService.request -> fetchUser -> loadUser -> onButtonTap۔ ہر درمیانی فنکشن throws سے نشان زد ہے اور do-catch پر مشتمل نہیں ہے — غلطی خود بخود اوپر منتقل ہوتی ہے۔ ViewController onButtonTap do-catch کے ساتھ آخری ہینڈلر ہے۔ اگر ViewModel غلطی کو تبدیل کرنے (دوسری قسم میں لپیٹنے) کا فیصلہ کرتا، تو وہ do-catch اور نیا throw استعمال کر سکتا تھا۔ خودکار propagation کوڈ کو کم کرتا ہے: Repository کو یہ جاننے کی ضرورت نہیں ہے کہ غلطی کو کیسے ہینڈل کرنا ہے — یہ ViewController کی ذمہ داری ہے، جس کے پاس صارف کو پیغام دکھانے کے لیے UI تک رسائی ہے۔

Kotlin میں استثنیات کے ذریعے Propagation

Kotlin میں، استثنیات کے ذریعے propagation کے لیے دستخط میں throws اعلان کی ضرورت نہیں ہے (تمام استثنیات ان چیکڈ ہیں)۔ استثنیٰ خود بخود اسٹیک میں اوپر چڑھتی ہے جب تک کہ try-catch سے نہ مل جائے۔ تاہم، دستخط میں throws کی عدم موجودگی propagation کو مضمر بنا دیتی ہے: ڈویلپر فنکشن کے دستخط سے نہیں دیکھ سکتا کہ یہ استثنیٰ پھینک سکتا ہے۔ یہ ایک ساتھ پلس (کم بوائلر پلیٹ) اور مائنس (ہینڈل کرنا بھولنا آسان) ہے۔ Kotlin اس مسئلے کو کنونشنز اور آرکیٹیکچرل پیٹرن کے ذریعے حل کرتا ہے، نہ کہ زبان کے ذریعے۔

kotlin
class UserRepository(
    private val api: ApiService,
    private val db: Database
) {
    suspend fun getUser(id: String): User {
        return try {
            api.fetchUser(id)
        } catch (e: IOException) {
            db.getCachedUser(id) ?: throw AppException("User unavailable")
        }
    }
}

class UserViewModel(private val repo: UserRepository) {
    private val _state = MutableStateFlow<UiState<User>>(UiState.Loading)
    val state: StateFlow<UiState<User>> = _state

    fun loadUser(id: String) {
        viewModelScope.launch {
            try {
                val user = repo.getUser(id)
                _state.value = UiState.Success(user)
            } catch (e: AppException) {
                _state.value = UiState.Error(e.message ?: "Unknown")
            }
        }
    }
}

Repository میں، تبدیلی کے ساتھ propagation: IOException (نیٹ ورک دستیاب نہیں) پر فنکشن ڈیٹا بیس سے کیشڈ ڈیٹا حاصل کرنے کی کوشش کرتا ہے۔ اگر کیش خالی ہے، تو AppException پھینکتا ہے — propagation نئی غلطی کی قسم کے ساتھ جاری رہتی ہے۔ ViewModel AppException کو پکڑتا ہے اور UiState.Error میں ترجمہ کرتا ہے — غلطی آگے نہیں جاتی، propagation UI پرت کی سطح پر ختم ہوتی ہے۔ Kotlin Coroutines خصوصیات شامل کرتے ہیں: launch میں استثنیٰ خود بخود CoroutineExceptionHandler کے ذریعے پھیلتی ہے، اور async میں — صرف await() کال کرنے پر۔ کوروٹینز میں propagation ڈیزائن کرتے وقت یہ اہم ہے — SupervisorJob چائلڈ کوروٹین میں غلطی پر پیرنٹ کوروٹین کی منسوخی کو روکتا ہے۔

Result Type کے ذریعے Propagation

استثنیات کا متبادل کنٹینر قسم کے ذریعے propagation ہے جو کامیابی یا غلطی کو ایک قدر کے طور پر منتقل کرتا ہے۔ اس طریقے میں، فنکشن قدر نہیں بلکہ ایک لفافہ لوٹاتا ہے: Swift میں Result<T, E>، Kotlin میں Result<T>، Dart میں Either<L, R> (fpdart یا dartz پیکیج سے)۔ غلطی اسٹیک نہیں کھولتی — یہ صرف کنٹینر کے اندر ہوتی ہے، اور اگلی سطح فیصلہ کرتی ہے کہ اس کے ساتھ کیا کرنا ہے۔ یہ propagation کو زیادہ واضح اور قابل کنٹرول بناتا ہے۔

kotlin
data class HttpResult<out T>(
    val data: T?,
    val error: AppError?
) {
    val isSuccess: Boolean get() = data != null
    val isError: Boolean get() = error != null
}

sealed class AppError {
    data class Network(val message: String) : AppError()
    data class Auth(val message: String) : AppError()
}

fun fetchUser(id: String): HttpResult<User> {
    return try {
        val response = api.get("/users/$id")
        HttpResult(data = parseUser(response), error = null)
    } catch (e: IOException) {
        HttpResult(data = null, error = AppError.Network("No internet"))
    }
}

HttpResult<T> — data اور error فیلڈز والا سادہ کنٹینر۔ Sealed class AppError غلطی کی اقسام (Network، Auth) کی وضاحت کرتا ہے۔ fetchUser فنکشن HttpResult لوٹاتا ہے، propagation میں اسٹیک کھولنے کی ضرورت نہیں — کال کرنے والا صرف isSuccess/isError چیک کرتا ہے۔ یہ طریقہ خاص طور پر Clean Architecture میں مفید ہے، جہاں ہر پرت (data، domain، presentation) غلطی کو تبدیل کر سکتی ہے: IOError -> DomainError -> UiError۔ کنٹینر کے ذریعے propagation ان تبدیلیوں کو واضح اور قابل آزمائش بناتا ہے، استثنیات کے برعکس جہاں تبدیلی کی زنجیر فنکشن دستخطوں میں نظر نہیں آتی۔

Propagation بمقابلہ Handling: کب منتقل کریں، کب ہینڈل کریں

غلطی ہینڈلنگ ڈیزائن میں اہم فیصلوں میں سے ایک propagation (اوپر منتقل کرنا) اور handling (یہاں ہینڈل کرنا) کے درمیان انتخاب ہے۔ فیصلہ کا اصول: غلطی کو اس سطح پر ہینڈل کریں جہاں بامعنی کارروائی کے لیے کافی سیاق و سباق ہو۔ اگر آپ کے پاس UI تک رسائی ہے — صارف کو پیغام دکھائیں۔ اگر آپ کے پاس کیش تک رسائی ہے — بحالی کی کوشش کریں۔ اگر کوئی نہیں ہے — propagate کریں۔

منظر نامہعملوجہ
Repository میں نیٹ ورک کی غلطیPropagateRepository نہیں جانتا کہ صارف درخواست دہرانا چاہتا ہے یا نہیں
Repository میں تجزیہ کی غلطیہینڈل کریں (ڈیفالٹ لوٹائیں)Repository فارمیٹ جانتا ہے، فال بیک قدر لوٹا سکتا ہے
ViewModel میں ٹائم آؤٹہینڈل کریں (UiState.Error)ViewModel UiState کا انتظام کرتا ہے، غلطی کا ترجمہ کرنا جانتا ہے
Interceptor میں اجازت کی غلطیہینڈل کریں (ٹوکن ریفریش)Interceptor کو ٹوکنز تک رسائی ہے اور سیشن بحال کر سکتا ہے
UseCase میں نامعلوم غلطیPropagateUseCase کے پاس UI سیاق و سباق نہیں — صرف کاروباری منطق

سنہری اصول: کم سے کم propagation، نچلی سطحوں پر زیادہ سے زیادہ handling۔ اگر Repository کیش سے بحال ہو سکتا ہے — اسے غلطی کو اوپر منتقل کیے بغیر ایسا کرنا چاہیے۔ اگر ViewModel Snackbar دکھا سکتا ہے — اسے دکھانے دیں، ViewController سے اضافی کوڈ کی ضرورت کے بغیر۔ propagation کی ہر سطح جوڑ کو بڑھاتی ہے اور جانچ کو پیچیدہ بناتی ہے۔ Google Android Architecture Guide (2026) کے مطابق، ViewModel سطح پر تمام ممکنہ حالات (Loading، Success، Error) کی نمائندگی کے لیے sealed class UiState استعمال کرکے اور استثنیات کو براہ راست UI پرت میں منتقل نہ کرکے پرت کی حدود میں propagation کو کم سے کم کرنے کی سفارش کی جاتی ہے۔

Error Propagation کے مسائل اور اینٹی پیٹرن

غلط propagation موبائل ایپلیکیشنز میں مشکل سے پکڑے جانے والے بگز کا ذریعہ ہے۔ آئیے پانچ اہم مسائل پر غور کریں جن کا ڈویلپرز کو سامنا ہے اور انہیں حل کرنے کے طریقے۔

غلطی کے سیاق و سباق کا نقصان

سب سے عام مسئلہ: propagation کے دوران، ایک استثنیٰ پکڑی جاتی ہے، لاگ کی جاتی ہے اور اصل استثنیٰ کے بغیر نئی پھینکی جاتی ہے۔ ڈویلپر StackTrace کھو دیتا ہے اور سمجھ نہیں پاتا کہ غلطی بالکل کہاں ہوئی۔ Swift میں، غلطی کا سلسلہ استعمال کریں: throw MyError(context: originalError)۔ Kotlin میں: throw AppException(cause = originalException)۔ Dart میں: throw AppException(message, originalException)۔ cause/underlyingError منتقل کیے بغیر کبھی نیا استثنیٰ نہ بنائیں۔

غلطی کو نظر انداز کرنا (خالی catch)

catch (e: Exception) { /* کچھ نہیں */ } ایک اینٹی پیٹرن ہے جو ایپلیکیشن کو غلط حالت میں کام جاری رکھنے کا سبب بنتا ہے۔ اگر آپ کو یقین ہے کہ غلطی کو نظر انداز کیا جا سکتا ہے — جواز کے ساتھ تبصرہ شامل کریں۔ Swift میں اختیاری نظر انداز کرنے کے لیے try? (غلطی -> nil) استعمال کریں۔ Kotlin میں — Result<T>.onFailure { /* لاگ */ }۔ لاگنگ کے بغیر استثنیات کو نہ نگلیں۔

حد سے زیادہ propagation گہرائی

اگر غلطی بغیر ہینڈل کیے 5+ سطحوں سے گزرتی ہے، تو آرکیٹیکچر پر نظر ثانی کی ضرورت ہے۔ propagation کی ہر سطح بنیادی فنکشنز کے throws دستخط پر انحصار ہے۔ حل: پرت کی حدود پر ناکامی کے کنٹینر (sealed class Result { Success, Error }) استعمال کریں تاکہ propagation واضح اور محدود ہو۔ propagation کی زنجیر جتنی چھوٹی ہوگی، کوڈ کی جانچ اور ڈیبگ کرنا اتنا ہی آسان ہوگا۔

SupervisorJob کے بغیر کوروٹینز میں Propagation

Kotlin Coroutines میں، launch میں استثنیٰ ڈیفالٹ طور پر پیرنٹ کوروٹین اور تمام siblings (اسی scope کے بچے) کو منسوخ کر دیتی ہے۔ اگر 10 متوازی کاموں میں سے ایک ناکام ہوتا ہے، تو باقی 9 منسوخ ہو جائیں گے، جو اکثر ناپسندیدہ ہے۔ غلطیوں کو الگ کرنے کے لیے SupervisorJob یا supervisorScope استعمال کریں: ایک بچے میں غلطی siblings کو منسوخ نہیں کرتی۔ ViewModelScope ڈیفالٹ طور پر SupervisorJob استعمال کرتا ہے، جو Android میں اس مسئلے سے بچاتا ہے۔

ہینڈلنگ کے بغیر کال بیک کے ذریعے Propagation

کال بیک پر مبنی API میں، غلطی اکثر کال بیک کے پیرامیٹر کے طور پر منتقل کی جاتی ہے۔ اگر کال بیک غلطی کو ہینڈل نہیں کرتا (یا غلط طریقے سے ہینڈل کرتا ہے)، تو propagation مضمر ہو جاتی ہے اور آسانی سے کھو جاتی ہے۔ حل: async/await (Swift) یا کوروٹینز (Kotlin) پر منتقل ہوں، جہاں propagation معیاری try-catch میکانزم کے ذریعے کام کرتی ہے۔ اگر کال بیک ناگزیر ہے — دونوں صورتوں کو ہینڈل کرنے پر مجبور کرنے کے لیے Either<Error, T> یا Result<T> استعمال کریں۔

اکثر پوچھے گئے سوالات

Error Propagation throw سے کیسے مختلف ہے؟

Throw ایک بار کی کارروائی ہے جو استثنیٰ پھینکتی ہے۔ Error Propagation throw سے catch تک کئی اسٹیک سطحوں کے ذریعے غلطی منتقل کرنے کا پورا عمل ہے۔ propagation میں throw، درمیانی فنکشنز کے ذریعے خودکار یا دستی منتقلی اور آخری ہینڈلنگ شامل ہے۔ یہ ایک وسیع تر تصور ہے جو غلطی کے لائف سائیکل کو بیان کرتا ہے۔

Error Propagation کی جانچ کیسے کریں؟

وہ mock آبجیکٹ استعمال کریں جو دیے گئے منظرناموں میں استثنیٰ پھینکتے ہیں۔ چیک کریں کہ فنکشن غلطی کو صحیح طریقے سے propagate یا ہینڈل کرتا ہے assertThrows (Kotlin/JUnit) یا XCTAssertThrowsError (Swift/XCTest) کے ذریعے۔ Result پر مبنی propagation کے لیے، isSuccess/isError اور دونوں صورتوں میں اقدار چیک کریں۔

Result کے ذریعے propagation استثنیات سے بہتر کب ہے؟

Result propagation ایک آرکیٹیکچرل حد کے اندر متوقع غلطیوں (غلط ڈیٹا، کاروباری قواعد) کے لیے افضل ہے۔ استثنیات غیر متوقع غلطیوں (نیٹ ورک کا نقصان، I/O غلطیاں) کے لیے بہتر ہیں جنہیں اعلی سطح پر ہینڈل کیا جانا چاہیے۔ غلطی کے ساتھ نتیجہ عمل کے بہاؤ میں خلل نہیں ڈالتا، استثنیٰ ڈالتی ہے۔

Kotlin کوروٹینز کے ذریعے غلطی کیسے propagate کریں؟

Kotlin Coroutines میں، launch میں استثنیٰ siblings کی منسوخی کے ساتھ خود بخود CoroutineScope کے ذریعے propagate ہوتی ہے۔ علیحدگی کے لیے supervisorScope یا SupervisorJob استعمال کریں: ایک کوروٹین میں غلطی دوسروں کو منسوخ نہیں کرتی۔ async کے لیے، await() کال کرتے وقت try-catch کے ذریعے غلطی کو واضح طور پر ہینڈل کرنا ضروری ہے، ورنہ یہ نگل جائے گی۔

کون سی سطح حتمی غلطی ہینڈلر ہونی چاہیے؟

مثالی حتمی ہینڈلر UI پرت (ViewController، Fragment/Composable) ہے۔ صرف اس کے پاس یوزر انٹرفیس تک رسائی ہے اور یہ پیغام، Snackbar یا ڈائیلاگ دکھا سکتا ہے۔ درمیانی پرتیں (Repository، UseCase، ViewModel) غلطی کو propagate کرتی ہیں، اگر ضرورت ہو تو اسے مزید تجریدی ڈومین قسم میں تبدیل کرتی ہیں۔

خلاصہ

  • Error Propagation — استثنیات یا Result کنٹینرز کے ذریعے غلطی کو وقوع کے مقام سے اسٹیک میں اوپر ہینڈلر تک منتقل کرنا
  • خودکار propagation Swift میں throws کے ذریعے درمیانی سطحوں پر کوڈ کی ضرورت نہیں — غلطی خود بخود اوپر چڑھتی ہے
  • دستی propagation Kotlin میں واضح try-catch اور ہر سطح پر throw کے ذریعے ہینڈلنگ کو باشعور لیکن لفاظی والا بناتی ہے
  • Result Type اسٹیک کھولے بغیر غلطی کو قدر کے طور پر منتقل کرتا ہے، Clean Architecture میں متوقع غلطیوں کے لیے آسان
  • ہینڈلنگ کا اصول: سیاق و سباق والی سطح (UI) پر ہینڈل کریں، بغیر سیاق و سباق والی سطحوں (domain, data) کے ذریعے propagate کریں
  • اینٹی پیٹرن: خالی catch، تبدیلی میں cause کا نقصان، حد سے زیادہ propagation گہرائی، SupervisorJob کو نظر انداز کرنا
  • ہر پرت کے لیے ٹائپ شدہ غلطیاں (sealed class / enum) ڈیزائن کریں اور پرت کی حدود عبور کرتے وقت انہیں تبدیل کریں

ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے

IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔

پروجیکٹ پر بحث کریں

مزید پڑھیں