import Foundation #if os(iOS) import UIKit #else import AppKit #endif /// Registering this device with Apple, and then with Calls, so that a /// knock can reach it when the app is closed. /// /// The local half — KnockWatch — only reaches an app that is running. /// This is the other half, and it is the only one that can help the /// person whose Mac is asleep or whose phone is in a pocket with Calls /// not open. /// /// Everything here is best effort and nothing is ever shown to anyone. A /// device that could not register is a device that will not be told /// about a knock, which is exactly where every device was before this /// existed. It is never a reason to fail a join, and never a sheet. @MainActor public enum RemoteKnocks { /// The platform name the server stores alongside the token. The two /// apps have different bundle identifiers and therefore different APNs /// topics, so a token is useless without it. public static var platform: String { #if os(iOS) return "ios" #else return "macos" #endif } /// Asks Apple for a device token. The answer arrives at the app /// delegate, asynchronously and possibly never — without the push /// entitlement, or on a build Apple will not issue one for, the /// failure callback is what comes back instead. public static func register() { #if os(iOS) UIApplication.shared.registerForRemoteNotifications() #else NSApplication.shared.registerForRemoteNotifications() #endif } /// Apple's token is opaque bytes; the server stores it as lower-case /// hex, which is the form every APNs provider uses and the form the /// registration endpoint validates. public static func hex(_ token: Data) -> String { token.map { String(format: "%02x", $0) }.joined() } }