VisionOS send notification

93 Views Asked by At

How does one send notification in vision os? I seem to not be able to find any information about notifications in vision os.

I have a window which after some time it should alert the user to look at it and I thought notification is a good idea (or is there some other way I could do that?) but I can't get it to work.

I have the permission requested and granted. Am I doing something wrong? Is vision os not supporting notifications? I can't find anything related to this. Did anyone manage to send a notification and if yes, how?

I tried the following code and it does not do anything:

let content = UNMutableNotificationContent()
content.title = "Feed the cat"
content.body = "It looks hungry"
content.sound = UNNotificationSound.default

let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)

let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.add(request)
2

There are 2 best solutions below

1
lorem ipsum On BEST ANSWER

You are missing the delegate and telling the UNUserNotificationCenter how to present the notification in the foreground.

extension NotificationManager:  UNUserNotificationCenterDelegate {
    
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {
        .banner
    }
}

The rest of the manager would look something like.

@Observable final class NotificationManager: NSObject {
    private(set) var isPermissionGranted = false
    let userNC = UNUserNotificationCenter.current()
    
    override init() {
        super.init()
        userNC.delegate = self
    }
    
    func sendNotifications() async throws {
        let id = UUID().uuidString
        
        let content = UNMutableNotificationContent()
        content.title = "Let's feed the cat"
        content.subtitle = "It looks hungry"
        
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: true)
        let request = UNNotificationRequest(identifier: id,content: content,trigger: trigger)
        try await userNC.add(request)
        print("\(#function) :: scheduled")
    }
    
    func requestAuthorization () async throws {
        do {
            isPermissionGranted = try await userNC.requestAuthorization(options: [.alert, .badge, .sound])
        } catch {
            print(error)
        }
    }
    
    func cancelAll() {
        userNC.removeAllPendingNotificationRequests()
    }
}

Note that you should create the manager at the very top of your app with

@State private var manager: NotificationManager = .init()

and inject it into the app with

.environment(manager)

So you can access it everywhere with

@Environment(NotificationManager.self) private var manager

It is important that the delegate is stable so the app doesn't miss notifications.

Here is a sample View

import SwiftUI
import RealityKit
import UserNotifications

struct NotificationSample: View {
    @Environment(NotificationManager.self) private var manager
    @State private var date: Date?
    @State private var isScheduling: Bool = false
    var body: some View {
        
        RealityView { content, attchments in
            guard let attachment = attchments.entity(for: "Feed") else {return}
            content.add(attachment)
        } update: { content, attchments in
            
        } attachments: {
            Attachment(id: "Feed") {
                VStack {
                    if let date {
                        Text(date, style: .timer)
                    }
                    Button("FEED THE CAT") {
                        isScheduling.toggle()
                    }.task(id: isScheduling) {
                        guard isScheduling else {return}
                        do {
                            try await manager.sendNotifications()
                        } catch {
                            print(error)
                        }
                        isScheduling = false
                        date = Calendar.current.date(byAdding: .minute, value: 1, to: Date())
                    }.disabled(!manager.isPermissionGranted)
                }
            }
        }
        .task {
            do {
                try await manager.requestAuthorization()
            } catch {
                print(error)
            }
            manager.cancelAll()
        }
        
    }
}
3
Andy Jazz On

Notifications in visionOS work fine. Use this code

import SwiftUI
import RealityKit
import UserNotifications

struct ContentView: View {
    @State var isPermissionGranted = false
    let sphere = ModelEntity(mesh: .generateSphere(radius: 0.05))
    let options: UNAuthorizationOptions = [.alert, .badge, .sound]
    let material = UnlitMaterial(color: .green)
    let userNC = UNUserNotificationCenter.current()

    func sendNotifications() {
        let id = UUID().uuidString

        let content = UNMutableNotificationContent()
        content.title = "Let's feed the cat"
        content.subtitle = "It looks hungry"

        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, 
                                                             repeats: true)
        let request = UNNotificationRequest(identifier: id, 
                                               content: content,
                                               trigger: trigger)
        userNC.add(request)
    }
    var body: some View {
        RealityView { content in content.add(sphere) }
        VStack {
            if isPermissionGranted {
                Button("FEED THE CAT") {
                    self.sendNotifications()
                    sphere.model?.materials = [material]
                    Task {
                        try await Task.sleep(nanoseconds: 5_000_000_000)
                        sphere.model?.materials = []
                    }
                }
            }
        }
        .onAppear {
            userNC.requestAuthorization(options: options) { (success, _) in
                if success {
                    isPermissionGranted = true
                }
            }
        }
    }
}

enter image description here