{"id":18421590,"url":"https://github.com/rebeloper/alertkit","last_synced_at":"2025-04-07T14:31:53.421Z","repository":{"id":169311081,"uuid":"324331596","full_name":"rebeloper/AlertKit","owner":"rebeloper","description":"🚨 SwiftUI alerts (and action sheets) done right","archived":false,"fork":false,"pushed_at":"2022-03-02T14:20:41.000Z","size":54,"stargazers_count":81,"open_issues_count":3,"forks_count":15,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-22T19:43:59.259Z","etag":null,"topics":["actionsheet","actionsheetview","alert","alert-swiftui","alertmanager","alertview","alertview-pickview-sheetview","swiftui-action-sheet","swiftui-alert","swifui"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rebeloper.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2020-12-25T09:30:16.000Z","updated_at":"2025-02-26T21:26:17.000Z","dependencies_parsed_at":null,"dependency_job_id":"cea09334-a986-45b5-a4ca-abbf8b881b53","html_url":"https://github.com/rebeloper/AlertKit","commit_stats":null,"previous_names":["rebeloper/alertkit"],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rebeloper%2FAlertKit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rebeloper%2FAlertKit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rebeloper%2FAlertKit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rebeloper%2FAlertKit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rebeloper","download_url":"https://codeload.github.com/rebeloper/AlertKit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247669945,"owners_count":20976480,"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","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":["actionsheet","actionsheetview","alert","alert-swiftui","alertmanager","alertview","alertview-pickview-sheetview","swiftui-action-sheet","swiftui-alert","swifui"],"created_at":"2024-11-06T04:26:11.719Z","updated_at":"2025-04-07T14:31:53.394Z","avatar_url":"https://github.com/rebeloper.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🚨 AlertKit\n\n![swift v5.3](https://img.shields.io/badge/swift-v5.3-orange.svg)\n![platform iOS](https://img.shields.io/badge/platform-iOS-blue.svg)\n![deployment target iOS 14](https://img.shields.io/badge/deployment%20target-iOS%2014-blueviolet)\n\n**AlertKit** is a lightweight library which makes `SwiftUI` alert and action sheet presentation super easy to use.\n\n## 💻 Installation\n### 📦 Swift Package Manager\nUsing \u003ca href=\"https://swift.org/package-manager/\" rel=\"nofollow\"\u003eSwift Package Manager\u003c/a\u003e, add it as a Swift Package in Xcode 11.0 or later, `select File \u003e Swift Packages \u003e Add Package Dependency...` and add the repository URL:\n```shell\nhttps://github.com/rebeloper/AlertKit.git\n```\n### ✊ Manual Installation\nDownload and include the `AlertKit` folder and files in your codebase.\n\n### 📲 Requirements\n- iOS 14+\n- Swift 5\n\n## 👉 Import\n\nImport `AlertKit` into your `View`\n\n``` swift\nimport AlertKit\n```\n\n## 🧳 Features\n\nHere's the list of the awesome features `AlertKit` has:\n- [X] programatic way to show `Alert`s in SwiftUI\n- [X] you don't have to add `Alert`s as view modifiers any more\n- [X] supports `Action Sheet`s\n- [X] blends in perfectly with all other SwiftUI functioanlity and principles\n\n## 😤 The Problem\n\nIn `SwiftUI` alerts are added as view modifiers with a bit of help from `@State`:\n\n``` swift\nstruct ContentView: View {\n    \n    @State private var isSwiftUIAlertPresented = false\n    \n    var body: some View {\n        VStack {\n            \n            Button(\"SwiftUI Alert\") {\n                isSwiftUIAlertPresented = true\n            }.alert(isPresented: $isSwiftUIAlertPresented) {\n                Alert(title: Text(\"SwiftUI Alert\"))\n            }\n            \n            Spacer()\n        }\n    }\n```\n\nThis will get ugly really quickly if you're trying to add multiple `Alert`s on a view. Lots of `@State`s with `Alert`s scattered all around your view 🤭\n\nWith `AlertKit` you can invoke an `Alert` as simple as calling: \n\n``` swift\nalertManager.show(dismiss: .success(message: \"AlertKit is awesome\"))\n```\n\n## ⚙️ How to use\n\nUsing `AlertKit` is super simple:\n\n1. create a `@StateObject` variable of `AlertManager()`\n2. add the `.uses(_:)` view-modifier\n3. show an alert 🤩\n\n``` swift\nimport AlertKit\n\nstruct ContentView: View {\n    \n    // 1.\n    @StateObject var alertManager = AlertManager()\n    \n    var body: some View {\n        VStack {\n            Button(\"Show Dismiss Alert\") {\n                // 3.\n                alertManager.show(dismiss: .success(message: \"AlertKit is awesome\"))\n            }\n        }\n        .uses(alertManager) // 2.\n    }\n}\n```\n\n### 1️⃣ Dismiss Alert\n\nThere are two types of alerts in `SwiftUI`. The `Dismiss Alert` is one of them. It presents an alert with:\n - [X] title\n - [X] message\n - [X] **one** button (dismiss)\n \n ``` swift\n alertManager.show(dismiss: .success(message: \"AlertKit is awesome\"))\n ```\n \n `AlertKit` comes with some predifined helpers to make your life easier. In all of the above the only variable is the `meassage`. `Title` and `button(s)` are predifined if that is the case. Of course you may override any or all of them if you wish. \n **Important:** Make sure that you use the `dismiss` ones with the `dismiss` alert and the `primarySeconday` with the `primarySecoondary` alert.\n\n``` swift\nalertManager.show(dismiss: .custom(title: \"AlertKit\", message: \"AlertKit is awesome\", dismissButton: .cancel()))\n\nalertManager.show(dismiss: .success(message: \"AlertKit is awesome\"))\n\nalertManager.show(dismiss: .error(message: \"AlertKit is awesome\"))\n\nalertManager.show(dismiss: .warning(message: \"AlertKit is awesome\"))\n\nalertManager.show(dismiss: .info(message: \"AlertKit is awesome\"))\n```\n\n### 2️⃣ PrimarySecondary Alert\n\nThe second type of alert displayes two buttons (instead of one):\n- [X] title\n- [X] message\n- [X] **two** buttons (primary and secondary)\n\nHere are the ways you may call it:\n\n``` swift\nalertManager.show(primarySecondary: .custom(title: \"AlertKit\", message: \"AlertKit is awesome\", primaryButton: Alert.Button.destructive(Text(\"OK\")), secondaryButton: Alert.Button.cancel()))\n\nalertManager.show(primarySecondary: .success(title: \"AlertKit\", message: \"AlertKit is awesome\", primaryButton: Alert.Button.destructive(Text(\"OK\")), secondaryButton: Alert.Button.cancel()))\n\nalertManager.show(primarySecondary: .error(title: \"AlertKit\", message: \"AlertKit is awesome\", primaryButton: Alert.Button.destructive(Text(\"OK\")), secondaryButton: Alert.Button.cancel()))\n\nalertManager.show(primarySecondary: .warning(title: \"AlertKit\", message: \"AlertKit is awesome\", primaryButton: Alert.Button.destructive(Text(\"OK\")), secondaryButton: Alert.Button.cancel()))\n\nalertManager.show(primarySecondary: .info(title: \"AlertKit\", message: \"AlertKit is awesome\", primaryButton: Alert.Button.destructive(Text(\"OK\")), secondaryButton: Alert.Button.cancel()))\n```\n\n### ⬆️ Action Sheet\n\nWant **more than two buttons** on the `Alert`? Well, you will have to use an `Action Sheet`:\n\n``` swift\nButton(\"Show Action Sheet\") {\n    let buttons: [ActionSheet.Button] = [\n        .destructive(Text(\"Do some work\"), action: {\n            fetchData()\n        }),\n        .default(Text(\"Nothing\")),\n        .cancel()\n    ]\n    alertManager.showActionSheet(.custom(title: \"Action Sheet\", message: \"What do you want to do next?\", buttons: buttons))\n}\n\n...\n\nfunc fetchData {\n    ...\n}\n```\n\nNote that you can use all of the `Alert.Button`s SwiftUI provides. Here I'm using a `destructive` with `action` button and have wrapped the actual work into a seperate `fetchData()` function. Cleaner code 👌\n\n### 🤖 View Model\n\nSpeaking of clean code... I highly recommend using a `view model` for your view. Here's mine that is simulating fetching some data by simply letting time pass:\n\n``` swift\n//\n//  ContentViewModel.swift\n//  \n\nimport SwiftUI\n\nclass ContentViewModel: ObservableObject {\n    \n    func fetchData(completion: @escaping (Result\u003cBool, Error\u003e) -\u003e ()) {\n        DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 4) {\n            DispatchQueue.main.async {\n                // completion(.success(true))\n                completion(.success(false))\n                // completion(.failure(NSError(domain: \"Could not fetch data\", code: 404, userInfo: nil)))\n            }\n        }\n    }\n}\n```\n\nAnd in my `ContentView` I'm using it like so:\n\n``` swift\n//\n//  ContentView.swift\n//  \n\nimport SwiftUI\nimport AlertKit\n\nstruct ContentView: View {\n    \n    @StateObject var alertManager = AlertManager()\n    @ObservedObject private var viewModel = ContentViewModel()\n    \n    var body: some View {\n        VStack {\n            ...\n        }\n        .uses(alertManager)\n        .onAppear {\n            fetchData()\n        }\n    }\n    \n    func fetchData() {\n        // to see all cases please modify the Result in ContentViewModel\n        self.viewModel.fetchData { (result) in\n            switch result {\n            case .success(let finished):\n                if finished {\n                    alertManager.show(dismiss: .info(message: \"Successfully fetched data\", dismissButton: Alert.Button.default(Text(\"Alright\"))))\n                } else {\n                    alertManager.show(primarySecondary: .error(title: \"🤔\", message: \"Something went wrong\", primaryButton: Alert.Button.default(Text(\"Try again\"), action: {\n                        fetchData()\n                    }), secondaryButton: .cancel()))\n                }\n            case .failure(let err):\n                alertManager.show(dismiss: .error(message: err.localizedDescription))\n            }\n        }\n    }\n}\n```\n\n## 🎨 Custom Alert\n\nCustom alerts are different as they have a few more steps to set up.\n\n1. Declare one ore more  `CustomAlertManager`s:\n\n``` swift\n@StateObject var customAlertManager = CustomAlertManager()\n\n@StateObject var customAlertManager2 = CustomAlertManager()\n```\n\nYou have to declare one for each custom alert that you want to present.\n\n2. Optionally, if you are using `TextField`s you have to set a `@State` variable that will hold the text value\n\n``` swift\n@State private var customAlertText: String = \"\"\n```\n\n3. Set up the custom alert on the root View:\n\n``` swift\nVStack {\n    ...\n}\n.customAlert(manager: customAlertManager, content: {\n    VStack {\n        Text(\"Hello Custom Alert\").bold()\n        TextField(\"Enter email\", text: $customAlertText).textFieldStyle(RoundedBorderTextFieldStyle())\n    }\n}, buttons: [\n    .cancel(content: {\n        Text(\"Cancel\").bold()\n    }),\n    .regular(content: {\n        Text(\"Send\")\n    }, action: {\n        print(\"Sending email: \\(customAlertText)\")\n    })\n])\n```\n\nYou may add any `View` as the `content` of your custom alert. You have two button types: \n- `.regular` has an action; it dismisses the alert with that action\n- `.cancel` has only content and **no** action; it dismisses the alert without any action\n\n``` swift\n.customAlert(manager: customAlertManager2, content: {\n    VStack(spacing: 12) {\n        Text(\"Hello Custom Alert 2\").bold()\n        Text(\"Some message here\")\n    }\n}, buttons: [\n    .regular(content: {\n        Text(\"Go\")\n    }, action: {\n        print(\"Go\")\n    }),\n    .cancel(content: {\n        Image(systemName: \"bell.slash.fill\").resizable().frame(width: 33, height: 33).foregroundColor(Color(.systemPurple))\n    }),\n    .cancel(content: {\n        Text(\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\").bold()\n    })\n])\n```\n\n**IMPORTANT: You must provide at least one button!**\n\n4. Triger the custom alert(s):\n\n``` swift\nVStack {\n    ...\n    Button(action: {\n        customAlertManager.show()\n    }, label: {\n        Text(\"Show custom alert\")\n    })\n    \n    Button(action: {\n        customAlertManager2.show()\n    }, label: {\n        Text(\"Show custom alert 2\")\n    })\n}\n```\n\n## 🪁 Demo project\n\nFor a comprehensive Demo project check out: \n\u003ca href=\"https://github.com/rebeloper/AlertKitDemo\"\u003eAlertKitDemo\u003c/a\u003e\n\n## ✍️ Contact\n\n\u003ca href=\"https://rebeloper.com/\"\u003erebeloper.com\u003c/a\u003e / \n\u003ca href=\"https://www.youtube.com/rebeloper/\"\u003eYouTube\u003c/a\u003e / \n\u003ca href=\"https://store.rebeloper.com/\"\u003eShop\u003c/a\u003e / \n\u003ca href=\"https://rebeloper.com/mentoring\"\u003eMentoring\u003c/a\u003e\n\n## 📃 License\n\nThe MIT License (MIT)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frebeloper%2Falertkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frebeloper%2Falertkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frebeloper%2Falertkit/lists"}