Local notifications in iOS are a way for an app to present information to the user while it's not in the foreground. They are used to display alerts, play sounds, or badge the app icon
1.Set up the UI first
- In the view controller,
+
can be added
- Link
+
button to second view controller
- In the view controller, enter 1 to prototype cells and add labels
- In the second view controller, date picker can be set up as below
2.Give an identifier to Table View Cell, ToDoCell
3.Create a separate class ToDoTVCell
for the Table View Cell, ToDoCell
we just created
4.Link ToDoCell
with ToDoTVCell
5.Create outlets for two labels in the ToDoTVCell
class
import UIKit
class ToDoTVCell: UITableViewCell {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var dateLabel: UILabel!
}
6.Create a class file, AddToDoVC
and link with the view controller
7.Define the struct
import Foundation
struct ToDoItem {
let title: String
let deadline: Date
}
8.AddToDoVC code
import UIKit
class AddToDoVC: UIViewController {
@IBOutlet weak var titleTextField: UITextField!
@IBOutlet weak var deadlineDatePicker: UIDatePicker!
// closure comes to rescue for passing the new item
// to able to add this to an array (toDoItems in the ViewController)
// we use type function - optional
var onSave: ((ToDoItem) -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func addToDo(_ sender: UIButton) {
guard let title = titleTextField.text, !title.isEmpty else {
return
}
let deadline = deadlineDatePicker.date
print(deadline)
// create an instnace of new item
let newItem = ToDoItem(title: title, deadline: deadline)
// use closure
onSave?(newItem)
// go back to main
self.navigationController?.popViewController(animated: true)
}
}
9.View controller code
import UIKit
// import this for notification
import UserNotifications
class ViewController: UIViewController {
var toDoItems: [ToDoItem] = [] {
didSet {
tableView.reloadData()
}
}
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
}
func scheduleNotification(for item: ToDoItem) {
let content = UNMutableNotificationContent()
content.title = "ToDo Reminder"
content.body = "Don't forget: \(item.title)"
content.sound = .default
// get the DateComponents for dateMatching for creating a trigger
let calender = Calendar.current
let components = calender.dateComponents([.year, .month, .day, .hour, .minute], from: item.deadline)
// create a trigger
let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: false)
// to set the notification -> we need to create a notification request
// don't use item.title for the indentifier. not recommended. maybe uuid
let request = UNNotificationRequest(identifier: item.title, content: content, trigger: trigger)
// add the request to the notification center
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("Error scheduling notification: \(error)")
}
}
}
// *use closure here
// how to transfer data from destination to source
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let addToDoVC = segue.destination as? AddToDoVC else {
return
}
// will be invoked when onSave() was called in addToDo() of AddToDoVC using closure
// you can assign code block to onSave()
addToDoVC.onSave = { newItem in
self.toDoItems.append(newItem)
// schedule notification
self.scheduleNotification(for: newItem)
}
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return toDoItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ToDoCell", for: indexPath) as? ToDoTVCell else { return UITableViewCell() }
let toDoItem = toDoItems[indexPath.row]
// date formatter
let formatter = DateFormatter()
formatter.dateFormat = "dd-MM-yyyy HH:mm"
let formmattedDate = formatter.string(from: toDoItem.deadline)
cell.titleLabel.text = toDoItem.title
cell.dateLabel.text = formmattedDate
return cell
}
// delete the data -> remove pending notification as well
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// cancel the notification
let item = toDoItems[indexPath.row]
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [item.title])
// remove the item from the data source
toDoItems.remove(at: indexPath.row)
}
}
}
10.Add code to get the authorization from users whether they will allow notification or not
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
// this one is the first one invoked when running the application
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// get the authorization from user to allow notification
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound]) { granted, error in
// handle the response if needed
// command + shift + h -> minimize the app to see if notification works!
}
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
}
10.Demo
minimize the app to see whether notification works using cmd + shift + h
you can delete the row as well, which will also delete the pending notification
Top comments (0)