swift, detect ibeacons on the background and send notifications when in range - swift

Hello I'm new at IBeacons and beginner on swift and I'm trying to make a small app that detects Ibeacon on the background of the app and send a notification when the Ibeacon is in range I manage to do so but only when I walk while the app is open I could not make it work and search for Ibeacons on the background even though I gave the app access to take the location on the background by using
if (CLLocationManager.authorizationStatus() != CLAuthorizationStatus.authorizedAlways) {
locationManager.requestAlwaysAuthorization();
}
this is my main problem, I have a side problem too that the app does not save the person name if the app is closed and opened again the app will forget the name. Here is my code I'd really appreciate your help so much also please if you have any references to learn more about IBeacons applications I'd appreciate it
import UIKit
import CoreLocation
import UserNotifications
class ViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var field: UITextField!
#IBOutlet weak var textLbl : UILabel!
var inRoom = false
var name = ""
var sendYet = false ;
func sendHiNoti()
{
name = field.text! ;
let content = UNMutableNotificationContent()
content.title = "Heloo "+name
content.subtitle = "Welcome to your Home"
content.body = "this messaage to welcome you in Home"
content.badge = 1
content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "quiteimpressed.mp3"))
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 2, repeats: false)
let request = UNNotificationRequest(identifier: "azanSoon", content: content, trigger: trigger)
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
UNUserNotificationCenter.current().add(request) {(error) in
if let error = error {
print("error: \(error)")
}
}
}
func sendByeNoti()
{
name = field.text! ;
let content = UNMutableNotificationContent()
content.title = "OH"+name
content.subtitle = "You are going out already ??"
content.body = "Take care of your self"
content.badge = 1
content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "quiteimpressed.mp3"))
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 2, repeats: false)
let request = UNNotificationRequest(identifier: "azanSoon", content: content, trigger: trigger)
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
UNUserNotificationCenter.current().add(request) {(error) in
if let error = error {
print("error: \(error)")
}
}
}
#IBAction func getText(){
name = field.text!
let alert = UIAlertController(title: "Your Name is", message: name, preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge], completionHandler: {didAllow, error in})
let uuid = UUID(uuidString: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0")!
let beaconRegion = CLBeaconRegion(proximityUUID: uuid, major: 444, minor: 333, identifier: "abcdefac005b")
if (CLLocationManager.authorizationStatus() != CLAuthorizationStatus.authorizedAlways) {
locationManager.requestAlwaysAuthorization();
}
locationManager.startRangingBeacons(in: beaconRegion)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
print(beacons)
if(beacons.count > 0){
if(!sendYet){
if beacons[0].proximity.rawValue < 2 {
textLbl.text = "In the room"
sendHiNoti()
sendYet = true ;
}
}
else if beacons[0].proximity.rawValue >= 3 {
textLbl.text = "Outside the room"
sendByeNoti()
sendYet = false ;
}
}
}
}

The code shown uses beacon ranging locationManager.startRangingBeacons(in: beaconRegion) which is generally not supported in the background for more than 10 seconds after transition between foreground and background.
The locationManager.requestAlwaysAuthorization() will only unlock the ability to use beacon monitoring in the background. Beacon monitoring gives you a single call when beacons either first appear (didEnter(region: region)) or all disappear(didExit(region: region)).
This is the only beacon API which works in the background under normal circumstances.
It is possible to do beacon ranging in the background for longer than 10 seconds using two techniques:
You can get 180 seconds of background ranging after the app transitions to the background by starting a background task as described in my blog post here.
You can also tell iOS that you are a location app to unlock unlimited background beacon ranging. You must first implement the solution in part 1. Then, in your Info.plist, declare:
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
Finally, in your code run locationManager.startUpdatingLocation(). This will cause your app to receive regular updates of its latitude/longitude, but as a side effect allows you background task from step 1 to run forever, allowing ranging to continue forever in the background.
If you choose to use option 2, beware that it will be more difficult to get your app approved for sale in the AppStore. You must convince Apple reviewers that your app is a legitimate location app (like Waze or Apple Maps) and the user is aware that your app is always running in the background. If you do not convince them of this, they will reject your app.
Separately, it is simple to save off values to persistent storage so they are retained across app restarts. Just use NSUserDefaults like this:
// save off name when user fills it in
UserDefaults.standard.set(name, forKey: "name")
// load back name on app restart
name = UserDefaults.standard.string(forKey: "name")

Related

Trying to customize notifications in macOS with Swift

I am using macOS 10.5.6 and I am trying to display a custom notification. I am using UNNotificationAction to set up a drop down menu for the notification and UNNotificationCategory to save it. I can get the notification correctly. The title and body are displayed but the popup menu for the notification is displayed under a button labeled "Actions".
What I would like to happen is have the label "Actions" changed to a two button format the way that the Reminders app does. I have spent a couple of days searching this web site and several others trying to find the answer but all I have found is the method I am currently using to set up the notification with out the button format that I would like to display. I know that it can be done I just do not know which key words to use to get the answer I would appreciate any help I can get.
enter image description here
Sample notifications
A notification with an attachment:
A notification with an attachment, mouse is hovering over to make the action buttons visible (they're visible right away if there's no attachment).
Sample project
Delegate
AppDelegate is going to handle notifications in the following sample project. We have to make it conform to the UNUserNotificationCenterDelegate protocol.
import UserNotifications
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate {
...
}
We have to set the UNUserNotificationCenter.delegate to our AppDelegate in order to receive notifications. It must be done in the applicationDidFinishLaunching: method.
func applicationDidFinishLaunching(_ aNotification: Notification) {
setupNotificationCategories() // See below
UNUserNotificationCenter.current().delegate = self
// Other stuff
}
Authorization, capabilities, ... omitted for simplicity.
Constants
An example how to avoid hardcoded constant.
enum Note {
enum Action: String {
case acceptInvitation = "ACCEPT_INVITATION"
case declineInvitation = "DECLINE_INVITATION"
var title: String {
switch self {
case .acceptInvitation:
return "Accept"
case .declineInvitation:
return "Decline"
}
}
}
enum Category: String, CaseIterable {
case meetingInvitation = "MEETING_INVITATION"
var availableActions: [Action] {
switch self {
case .meetingInvitation:
return [.acceptInvitation, .declineInvitation]
}
}
}
enum UserInfo: String {
case meetingId = "MEETING_ID"
case userId = "USER_ID"
}
}
Setup categories
Make the notification center aware of our custom categories and actions. Call this function in the applicationDidFinishLaunching:.
func setupNotificationCategories() {
let categories: [UNNotificationCategory] = Note.Category.allCases
.map {
let actions = $0.availableActions
.map { UNNotificationAction(identifier: $0.rawValue, title: $0.title, options: [.foreground]) }
return UNNotificationCategory(identifier: $0.rawValue,
actions: actions,
intentIdentifiers: [],
hiddenPreviewsBodyPlaceholder: "",
options: .customDismissAction)
}
UNUserNotificationCenter.current().setNotificationCategories(Set(categories))
}
Create a notification content
Sample notification content with an attachment. If we fail to create an
attachment we will continue without it.
func sampleNotificationContent() -> UNNotificationContent {
let content = UNMutableNotificationContent()
content.title = "Hey Jim! Weekly Staff Meeting"
content.body = "Every Tuesday at 2pm"
content.userInfo = [
Note.UserInfo.meetingId.rawValue: "123",
Note.UserInfo.userId.rawValue: "456"
]
content.categoryIdentifier = Note.Category.meetingInvitation.rawValue
// https://developer.apple.com/documentation/usernotifications/unnotificationattachment/1649987-init
//
// The URL of the file you want to attach to the notification. The URL must be a file
// URL and the file must be readable by the current process. This parameter must not be nil.
//
// IOW We can't use image from the assets catalog. You have to add an image to your project
// as a resource outside of assets catalog.
if let url = Bundle.main.url(forResource: "jim#2x", withExtension: "png"),
let attachment = try? UNNotificationAttachment(identifier: "", url: url, options: nil) {
content.attachments = [attachment]
}
return content
}
Important: you can't use an image from the assets catalog, because you need an URL pointing to a file readable by the current process.
Trigger helper
Helper to create a trigger which will fire a notification in seconds seconds.
func triggerIn(seconds: Int) -> UNNotificationTrigger {
let currentSecond = Calendar.current.component(.second, from: Date())
var dateComponents = DateComponents()
dateComponents.calendar = Calendar.current
dateComponents.second = (currentSecond + seconds) % 60
return UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)
}
Notification request
let content = sampleNotificationContent()
let trigger = triggerIn(seconds: 5)
let uuidString = UUID().uuidString
let request = UNNotificationRequest(identifier: uuidString, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { (error) in
if error != nil {
print("Failed to add a notification request: \(String(describing: error))")
}
}
Handle notifications
Following functions are implemented in the sample project AppDelegate.
Background
This is called when your application is in the background (or even if your application is running, see Foreground below).
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler:
#escaping () -> Void) {
guard let action = Note.Action(rawValue: response.actionIdentifier) else {
print("Unknown response action: \(response.actionIdentifier)")
completionHandler()
return
}
let userInfo = response.notification.request.content.userInfo
guard let meetingId = userInfo[Note.UserInfo.meetingId.rawValue] as? String,
let userId = userInfo[Note.UserInfo.userId.rawValue] as? String else {
print("Missing or malformed user info: \(userInfo)")
completionHandler()
return
}
print("Notification response: \(action) meetingId: \(meetingId) userId: \(userId)")
completionHandler()
}
Foreground
This is called when the application is in the foreground. You can handle the notification silently or you can just show it (this is what the code below does).
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler:
#escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert, .badge, .sound])
}
iOS customization
There's another way how to customize the appearance of notifications, but this is not available on the macOS. You have to use attachments.

MacOS App Local Notification Not Showing when testing with XCode

I have tried to add a banner notification generator to my macOS swift app and the banner does not appear when test running in XCode and neither are there any new notifications visible in the notification centre. Other apps on my computer are generating notifications regularly. What have I missed? I have granted permission when requested
My app delegate is as follows
class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDelegate {
#IBOutlet weak var mainMenu: NSMenu!
func applicationDidFinishLaunching(_ aNotification: Notification)
{
NSUserNotificationCenter.default.delegate = self ;
}
func userNotificationCenter(_ center: NSUserNotificationCenter, shouldPresent notification: NSUserNotification) -> Bool
{
return true
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
On app startup I run the following method and I see the console line "Notifications allowed"
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound, .badge, .provisional])
{ granted, error in
if error != nil
{
print ("Request notifications permission Error");
};
if granted
{
self.allowNotifications = true ;
print ("Notifications allowed");
}
else
{
self.allowNotifications = false ;
print ("Notifications denied");
};
}
The method I have added to my ViewController is as follows and I have tested that the print statement at the end is reached
func generateNotification (summary:String, sound:String, title:String , body:String)
{
let notification = NSUserNotification()
if !allowNotifications {return};
notification.title = summary ;
notification.subtitle = title ;
notification.informativeText = body ;
if (sound == "YES") {notification.soundName = NSUserNotificationDefaultSoundName};
NSUserNotificationCenter.default.deliver (notification);
print ("notification generated");
};
Please help me
I believe that my problem here was asking permission to use UNUserNotification and then using NSUserNotification to create the notification itself, which of course I had not requested permission to use. Requesting permission is now mandatory in Catalina (and perhaps it was in earlier versions of macOS as well.)
So I replaced the generateNotification function with the following and it all works correctly.
let notificationCenter = UNUserNotificationCenter.current();
notificationCenter.getNotificationSettings
{ (settings) in
if settings.authorizationStatus == .authorized
{
//print ("Notifications Still Allowed");
// build the banner
let content = UNMutableNotificationContent();
content.title = summary ;
content.body = title ;
if sound == "YES" {content.sound = UNNotificationSound.default};
// could add .badge
// could add .userInfo
// define when banner will appear - this is set to 1 second - note you cannot set this to zero
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false);
// Create the request
let uuidString = UUID().uuidString ;
let request = UNNotificationRequest(identifier: uuidString, content: content, trigger: trigger);
// Schedule the request with the system.
notificationCenter.add(request, withCompletionHandler:
{ (error) in
if error != nil
{
// Something went wrong
}
})
//print ("Notification Generated");
}
In addition to the answer of Steve Brooker, for me it was working only when I set the delegate for UNUserNotificationCenter. I've spent half a day trying to make it work with NSUserNotificationCenter / NSUserNotification without any success. So thanks for your answer, Steve :)
My working version is as follows:
if #available(OSX 10.14, *) {
UNUserNotificationCenter.current().delegate = self // must have delegate, otherwise notification won't appear
UNUserNotificationCenter.current()
.requestAuthorization(options: [.alert, .sound, .badge]) {
[weak self] granted, error in
print("Permission granted: \(granted)")
guard granted else { return }
let sound = "NO"
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.getNotificationSettings
{ (settings) in
if settings.authorizationStatus == .authorized {
//print ("Notifications Still Allowed");
// build the banner
let content = UNMutableNotificationContent();
content.title = "summary" ;
content.body = "title" ;
if sound == "YES" {content.sound = UNNotificationSound.default};
// could add .badge
// could add .userInfo
// define when banner will appear - this is set to 1 second - note you cannot set this to zero
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false);
// Create the request
let uuidString = UUID().uuidString ;
let request = UNNotificationRequest(identifier: uuidString, content: content, trigger: trigger);
// Schedule the request with the system.
notificationCenter.add(request, withCompletionHandler:
{ (error) in
if error != nil
{
// Something went wrong
}
})
//print ("Notification Generated");
}
}
}
} else {
// Fallback on earlier versions
}

How to show region notification for iOS at most once a month?

My app's deployment target is 10.0, and I used UNUserNotificationCenter to show region notification even when the app is closed/killed. But new mission is to show it at most once a month, though the user may enter the region more than once a month.
What I tried until now (which worked great) is...
let content = UNMutableNotificationContent()
content.title = "... Reminder"
content.body = "Welcome to \(element.name). Please let us know how we can serve you and your loved ones, and we hope ... will simplify your visit here."
content.sound = UNNotificationSound.default
content.categoryIdentifier = "LOCATION_CAT"
let centerCoordinate2D = element.location.coordinate
let identifierName = element.name.replacingOccurrences(of: " ", with: "_")
let region = CLCircularRegion(center: centerCoordinate2D, radius: 300, identifier: identifierName)
region.notifyOnExit = false
region.notifyOnEntry = true
let trigger = UNLocationNotificationTrigger(region: region, repeats: true)
// request = content + trigger
let request = UNNotificationRequest(identifier: "REGION \(element.name)", content: content, trigger: trigger)
// add (or "schedule") request to center
let center = UNUserNotificationCenter.current()
center.add(request, withCompletionHandler: { (error: Error?) in
if let theError = error {
print(theError.localizedDescription)
}
})
But then, to let it happen at most once a month, I did the following:
let centerCoordinate2D = element.location.coordinate
let identifierName = element.name.replacingOccurrences(of: " ", with: "_")
let region = CLCircularRegion(center: centerCoordinate2D, radius: 300, identifier: identifierName)
region.notifyOnExit = true
region.notifyOnEntry = true
R.shared.appleLocationManager.startMonitoring(for: region)
Also in AppDelegate.swift,
extension AppDelegate: CLLocationManagerDelegate {
// called when user Enters a monitored region
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
print("AppDelegate.locationManager( didEnterRegion ): called with region identifier: \(region.identifier)")
if region is CLCircularRegion {
// Do what you want if this information
self.handleEvent(forRegion: region)
}
}
func handleEvent(forRegion region: CLRegion) {
// we save 'date' with "NOTIFICATION DATE request_identifier" as its key.
let key = "NOTIFICATION DATE \(region.identifier)"
let defaultValue = defaults.double(forKey: key)
if defaultValue == 0 {
print("AppDelegate.handleEvent(): need to show notification: no key")
// set value first.
defaults.set(Date().timeIntervalSince1970, forKey: key)
showNotification(forRegion: region)
} else {
let diff = Date().timeIntervalSince(Date(timeIntervalSince1970: defaultValue))
if diff > 60 * 60 * 24 * 30 {
print("AppDelegate.handleEvent(): need to show notification: diff > 30 days")
// set value first.
defaults.set(Date().timeIntervalSince1970, forKey: key)
showNotification(forRegion: region)
} else {
// just pass.
print("AppDelegate.handleEvent(): need NOT to show notification: diff: \(dot2(diff / 24 / 60)) mins")
}
}
}
func showNotification(forRegion region: CLRegion, message: String = "") {
// customize your notification content
let content = UNMutableNotificationContent()
content.title = "... Reminder"
let hospitalName = region.identifier.replacingOccurrences(of: "_", with: " ")
content.body = "Welcome to \(hospitalName). \(message) Please let us know how we can serve you and your loved ones, and we hope ... will simplify your visit here."
content.sound = UNNotificationSound.default
// the actual trigger object
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0,
repeats: false)
// notification unique identifier, for this example, same as the region to avoid duplicate notifications
let identifier = "REGION \(hospitalName)"
// the notification request object
let request = UNNotificationRequest(identifier: identifier,
content: content,
trigger: trigger)
// trying to add the notification request to notification center
UNUserNotificationCenter.current().add(request, withCompletionHandler: { (error) in
if let theError = error {
print(theError.localizedDescription)
}
})
}
with the following, still for the AppDelegate class:
let appleLocationManager = CLLocationManager()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.appleLocationManager.delegate = self
...
}
I think there is error in the code, but it is not clear to me if the locationManager( didExitRegion: ) is to be called even when the app is closed/killed - in which case appleLocationManager is not alive?
If I can't use locationManager( didExitRegion: ) for this problem, what can I do to make the region notification happen at most once a month? I also know that there is a different type of trigger, UNTimeIntervalNotificationTrigger or UNLocationNotificationTrigger, and I wanted to use them somehow to solve this problem, but would there be any way to make it run some of my code even when the app is not running at all? If this is impossible to solve, isn't it enough to say that the region notification is too much restricted?
Described as Apple's document, region monitoring with CLLocationManager wakes up your app if needed.
c.f. https://developer.apple.com/documentation/corelocation/monitoring_the_user_s_proximity_to_geographic_regions
Whenever the user crosses the boundary of one of your app's registered regions, the system notifies your app. If an iOS app is not running when the boundary crossing occurs, the system tries to launch it. An iOS app that supports region monitoring must enable the Location updates background mode so that it can be launched in the background.
You can also detect if the app is launched by regional notification with UIApplication.LaunchOptionsKey.
Boundary crossing notifications are delivered to your location manager's delegate object. Specifically, the location manager calls the locationManager(:didEnterRegion:) or locationManager(:didExitRegion:) methods of its delegate. If your app was launched, you must configure a CLLocationManager object and delegate object right away so that you can receive these notifications. To determine whether your app was launched for a location event, look for the UIApplication.LaunchOptionsKey in the launch options dictionary.

iBeacon background ranging

I'm trying to set my application as a way to just start ranging in the background and get a notification when the user hit the shoulder button or home button(not all the time) and I don't want using background mode.
So I coded this in swift, it works when my app is working in background just for 10 seconds and ranging won't be restarted when the user re-enter or exit from region but I get callback for about 180 seconds which means my ranging will work for 3 minutes but it only send notification in its first 10 seconds.
I started a task in the background and then call my run function which will wake up init function. I'll be happy if someone shares its experience.
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
BackgroundTask1.run(application:
application) { (BackgroundTask1_) in
}
run and init functions:
class func run(application: UIApplication, handler: (BackgroundTask1) -> ()) {
let backgroundTask = BackgroundTask1(application: application)
backgroundTask.begin()
handler(backgroundTask)
}
func init_()
{
let uuidString = "43F2ACD1-5522-4E0D-9E3F-4A828EA12C24"
let beaconRegionIdentifier = "Hello"
let beaconUUID:UUID = UUID(uuidString:uuidString)!
beaconRegion = CLBeaconRegion(proximityUUID: beaconUUID, identifier: beaconRegionIdentifier)
beaconRegion.notifyEntryStateOnDisplay = true
locationManager_ = CLLocationManager()
if (CLLocationManager.authorizationStatus() != CLAuthorizationStatus.authorizedWhenInUse) {
locationManager_!.requestWhenInUseAuthorization()
}
locationManager_?.allowsBackgroundLocationUpdates = true
locationManager_!.delegate = self
locationManager_!.pausesLocationUpdatesAutomatically=false
locationManager_?.startRangingBeacons(in: beaconRegion)
}
This is notificaion's code:
func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
print("sdljflkj")
if beacons.count == 0 {
return
}
let currentbeacon = beacons[0]
lastproximity = currentbeacon.proximity
if currentbeacon.proximity == CLProximity.immediate{
DispatchQueue.global(qos: .userInitiated).async {
let content = UNMutableNotificationContent()
content.title = "Forget Me Not"
content.body = "Are you forgetting something?"
content.sound = .default()
let request = UNNotificationRequest(identifier: "ForgetMeNot", content: content, trigger: nil)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
print("here1")
NotificationCenter.default.post(name: Notification.Name(rawValue: "iBeaconFoundReceivedNotification"), object: nil, userInfo: ["Major":currentbeacon.major, "minor": currentbeacon.minor])
}
}
}

How to trigger a WK Notification

What code do I write to trigger a watch kit notification from the watch app itself? For example, if I connect a button from my watch storyboard to my WatchInterfaceController as an action then when pressed it triggers a notification on the watch.
For trigger a notification, first of all you need permission: (declared usually in the ExtensionDelegate)
func askPermission() {
UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .alert,.sound]) { (authBool, error) in
if authBool {
let okAction = UNNotificationAction(identifier: "ok", title: "Ok", options: [])
let category = UNNotificationCategory(identifier: "exampleCategoryIdentifier", actions: [okAction], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([category])
UNUserNotificationCenter.current().delegate = self
}
}
}
For having this working, you need to import (in the ExtensionDelegate) "UserNotifications" and to extend:
UNUserNotificationCenterDelegate
Once you have done that, you can call askPermission where you want, in a way like this:
if let delegate = WKExtension.shared().delegate as? ExtensionDelegate {
delegate.askPermission()
}
Now you have (hopefully) the permission for trigger a notification!
For trigger a notification, you can use a function like this:
func notification() {
let content = UNMutableNotificationContent()
content.body = "Body Of The Notification"
content.categoryIdentifier = "exampleCategoryIdentifier" // Re-Use the same identifier of the previous category.
content.sound = UNNotificationSound.default() // This is optional
let request = UNNotificationRequest(identifier: NSUUID().uuidString,
content: content,
trigger: nil)
let center = UNUserNotificationCenter.current()
center.add(request) { (error) in
if error != nil {
print(error!)
} else {
print("notification: ok")
}
}
}
In order to test watch notifications, you must first create a new build scheme.
Duplicate your watch app scheme, and in the "Run" section, choose your custom notification as the executable.
Now you can run the notification scheme.
Inside the extensions group in your project, under Supporting Files is a file called PushNotificationPayload.json.
You can edit the payload file to try different notifications and categories.