{"id":23143103,"url":"https://github.com/cleancocoa/errorhandling","last_synced_at":"2025-08-24T08:27:04.033Z","repository":{"id":65094339,"uuid":"44795031","full_name":"CleanCocoa/ErrorHandling","owner":"CleanCocoa","description":"AppKit drop-in error handling and reporting mechanism","archived":false,"fork":false,"pushed_at":"2024-05-10T07:46:54.000Z","size":180,"stargazers_count":20,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-07T01:29:55.337Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/CleanCocoa.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2015-10-23T06:31:19.000Z","updated_at":"2025-07-30T04:10:02.000Z","dependencies_parsed_at":"2025-05-28T12:32:49.222Z","dependency_job_id":"fe21c059-3eb3-4302-8415-1d30ff6de07d","html_url":"https://github.com/CleanCocoa/ErrorHandling","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/CleanCocoa/ErrorHandling","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CleanCocoa%2FErrorHandling","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CleanCocoa%2FErrorHandling/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CleanCocoa%2FErrorHandling/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CleanCocoa%2FErrorHandling/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CleanCocoa","download_url":"https://codeload.github.com/CleanCocoa/ErrorHandling/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CleanCocoa%2FErrorHandling/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270586458,"owners_count":24611317,"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-08-15T02:00:12.559Z","response_time":110,"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":[],"created_at":"2024-12-17T15:12:10.337Z","updated_at":"2025-08-15T15:13:29.311Z","avatar_url":"https://github.com/CleanCocoa.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ErrorHandling\nb\nSooner or later, every program crashes or does something weird. Even when the app can recover, it's best to make the user report problems sooner or later.\n\nInstead of deferring this until later ...\n\n```swift\ndo {\n    try managedObjectContext.save()\n} catch let error as NSError {\n    fatalError(\"\\(error)\") // TODO report crash\n}\n```\n\n... drop in this micro-framework and have the proper handling in place. You can still worry about the \"how\" of reporting errors later. But now you will know that errors are handled:\n\n\n```swift\ndo {\n    try managedObjectContext.save()\n} catch let error as NSError {\n    applicationError(\"Saving MOC failed: \\(error)\")\n    // Optionally help the user rescue the data:\n    // saveManagedObjectContextChangesToPlist(managedObjectContext)\n    return \n}\n```\n\n## Set-up\n\nSet up the reporter at the beginning of your app. You can do this during [bootstrapping](http://christiantietze.de/posts/2015/10/bootstrapping-appdelegate/) or simply call this in AppDelegate:\n\n```swift\nimport ErrorHandling\n\nfunc bootstrapErrorReporting() {\n    ErrorAlert.emailer = TextEmailer()\n}\n```\n\n![Info.plist setup](info-plist.png)\n\nThe default `TextEmailer` expects your e-mail address in your app's `Info.plist` under the key \"**SupportEmail**\". Simply add this key with a string value to make it work.\n\n\n## Usage\n\n![Default Error Dialog](screenshot.png)\n\nThe framework includes:\n\n* `ErrorAlert` to display alerts with an error message, informing the user about what happened.\n* `TextEmailer` is the default handler of errors. It simply uses the `mailto:` protocol to send a mail with the system's default e-mail program. You can drop in anything you'd like as replacement which conforms to the `ReportEmailer` protocol.\n\nAll you have to do is call:\n\n```swift\nlet error: NSError = ...\nErrorAlert(error: error).displayModal()\n```\n    \nAnd you're set.\n\n### Reporting (and e-mailing) additional info\n\nThe `Report` type allows you to wrap any `Error` with a custom string that should be sent along with the report email.\n\n```swift\nlet error: Error = ...\nlet logs = previousLogMessages.joined(separator: \"\\n\")\nlet report = Report(error: error, additionalInfo: logs)\nErrorAlert(report: report).displayModal()\n```\n\n#### Example: Logging to an array with SwiftyBeaver\n\nIf you happen to use [SwiftyBeaver](https://github.com/SwiftyBeaver/SwiftyBeaver) for logging, here's a `InMemoryDestination` that keeps track of the past log messages so you can send them along in your report:\n\n```swift\nimport SwiftyBeaver\n\nclass InMemoryDestination: BaseDestination {\n\n    var maxHistory = 5\n    fileprivate(set) var messages: [String] = []\n\n    override func send(_ level: SwiftyBeaver.Level, msg: String, thread: String, file: String, function: String, line: Int) -\u003e String? {\n\n        let formattedString = super.send(level, msg: msg, thread: thread, file: file, function: function, line: line) ?? \"\\(msg) (formatting error!)\"\n\n        messages = Array(messages\n            .appending(formattedString)\n            .suffix(maxHistory))\n\n        return formattedString\n    }\n}\n```\n\n\n## Convenience methods\n\nBecause doing this sucks for unexpected errors, I add something to my projects so I can simply do this:\n\n```\nlet error: NSError = ...\nfatalApplicationError(\"Start-up failed: \\(error)\")\n```\n\nOr this, to handle cases when I'm stupid:\n\n```swift\nprogrammerError(\"Unhandled and unexpected exception!!11 \\(error)\")\n```\n    \nOr this, when something that may regularly go wrong goes wrong but isn't handled properly, yet:\n\n```swift\n// Think of it like a feature request! :)\napplicationError(\"File not found: \\(error)\")\n```\n\nHere's some code to achieve just that, as a bonus:\n\n```swift\nimport Foundation\n\nlet errorHandler = ErrorHandler() // This is just for demonstration, of course\n\n@noreturn func fatalApplicationError(message: String, function: String = __FUNCTION__, file: String = __FILE__, line: Int = __LINE__) {\n\n    applicationError(message, function: function, file: file, line: line)\n    fatalError(message)\n}\n\nfunc applicationError(message: String, function: String = __FUNCTION__, file: String = __FILE__, line: Int = __LINE__) {\n\n    let error = ErrorHandler.errorWithMessage(message, function: function, file: file, line: line)\n    errorHandler.handle(error)\n}\n\nfunc programmerError(message: String, function: String = __FUNCTION__, file: String = __FILE__, line: Int = __LINE__) {\n\n    let error = ErrorHandler.errorWithMessage(\"Program fault:\\n \\(message)\", function: function, file: file, line: line)\n    errorHandler.handle(error)\n}\n\npublic class ErrorHandler {\n\n    static let exceptionDomain = \"de.christiantietze.MyNextApp.exception\"\n    static var functionKey: String { return \"\\(exceptionDomain).function\" }\n    static var fileKey: String { return \"\\(exceptionDomain).file\" }\n    static var lineKey: String { return \"\\(exceptionDomain).line\" }\n\n    public init() { }\n}\n\nextension ErrorHandler {\n\n    public func handle(error: NSError?) {\n    \n        if let error = error {\n            logError(error)\n            reportError(error)\n        }\n    }\n    \n    private func logError(error: NSError) {\n        \n        NSLog(\"Error: \\(error)\")\n    }\n    \n    private func reportError(error: NSError) {\n\n        ErrorAlert(error: error).displayModal()\n    }\n\n    public static func errorWithMessage(message: String? = nil, function: String = __FUNCTION__, file: String = __FILE__, line: Int = __LINE__) -\u003e NSError {\n    \n        var userInfo: [String: AnyObject] = [\n            functionKey: function,\n            fileKey: file,\n            lineKey: line,\n        ]\n    \n        if let message = message {\n            userInfo[NSLocalizedDescriptionKey] = message\n        }\n    \n        return NSError(domain: exceptionDomain, code: 0, userInfo: userInfo)\n    }\n}\n```\n\n\n## Privacy Manifest\n\nThe package does not collect or store any data.\n\nThe `TextEmailer` relies on the user's email client to send an email to your support inbox, so it doesn't process the user's email at all.\n \n\n## License\n\nCopyright (c) 2015 Christian Tietze. Distributed under the MIT License.\n\nSee LICENSE file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcleancocoa%2Ferrorhandling","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcleancocoa%2Ferrorhandling","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcleancocoa%2Ferrorhandling/lists"}