Pagpapalaganap ng Error — mekanismo ng pagpapakalat ng error pataas sa stack ng tawag mula sa lugar ng pinagmulan hanggang sa tagapangasiwa. Kapag ang isang function ay hindi kayang hawakan ang error nang mag-isa, ipinapasa nito ito sa tumatawag sa pamamagitan ng exception, deklarasyon ng throws, o uri ng pagbabalik. Ang tamang implementasyon ng pagpapalaganap ay kritikal para sa katatagan ng mga mobile application: ang mga hindi nahawakan o hindi wastong naipasa na mga error ay humahantong sa pag-crash. Ayon sa Apple Swift Documentation (2026), ang awtomatikong pagpapalaganap sa pamamagitan ng throws sa Swift ay nagbibigay-daan sa pagpapasa ng error sa anumang antas nang walang boilerplate code.
Mga Pangunahing Punto
Pagpapalaganap ng Error — proseso ng pagpapasa ng object ng error mula sa function kung saan ito lumitaw pataas sa chain ng tawag patungo sa pinakamalapit na angkop na tagapangasiwa. Isipin ang stack ng tawag: ViewController ay tumatawag sa ViewModel, ViewModel ay tumatawag sa Repository, Repository ay tumatawag sa API. Kung ang API ay nagbalik ng network error, dapat itong dumaan sa Repository at ViewModel patungo sa ViewController, na magpapakita ng mensahe sa user. Bawat intermediary function ay nagdedesisyon: hawakan ang error o ipasa pa (ipalaganap).
May dalawang approach sa pagpapalaganap: awtomatiko at manu-mano. Sa awtomatikong approach (Swift throws, Java checked exceptions) pinipilit ng compiler ang developer na hawakan ang error o ideklara ang pagpapalaganap sa lagda. Sa manu-manong approach (uri ng Result, Kotlin Try) ang error ay ipinapasa bilang halaga — ang developer ay tahasang nagsusulat ng code para sa pagpapasa o pagbabago ng error. Ayon sa Kotlin Result Docs (2026), ang Result<T> sa Kotlin ay hindi nilayon para sa direktang pagpapalaganap sa mga hangganan ng function — kailangan itong baguhin o hawakan sa bawat antas, na ginagawang mas may kamalayan ang pagpapalaganap, ngunit mas verbose.
Ang pagpili ng approach ay depende sa arkitektura ng application at wika. Sa Swift, nangingibabaw ang awtomatikong pagpapalaganap sa pamamagitan ng throws, sa Kotlin — halo ng exceptions (para sa hindi inaasahang mga error) at mga container na parang Result (para sa inaasahang mga error). Mahalagang maunawaan: ang pagpapalaganap ay hindi layunin, kundi pangangailangan. Ang perpektong arkitektura ay pinapaliit ang lalim ng pagpapalaganap sa pamamagitan ng paghawak ng mga error sa pinakamababang antas kung saan may sapat na konteksto para gumawa ng desisyon.
Sa Swift, ang pagpapalaganap sa pamamagitan ng throws ay awtomatikong nangyayari: kung ang function A na may throws ay tumatawag sa function B na may throws, at hindi hinahawakan ni A ang error ni B sa do-catch, ang error ay awtomatikong ipinapasa sa tumatawag kay A. Tinatanggal nito ang boilerplate code na katangian ng Java checked exceptions, kung saan ang throws ay dapat ideklara sa bawat method ng chain. Ginagamit ng Swift ang prinsipyong „isang throws function sa chain = ang buong chain ay nagiging throws, kung hindi hahawakan sa mga intermediary na antas”.
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 — huling tagapangasiwa
func onButtonTap() {
let vm = UserViewModel()
do {
let user = try vm.loadUser(id: 42)
updateUI(user)
} catch {
showError("Nabigong i-load ang user")
}
}
Chain ng pagpapalaganap: networkService.request -> fetchUser -> loadUser -> onButtonTap. Bawat intermediary function ay may markang throws at walang do-catch — ang error ay awtomatikong ipinapasa pataas. ViewController onButtonTap ang huling tagapangasiwa na may do-catch. Kung nagpasya ang ViewModel na baguhin ang error (ibalot sa ibang uri), maaari itong gumamit ng do-catch at bagong throw. Awtomatikong pagpapalaganap ay nagpapaikli ng code: hindi kailangang malaman ng Repository kung paano hawakan ang error — responsibilidad iyon ng ViewController, na may access sa UI para magpakita ng mensahe sa user.
Sa Kotlin, ang pagpapalaganap sa pamamagitan ng exceptions ay hindi nangangailangan ng deklarasyon ng throws sa lagda (lahat ng exceptions ay unchecked). Ang exception ay awtomatikong tumataas sa stack hanggang sa makakita ng try-catch. Gayunpaman, ang kawalan ng throws sa lagda ay ginagawang implicit ang pagpapalaganap: hindi nakikita ng developer mula sa lagda ng function na maaari itong magtapon ng exception. Ito ay parehong kalamangan (mas kaunting boilerplate) at kahinaan (mas madaling makalimot na hawakan). Niresolba ng Kotlin ang problemang ito sa pamamagitan ng mga convention at pattern ng arkitektura, hindi sa pamamagitan ng wika.
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")
}
}
}
}
Sa Repository pagpapalaganap na may pagbabago: sa IOException (hindi available ang network) sinusubukan ng function na kunin ang naka-cache na data mula sa database. Kung walang laman ang cache, nagtatapon ng AppException — nagpapatuloy ang pagpapalaganap na may bagong uri ng error. Nahuhuli ng ViewModel ang AppException at isinasalin ito sa UiState.Error — hindi na nagpapatuloy ang error, kumpleto ang pagpapalaganap sa antas ng UI layer. Kotlin Coroutines ay nagdaragdag ng mga katangian: ang exceptions sa launch ay awtomatikong kumakalat sa pamamagitan ng CoroutineExceptionHandler, at sa async — lamang sa pagtawag ng await(). Mahalagang isaalang-alang ito kapag nagdidisenyo ng pagpapalaganap sa coroutines — pinipigilan ng SupervisorJob ang pagkansela ng parent coroutine kapag may error sa child coroutine.
Alternatibo sa exceptions — pagpapalaganap sa pamamagitan ng uri-container na nagpapasa ng tagumpay o error bilang halaga. Sa approach na ito, ang function ay hindi nagbabalik ng halaga, kundi isang balot: Result<T, E> sa Swift, Result<T> sa Kotlin, Either<L, R> sa Dart (mula sa package na fpdart o dartz). Hindi binubuksan ng error ang stack — ito ay nasa container lamang, at ang susunod na antas ay nagdedesisyon kung ano ang gagawin dito. Ginagawa nitong mas tahasan at kontrolado ang pagpapalaganap.
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> — isang simpleng container na may mga field na data at error. Ang sealed class AppError ay nagdedefine ng mga uri ng error (Network, Auth). Ang function na fetchUser ay nagbabalik ng HttpResult, ang pagpapalaganap ay hindi nangangailangan ng pagbubukas ng stack — ang tumatawag ay simpleng sinusuri ang isSuccess/isError. Ang approach na ito ay lalong kapaki-pakinabang sa Clean Architecture, kung saan ang bawat layer (data, domain, presentation) ay maaaring magbago ng error: IOError -> DomainError -> UiError. Ang pagpapalaganap sa pamamagitan ng container ay ginagawang tahasan at masusubok ang mga pagbabagong ito, hindi tulad ng exceptions kung saan hindi nakikita ang chain ng pagbabago sa mga lagda ng function.
Isa sa mga pangunahing desisyon sa pagdidisenyo ng paghawak ng error ay ang pagpili sa pagitan ng pagpapalaganap (ipasa pataas) at paghawak (hawakan dito). Ang patakaran sa pagdedesisyon: hawakan ang error sa antas kung saan may sapat na konteksto para sa makabuluhang aksyon. Kung may access ka sa UI — magpakita ng mensahe sa user. Kung may access ka sa cache — subukang bumawi. Kung wala ni isa — ipalaganap.
| Scenario | Aksyon | Paliwanag |
|---|---|---|
| Network error sa Repository | Ipalaganap | Hindi alam ng Repository kung gusto ng user na ulitin ang request |
| Parse error sa Repository | Hawakan (ibalik ang default na halaga) | Alam ng Repository ang format, maaaring magbalik ng fallback na halaga |
| Timeout sa ViewModel | Hawakan (UiState.Error) | Pinamamahalaan ng ViewModel ang UiState, alam kung paano isalin ang error |
| Error sa awtorisasyon sa Interceptor | Hawakan (refresh token) | Ang Interceptor ay may access sa mga token at maaaring ibalik ang sesyon |
| Hindi kilalang error sa UseCase | Ipalaganap | Walang UI context ang UseCase — lohika ng negosyo lamang |
Gintong patakaran: minimum na pagpapalaganap, maximum na paghawak sa mas mababang antas. Kung ang Repository ay maaaring bumawi mula sa cache — dapat itong gawin, nang hindi ipinapasa ang error pataas. Kung ang ViewModel ay maaaring magpakita ng Snackbar — hayaan itong magpakita, nang hindi nangangailangan ng karagdagang code mula sa ViewController. Bawat antas ng pagpapalaganap ay nagpapataas ng coupling at nagpapahirap sa pagsubok. Ayon sa Google Android Architecture Guide (2026), inirerekomenda na bawasan ang pagpapalaganap sa mga hangganan ng layer sa pamamagitan ng paggamit ng sealed class UiState upang kumatawan sa lahat ng posibleng estado (Loading, Success, Error) sa antas ng ViewModel at huwag magpasa ng exceptions nang direkta sa UI layer.
Ang hindi tamang pagpapalaganap ay pinagmumulan ng mga bug na mahirap mahanap sa mga mobile application. Tingnan natin ang limang pangunahing problema na kinakaharap ng mga developer at mga paraan upang malutas ang mga ito.
Pinakakaraniwang problema: sa panahon ng pagpapalaganap, ang exception ay nahuhuli, na-log, at isang bago ang itinatapon nang walang orihinal na exception. Nawawala ng developer ang StackTrace at hindi maintindihan kung saan eksaktong nangyari ang error. Sa Swift gamitin ang pagkakabit ng error: throw MyError(context: originalError). Sa Kotlin: throw AppException(cause = originalException). Sa Dart: throw AppException(message, originalException). Huwag kailanman gumawa ng bagong exception nang hindi ipinapasa ang sanhi/saloob na error.
catch (e: Exception) { /* wala */ } — antipattern na nagiging sanhi ng pagpapatuloy ng application sa maling estado. Kung sigurado kang maaaring balewalain ang error — magdagdag ng komento na may paliwanag. Sa Swift para sa opsyonal na pagbalewala gamitin ang try? (error -> nil). Sa Kotlin — Result<T>.onFailure { /* log */ }. Huwag pigilan ang exceptions nang hindi nagla-log.
Kung ang error ay dumadaan sa 5+ antas nang walang paghawak, ang arkitektura ay nangangailangan ng pagbabago. Bawat antas ng pagpapalaganap ay isang dependency sa throws lagda ng mga nasa ilalim na function. Solusyon: gumamit ng Failure container (sealed class Result { Success, Error }) sa mga hangganan ng layer upang gawing tahasan at limitado ang pagpapalaganap. Kung mas maikli ang chain ng pagpapalaganap, mas madaling subukan at i-debug ang code.
Sa Kotlin Coroutines, ang exception sa launch ay nagkakansela ng parent coroutine at lahat ng sibling (mga anak ng parehong scope). Kung ang isa sa 10 parallel tasks ay bumagsak, ang natitirang 9 ay kakanselahin, na madalas ay hindi kanais-nais. Gumamit ng SupervisorJob o supervisorScope para sa paghihiwalay ng error: ang error sa isang child ay hindi nagkakansela ng mga sibling. Ang ViewModelScope ay gumagamit ng SupervisorJob bilang default, na nagpoprotekta laban sa problemang ito sa Android.
Sa API na nakabatay sa callback, ang error ay madalas na ipinapasa bilang parameter ng callback. Kung hindi hinahawakan ng callback ang error (o hindi wastong hinahawakan), ang pagpapalaganap ay nagiging implicit at madaling mawala. Solusyon: lumipat sa async/await (Swift) o coroutines (Kotlin), kung saan gumagana ang pagpapalaganap sa pamamagitan ng standard na mekanismo ng try-catch. Kung hindi maiiwasan ang callback — gumamit ng Either<Error, T> o Result<T> para sa sapilitang paghawak ng parehong kaso.
Mga Madalas Itanong
Throw ay isang beses na aksyon ng pagtatapon ng exception. Pagpapalaganap ng Error ay ang buong proseso ng pagpapasa ng error sa pamamagitan ng ilang antas ng stack, mula throw hanggang catch. Kasama sa pagpapalaganap ang throw, awtomatiko o manu-manong pagpapasa sa pamamagitan ng intermediary function at huling paghawak. Ito ay mas malawak na konsepto na naglalarawan sa lifecycle ng error.
Gumamit ng mock object na nagtatapon ng exceptions sa mga ibinigay na scenario. Suriin na ang function ay wastong nagpapalaganap o humahawak ng error sa pamamagitan ng assertThrows (Kotlin/JUnit) o XCTAssertThrowsError (Swift/XCTest). Para sa Result-based na pagpapalaganap, suriin ang isSuccess/isError at mga halaga sa parehong kaso.
Mas gusto ang Result pagpapalaganap para sa inaasahang mga error (di-wastong data, mga patakaran ng negosyo) sa loob ng isang hangganan ng arkitektura. Mas mahusay ang exceptions para sa hindi inaasahang mga error (pagkawala ng network, I/O error) na dapat hawakan sa mataas na antas. Ang resulta na may error ay hindi nakakaabala sa daloy ng pagpapatupad, ang exception ay nakakaabala.
Sa Kotlin Coroutines, ang exception sa launch ay awtomatikong nagpapalaganap sa pamamagitan ng CoroutineScope na may pagkansela ng sibling. Gumamit ng supervisorScope o SupervisorJob para sa paghihiwalay: ang error sa isang coroutine ay hindi nagkakansela ng iba. Para sa async, ang error ay dapat tahasang hawakan sa pamamagitan ng try-catch sa pagtawag ng await(), kung hindi ito lalamunin.
Ang perpektong huling tagapangasiwa ay ang UI layer (ViewController, Fragment/Composable). Tanging ito ay may access sa user interface at maaaring magpakita ng mensahe, Snackbar o dialog. Ang mga intermediary layer (Repository, UseCase, ViewModel) ay nagpapalaganap ng error, binabago ito kung kinakailangan sa mas abstract na uri ng domain.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din