AlertKit is a lightweight library which makes SwiftUI
alert and action sheet presentation super easy to use.
Using Swift Package Manager, add it as a Swift Package in Xcode 11.0 or later, select File > Swift Packages > Add Package Dependency...
and add the repository URL:
https://github.com/rebeloper/AlertKit.git
Download and include the AlertKit
folder and files in your codebase.
- iOS 14+
- Swift 5
Import AlertKit
into your View
import AlertKit
Here's the list of the awesome features AlertKit
has:
- programatic way to show
Alert
s in SwiftUI - you don't have to add
Alert
s as view modifiers any more - supports
Action Sheet
s - blends in perfectly with all other SwiftUI functioanlity and principles
In SwiftUI
alerts are added as view modifiers with a bit of help from @State
:
struct ContentView: View {
@State private var isSwiftUIAlertPresented = false
var body: some View {
VStack {
Button("SwiftUI Alert") {
isSwiftUIAlertPresented = true
}.alert(isPresented: $isSwiftUIAlertPresented) {
Alert(title: Text("SwiftUI Alert"))
}
Spacer()
}
}
This 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 π€
With AlertKit
you can invoke an Alert
as simple as calling:
alertManager.show(dismiss: .success(message: "AlertKit is awesome"))
Using AlertKit
is super simple:
- create a
@StateObject
variable ofAlertManager()
- add the
.uses(_:)
view-modifier - show an alert π€©
import AlertKit
struct ContentView: View {
// 1.
@StateObject var alertManager = AlertManager()
var body: some View {
VStack {
Button("Show Dismiss Alert") {
// 3.
alertManager.show(dismiss: .success(message: "AlertKit is awesome"))
}
}
.uses(alertManager) // 2.
}
}
There are two types of alerts in SwiftUI
. The Dismiss Alert
is one of them. It presents an alert with:
- title
- message
- one button (dismiss)
alertManager.show(dismiss: .success(message: "AlertKit is awesome"))
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.
Important: Make sure that you use the dismiss
ones with the dismiss
alert and the primarySeconday
with the primarySecoondary
alert.
alertManager.show(dismiss: .custom(title: "AlertKit", message: "AlertKit is awesome", dismissButton: .cancel()))
alertManager.show(dismiss: .success(message: "AlertKit is awesome"))
alertManager.show(dismiss: .error(message: "AlertKit is awesome"))
alertManager.show(dismiss: .warning(message: "AlertKit is awesome"))
alertManager.show(dismiss: .info(message: "AlertKit is awesome"))
The second type of alert displayes two buttons (instead of one):
- title
- message
- two buttons (primary and secondary)
Here are the ways you may call it:
alertManager.show(primarySecondary: .custom(title: "AlertKit", message: "AlertKit is awesome", primaryButton: Alert.Button.destructive(Text("OK")), secondaryButton: Alert.Button.cancel()))
alertManager.show(primarySecondary: .success(title: "AlertKit", message: "AlertKit is awesome", primaryButton: Alert.Button.destructive(Text("OK")), secondaryButton: Alert.Button.cancel()))
alertManager.show(primarySecondary: .error(title: "AlertKit", message: "AlertKit is awesome", primaryButton: Alert.Button.destructive(Text("OK")), secondaryButton: Alert.Button.cancel()))
alertManager.show(primarySecondary: .warning(title: "AlertKit", message: "AlertKit is awesome", primaryButton: Alert.Button.destructive(Text("OK")), secondaryButton: Alert.Button.cancel()))
alertManager.show(primarySecondary: .info(title: "AlertKit", message: "AlertKit is awesome", primaryButton: Alert.Button.destructive(Text("OK")), secondaryButton: Alert.Button.cancel()))
Want more than two buttons on the Alert
? Well, you will have to use an Action Sheet
:
Button("Show Action Sheet") {
let buttons: [ActionSheet.Button] = [
.destructive(Text("Do some work"), action: {
fetchData()
}),
.default(Text("Nothing")),
.cancel()
]
alertManager.showActionSheet(.custom(title: "Action Sheet", message: "What do you want to do next?", buttons: buttons))
}
...
func fetchData {
...
}
Note 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 π
Speaking 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:
//
// ContentViewModel.swift
//
import SwiftUI
class ContentViewModel: ObservableObject {
func fetchData(completion: @escaping (Result<Bool, Error>) -> ()) {
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 4) {
DispatchQueue.main.async {
// completion(.success(true))
completion(.success(false))
// completion(.failure(NSError(domain: "Could not fetch data", code: 404, userInfo: nil)))
}
}
}
}
And in my ContentView
I'm using it like so:
//
// ContentView.swift
//
import SwiftUI
import AlertKit
struct ContentView: View {
@StateObject var alertManager = AlertManager()
@ObservedObject private var viewModel = ContentViewModel()
var body: some View {
VStack {
...
}
.uses(alertManager)
.onAppear {
fetchData()
}
}
func fetchData() {
// to see all cases please modify the Result in ContentViewModel
self.viewModel.fetchData { (result) in
switch result {
case .success(let finished):
if finished {
alertManager.show(dismiss: .info(message: "Successfully fetched data", dismissButton: Alert.Button.default(Text("Alright"))))
} else {
alertManager.show(primarySecondary: .error(title: "π€", message: "Something went wrong", primaryButton: Alert.Button.default(Text("Try again"), action: {
fetchData()
}), secondaryButton: .cancel()))
}
case .failure(let err):
alertManager.show(dismiss: .error(message: err.localizedDescription))
}
}
}
}
Custom alerts are different as they have a few more steps to set up.
- Declare one ore more
CustomAlertManager
s:
@StateObject var customAlertManager = CustomAlertManager()
@StateObject var customAlertManager2 = CustomAlertManager()
You have to declare one for each custom alert that you want to present.
- Optionally, if you are using
TextField
s you have to set a@State
variable that will hold the text value
@State private var customAlertText: String = ""
- Set up the custom alert on the root View:
VStack {
...
}
.customAlert(manager: customAlertManager, content: {
VStack {
Text("Hello Custom Alert").bold()
TextField("Enter email", text: $customAlertText).textFieldStyle(RoundedBorderTextFieldStyle())
}
}, buttons: [
.cancel(content: {
Text("Cancel").bold()
}),
.regular(content: {
Text("Send")
}, action: {
print("Sending email: \(customAlertText)")
})
])
You may add any View
as the content
of your custom alert. You have two button types:
.regular
has an action; it dismisses the alert with that action.cancel
has only content and no action; it dismisses the alert without any action
.customAlert(manager: customAlertManager2, content: {
VStack(spacing: 12) {
Text("Hello Custom Alert 2").bold()
Text("Some message here")
}
}, buttons: [
.regular(content: {
Text("Go")
}, action: {
print("Go")
}),
.cancel(content: {
Image(systemName: "bell.slash.fill").resizable().frame(width: 33, height: 33).foregroundColor(Color(.systemPurple))
}),
.cancel(content: {
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.").bold()
})
])
IMPORTANT: You must provide at least one button!
- Triger the custom alert(s):
VStack {
...
Button(action: {
customAlertManager.show()
}, label: {
Text("Show custom alert")
})
Button(action: {
customAlertManager2.show()
}, label: {
Text("Show custom alert 2")
})
}
For a comprehensive Demo project check out: AlertKitDemo
rebeloper.com / YouTube / Shop / Mentoring
The MIT License (MIT)
Permission 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:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE 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.