{"id":32151746,"url":"https://github.com/jamitlabs/mungohealer","last_synced_at":"2025-10-28T18:13:04.230Z","repository":{"id":62448131,"uuid":"153099147","full_name":"JamitLabs/MungoHealer","owner":"JamitLabs","description":"Error Handler based on localized \u0026 healable (recoverable) errors without the overhead of NSError.","archived":false,"fork":false,"pushed_at":"2019-04-29T07:04:16.000Z","size":162,"stargazers_count":7,"open_issues_count":3,"forks_count":5,"subscribers_count":3,"default_branch":"stable","last_synced_at":"2025-10-11T02:22:55.655Z","etag":null,"topics":["error-handler","error-handling","errors","localized","recoverable"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JamitLabs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-10-15T11:07:21.000Z","updated_at":"2022-04-18T16:00:17.000Z","dependencies_parsed_at":"2022-11-01T23:17:49.082Z","dependency_job_id":null,"html_url":"https://github.com/JamitLabs/MungoHealer","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/JamitLabs/MungoHealer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JamitLabs%2FMungoHealer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JamitLabs%2FMungoHealer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JamitLabs%2FMungoHealer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JamitLabs%2FMungoHealer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JamitLabs","download_url":"https://codeload.github.com/JamitLabs/MungoHealer/tar.gz/refs/heads/stable","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JamitLabs%2FMungoHealer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280248569,"owners_count":26297925,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-21T02:00:06.614Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["error-handler","error-handling","errors","localized","recoverable"],"created_at":"2025-10-21T10:52:10.228Z","updated_at":"2025-10-21T10:52:14.381Z","avatar_url":"https://github.com/JamitLabs.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://raw.githubusercontent.com/JamitLabs/MungoHealer/stable/Logo.png\"\n      width=600\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://app.bitrise.io/app/fbdb7fe2d4879760\"\u003e\n        \u003cimg src=\"https://app.bitrise.io/app/fbdb7fe2d4879760/status.svg?token=bXijt_o5Fsl1E8cqtrrXLw\u0026branch=stable\"\n             alt=\"Build Status\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/JamitLabs/MungoHealer/releases\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/Version-0.3.2-blue.svg\"\n             alt=\"Version: 0.3.2\"\u003e\n    \u003c/a\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Swift-5.0-FFAC45.svg\"\n         alt=\"Swift: 5.0\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Platforms-iOS%20%7C%20tvOS-FF69B4.svg\"\n        alt=\"Platforms: iOS | tvOS\"\u003e\n    \u003ca href=\"https://github.com/JamitLabs/MungoHealer/blob/stable/LICENSE\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/License-MIT-lightgrey.svg\"\n              alt=\"License: MIT\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"#installation\"\u003eInstallation\u003c/a\u003e\n  • \u003ca href=\"#usage\"\u003eUsage\u003c/a\u003e\n  • \u003ca href=\"https://github.com/JamitLabs/MungoHealer/issues\"\u003eIssues\u003c/a\u003e\n  • \u003ca href=\"#contributing\"\u003eContributing\u003c/a\u003e\n  • \u003ca href=\"#license\"\u003eLicense\u003c/a\u003e\n\u003c/p\u003e\n\n# MungoHealer\n\nError Handler based on localized \u0026 healable (recoverable) errors without the overhead of NSError (which you would have when using LocalizedError \u0026 RecoverableError instead).\n\n## Why use MungoHealer?\n\nWhen developing a new feature for an App developers often need to both have presentable results **fast** and at the same time provide **good user feedback** for edge cases like failed network requests or invalid user input.\n\nWhile there are many ways to deal with such situations, MungoHealer provides a straightforward and Swift-powered approach that uses system alerts for user feedback by default, but can be easily customized to use custom UI when needed.\n\n## tl;dr\n\nHere's a very simple example of basic error handling without MungoHealer:\n\n```swift\nfunc login(success: (String) -\u003e Void) {\n    guard let username = usernameLabel.text, !username.isEmpty else {\n        let alertCtrl = UIAlertController(title: \"Invalid User Input\", message: \"Please enter a username.\", preferredStyle: .alert)\n        alertCtrl.addAction(UIAlertAction(title: \"OK\", style: .default, handler: nil))\n        viewController.present(alertCtrl, animated: true, completion: nil)\n        return\n    }\n    guard let password = passwordLabel.text, !password.isEmpty else {\n        let alertCtrl = UIAlertController(title: \"Invalid User Input\", message: \"Please enter a password.\", preferredStyle: .alert)\n        alertCtrl.addAction(UIAlertAction(title: \"OK\", style: .default, handler: nil))\n        viewController.present(alertCtrl, animated: true, completion: nil)\n        return\n    }\n    guard let apiToken = getApiToken(username, password) else {\n        let alertCtrl = UIAlertController(title: \"Invalid User Input\", message: \"Username and password did not match.\", preferredStyle: .alert)\n        alertCtrl.addAction(UIAlertAction(title: \"OK\", style: .default, handler: nil))\n        viewController.present(alertCtrl, animated: true, completion: nil)\n        return\n    }\n    success(apiToken)\n)\n```\n\nUsing MungoHealer the above code becomes this:\n\n```swift\nfunc login(success: (String) -\u003e Void) {\n    mungo.do {\n        guard let username = usernameLabel.text, !username.isEmpty else {\n            throw MungoError(source: .invalidUserInput, message: \"Please enter a username.\")\n        }\n        guard let password = passwordLabel.text, !password.isEmpty else {\n            throw MungoError(source: .invalidUserInput, message: \"Please enter a password.\")\n        }\n        guard let apiToken = getApiToken(username, password) else {\n            throw MungoError(source: .invalidUserInput, message: \"Username and password did not match.\")\n        }\n        success(apiToken)\n    }\n)\n```\n\n## Installation\n\nInstalling via [Carthage](https://github.com/Carthage/Carthage#carthage) \u0026 [CocoaPods](https://guides.cocoapods.org/using/getting-started.html) are both supported.\n\nSupport for SPM is currently not possible as this framework uses UIKit.\n\n## Usage\n\nPlease also have a look at the `MungoHealer iOS-Demo` project in the subfolder `Demos` for a live usage example.\n\n---\n### Features Overview\n\n- [Defining Errors](#defining-errors)\n- [Default Error Types](#default-error-types)\n- [Error Handling](#error-handling)\n- [Usage Example](#usage-example)\n\n---\n\n### Defining Errors\n\nMungoHealer is based on Swifts built-in [error handling mechanism](https://docs.swift.org/swift-book/LanguageGuide/ErrorHandling.html). So before we can throw any useful error message when something goes wrong, we need to define our errors.\n\nMungoHealer can deal with any errors thrown by system frameworks or third party libraries alike, but to make use of the user feedback automatics, you need to implement one of MungoHealers error type protocols:\n\n#### `BaseError`\n\nA localized error type without the overhead of NSError – truly designed for Swift. Use this for any error you want to provide localized user feedback for.\n\n\u003cdetails\u003e\n\u003csummary\u003eRequirements\u003c/summary\u003e\n\n**`source: ErrorSource`**\nA classification of the errors source. MungoHealer will automatically provide an alert title based on it. The available options are:\n\n-  `.invalidUserInput`\n-  `.internalInconsistency`\n-  `.externalSystemUnavailable`\n-  `.externalSystemBehavedUnexpectedlyBased`\n\nThe options are explained in detail [here](https://khawerkhaliq.com/blog/swift-error-handling/#Sources_of_errors).\n\n**`errorDescription: String`**\nA localized message describing what error occurred. This will be presented to the user as the alerts message by default when the error occurs.\n\n**`debugDescription: String?`**\nAn optional message describing the error in more technical detail for debugging purposes. This will **not** be presented to the user and so for **only** logged.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eExample\u003c/summary\u003e\n\n```swift\nstruct PasswordValidationError: BaseError {\n    let errorDescription = \"Your password confirmation didn't match your password. Please try again.\"\n    let source = ErrorSource.invalidUserInput\n}\n```\n\n\u003c/details\u003e\n\n#### `FatalError`\n\nA non-healable (non-recoverable) \u0026 localized fatal error type without the overhead of NSError – truly designed for Swift. Use this as an alternative for `fatalError` and tasks like force-unwrapping when you don't expect a `nil` value and therefore don't plan to heal (recover from).\n\nNote that throwing a `FatalError` will crash your app, just like `fatalError()` or force-unwrapping `nil` would. The difference is, that here the user is first presented with an error message which is a better user experience. Additionally, before the app crashes you have the chance to do any cleanup or reporting tasks via a callback if you need to.\n\n*It is highly recommended to ***keep the suffix `FatalError`*** in your custom errors class name to clearly communicate that throwing this will crash the app.*\n\n\u003cdetails\u003e\n\u003csummary\u003eRequirements\u003c/summary\u003e\n\n`FatalError` has the exact same requirements as `BaseError`. In fact its type declaration is as simple as this:\n\n```swift\npublic protocol FatalError: BaseError {}\n```\n\nThe only difference is semantics – the data provided by a `FatalError` will be used for alert title \u0026 message as well. But confirming the alert will crash the app.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eExample\u003c/summary\u003e\n\n```swift\nstruct UnexpectedNilFatalError: FatalError {\n    let errorDescription = \"An unexpected data inconsistency has occurred. App execution can not be continued.\"\n    let source = ErrorSource.internalInconsistency\n}\n```\n\n\u003c/details\u003e\n\n\n#### `HealableError`\n\nA healable (recoverable) \u0026 localized error type without the overhead of NSError – truly designed for Swift. Use this for any edge-cases you can heal (recover from) like network timeouts (healing via Retry), network unauthorized responses (healing via Logout) etc.\n\n\u003cdetails\u003e\n\u003csummary\u003eRequirements\u003c/summary\u003e\n\n`HealableError` extends `BaseError` and therefore has the same requirements. In addition to that, you need to add:\n\n**`healingOptions: [HealingOption]`**\nProvides an array of possible healing options to present to the user. A healing option consists of the following:\n\n- **`style: Style`**: The style of the healing option. One of: `.normal`, `.recommended` or `.destructive`\n- **`title: String`**: The title of the healing option.\n- **`handler: () -\u003e Void`**: The code to be executed when the user chooses the healing option.\n\nNote that you must provide at least one healing option.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eExample\u003c/summary\u003e\n\n```swift\nstruct NetworkUnavailableError: HealableError {\n    private let retryClosure: () -\u003e Void\n\n    init(retryClosure: @escaping () -\u003e Void) {\n        self.retryClosure = retryClosure\n    }\n\n    let errorDescription = \"Could not connect to server. Please check your internet connection and try again.\"\n    let source = ErrorSource.externalSystemUnavailable\n\n    var healingOptions: [HealingOption] {\n        let retryOption = HealingOption(style: .recommended, title: \"Try Again\", handler: retryClosure)\n        let cancelOption = HealingOption(style: .normal, title: \"Cancel\", handler: {})\n        return [retryOption, cancelOption]\n    }\n}\n```\n\n\u003c/details\u003e\n\n### Default Error Types\n\nMungoHealer provides one basic implementation of each error protocol which you can use for convenience so you don't have to write a new error type for simple message errors. These are:\n\n\u003cdetails\u003e\n\u003csummary\u003eMungoError\u003c/summary\u003e\n\n- Implements `BaseError`\n- `init` takes `source: ErrorSource` \u0026 `message: String`\n\nExample Usage:\n\n```swift\nfunc fetchImage(urlPath: String) {\n  guard let url = URL(string: urlPath) else {\n    throw MungoError(source: .invalidUserInput, message: \"Invalid Path\")\n  }\n\n  // ...\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eMungoFatalError\u003c/summary\u003e\n\n- Implements `FatalError`\n- `init` takes `source: ErrorSource` \u0026 `message: String`\n\nExample Usage:\n\n```swift\nfunc fetchImage(urlPath: String) {\n  guard let url = URL(string: urlPath) else {\n    throw MungoFatalError(source: .invalidUserInput, message: \"Invalid Path\")\n  }\n\n  // ...\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eMungoHealableError\u003c/summary\u003e\n\n- Implements `HealableError`\n- `init` takes `source: ErrorSource` \u0026 `message: String`\n- `init` additionally takes `healOption: HealOption`\n\nExample Usage:\n\n```swift\nfunc fetchImage(urlPath: String) {\n  guard let url = URL(string: urlPath) else {\n    let healingOption = HealingOption(style: .recommended, title: \"Retry\") { [weak self] in self?.fetchImage(urlPath: urlPath) }\n    throw MungoHealableError(source: .invalidUserInput, message: \"Invalid Path\", healingOption: healingOption)\n  }\n\n  // ...\n}\n```\n\n\u003c/details\u003e\n\n### Error Handling\n\nMungoHealer makes handling errors easier by providing the `ErrorHandler` protocol and a default implementation of it based on alert views, namely `AlertLogErrorHandler`.\n\nThe easiest way to get started with MungoHealer is to use a global variable and set it in your AppDelegate.swift like this:\n\n```swift\nimport MungoHealer\nimport UIKit\n\nvar mungo: MungoHealer!\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n    var window: UIWindow?\n\n    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -\u003e Bool {\n        configureMungoHealer()\n        return true\n    }\n\n    private func configureMungoHealer() {\n        let errorHandler = AlertLogErrorHandler(window: window!, logError: { print(\"Error: \\($0)\") })\n        mungo = MungoHealer(errorHandler: errorHandler)\n    }\n}\n```\n\nNote that the following steps were taken in the above code:\n\n1. Add `import MungoHealer` at the top\n2. Add `var mungo: MungoHealer!` global variable\n3. Add a private `configureMungoHealer()` method\n4. Provide your preferred `logError` handler (e.g. [SwiftyBeaver](https://github.com/SwiftyBeaver/SwiftyBeaver))\n5. Call `configureMungoHealer()` on app launch\n\nAs you can see, the `AlertLogErrorHandler` receives two parameters: The first is the `window` so it can find the current view controller to present alerts within. The second is a error log handler – the `AlertLogErrorHandler` not only presents alerts when there is a localized error, but it will also log all  errors calling the `logError` handler with the errors localized description.\n\n#### Custom `ErrorHandler`\n\nWhile starting with the `AlertLogErrorHandler` is recommended by default, you might of course want to handle errors differently then just with system alerts \u0026 logs. For these cases, you just need to implement your own error handler by conforming to `ErrorHandler` which requires the following methods:\n\n- `handle(error: Error)`: Called for \"normal\" error types.\n- `handle(baseError: BaseError)`: Called for base error types.\n- `handle(fatalError: FatalError)`: Called for fatal error types – App should crash at the end of this method.\n- `handle(healableError: HealableError)`: Calles for healable error types.\n\nSee the implementation of `AlertLogErrorHandler` [here](https://github.com/JamitLabs/MungoHealer/blob/stable/Frameworks/MungoHealer/ErrorHandlers/AlertLogErrorHandler.swift) for a working example.\n\nNote that you don't have to use a single global variable named `mungo` as in the example above. You could also write your own Singleton with multiple `MungoHealer` objects, each with a different `ErrorHandler` type. This way you could choose to either show an alert or your custom handling, depending on the context. The Singleton might look something like this:\n\n```swift\nenum ErrorHandling {\n    static var alertLogHandler: MungoHealer!\n    static var myCustomHandler: MungoHealer!\n}\n```\n\n### Usage Example\n\nOnce you've written your own error types and configured your error handler, you should write throwing methods and deal with errors either directly (as before) or use MungoHealer's `handle` method which will automatically deal with the error cases.\n\nHere's a throwing method:\n\n```swift\nprivate func fetchImage(urlPath: String) throws -\u003e UIImage {\n    guard let url = URL(string: urlPath) else {\n        throw StringNotAValidURLFatalError()\n    }\n\n    guard let data = try? Data(contentsOf: url) else {\n        throw NetworkUnavailableError(retryClosure: { [weak self] intry self?.loadAvatarImage() })\n    }\n\n    guard let image = UIImage(data: data) else {\n        throw InvalidDataError()\n    }\n\n    return image\n}\n```\n\nYou can see that different kinds of errors could be thrown here. All of them can be handled at once as easy as this:\n\n```swift\nprivate func loadAvatarImage() {\n    do {\n        imageView.image = try fetchImage(urlPath: user.avatarUrlPath)\n    } catch {\n        mungo.handle(error)\n    }\n}\n```\n\nWe don't need to deal with error handling on the call side which  makes our code both more readable \u0026 more fun to write. Instead, we define how to deal with the errors at the point where the error is thrown/defined. On top of that, the way errors are communicated to the user is abstracted away and can be changed App-wide by simply editing the error handler code. This also makes it possible to handle errors in the model or networking layer without referencing any `UIKit` classes.\n\nFor cases where you just want one catch-all where you just call the `handle(error)` method, there's even a shorthand which will deal with this automatically. Just use this instead of the above code:\n\n```swift\nprivate func loadAvatarImage() {\n    mungo.do {\n        imageView.image = try fetchImage(urlPath: user.avatarUrlPath)\n    }\n}\n```\n\nSo as you can see, used wisely, MungoHealer can help to make your code **cleaner**, **less error prone** and it can **improve the User Experience** for your users.\n\n## Contributing\n\nSee the file [CONTRIBUTING.md](https://github.com/JamitLabs/MungoHealer/blob/stable/CONTRIBUTING.md).\n\n\n## License\nThis library is released under the [MIT License](http://opensource.org/licenses/MIT). See LICENSE for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamitlabs%2Fmungohealer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjamitlabs%2Fmungohealer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamitlabs%2Fmungohealer/lists"}