Step 1: have a paid Apple developer account
Step 2: enable the Capability in Signing & Capabilities
.
Background Mode: Remote notifications
Push Notifications
Step 3: create the Certificate and Certificate Signing Request (CSR)
Step 4: tested on real machine
Step 5: register to APNS
- Your app asks to be registered with APNs.
- On successful registration, APNs sends an app-specific device token to the device.
- The system delivers the device to your app by calling a method in your app delegate.
- Your app sends the device token to the app’s associated provider.
Device token
An app-specific device token is globally unique and identifies one app-device combination.
Device token may change
Never cache device tokens in your app; instead, get them from the system when you need them. APNs issues a new device token to your app when certain events happen. The device token is guaranteed to be different, for example, when a user restores a device from a backup, when the user installs your app on a new device, and when the user reinstalls the operating system. Fetching the token, rather than relying on a cache, ensures that you have the current device token needed for your provider to communicate with APNs. When you attempt to fetch a device token but it has not changed, the fetch method returns quickly.
When a device token has changed, the user must launch your app once before APNs can once again deliver remote notifications to the device.
Obtaining a device token
you initiate APNs registration for your app by calling the registerForRemoteNotifications
method of the UIApplication
object. Call this method at launch time as part of your normal startup sequence. The first time your app calls this method, the app object contacts APNs and requests the app-specific device token on your behalf.
The system then asynchronously calls one of two following app delegate methods, depending on success or failure:
- On successful issuance of an app-specific device token, the system calls the
application:didRegisterForRemoteNotificationsWithDeviceToken:
method. Implement this method to receive the token and forward it to your provider. - On error, the system calls the
application:didFailToRegisterForRemoteNotificationsWithError:
method. Implement this method to respond to APNs registration errors.
After successful APNs registration, the app object contacts APNs only when the device token has changed; otherwise, calling the registerForRemoteNotifications
method results in a call to the application:didRegisterForRemoteNotificationsWithDeviceToken:
method which returns the existing token quickly.
Working code:
class APNSAuthManager: ObservableObject {
let center = UNUserNotificationCenter.current()
func requestAuth() {
center.requestAuthorization(options: [.alert, .sound, .badge]) { [self] granted, error in
if error != nil {
// Handle the error here.
}
print("[shark-APNS] user allowed")
self.registerAPNS()
}
}
func getSetting() {
center.getNotificationSettings { settings in
guard (settings.authorizationStatus == .authorized) ||
(settings.authorizationStatus == .provisional) else { return }
if settings.alertSetting == .enabled {
// Schedule an alert-only notification.
print("alertSetting")
} else {
// Schedule a notification with a badge and sound.
print("badge and sound")
}
}
}
func registerAPNS() {
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
func sendLocalNote() {
// payload
let content = UNMutableNotificationContent()
content.title = NSString.localizedUserNotificationString(forKey: "Local Note Title!", arguments: nil)
content.subtitle = "subtitle"
content.body = NSString.localizedUserNotificationString(forKey: "Local Note Body", arguments: nil)
content.sound = .default
content.badge = 1
// trigger
let date = Date().addingTimeInterval(5)
let dateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: date)
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger) // Schedule the notification.
center.add(request) { (error : Error?) in
if error != nil {
// Handle any errors
}
print("local note sent!")
}
}
func updateDeviceToken(deviceToken: String) {
// add
guard let dt: String = UserDefaults.standard.string(forKey: "deviceToken") else {
UserDefaults.standard.setValue(deviceToken, forKey: "deviceToken")
sendDeviceToken()
return
}
// update
if dt != deviceToken {
UserDefaults.standard.setValue(deviceToken, forKey: "deviceToken")
sendDeviceToken()
}
}
func sendDeviceToken() {
// get device indentification
let data = DeviceDTO()
// arrange the data
do {
// send to server
let url = URL(string: "")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let jsonData = try JSONEncoder().encode(data)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString)
request.httpBody = jsonData
// let postString = "a=test&b=bla"
// request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
// check error
if let error = error {
print ("error: \(error)")
return
}
// check response
guard let response = response as? HTTPURLResponse,
(200...299).contains(response.statusCode) else {
print ("server error")
return
}
// print data from server
guard let data = data else {
print("no data")
return
}
let dataString = String(data: data, encoding: .utf8)
print ("got data: \(String(describing: dataString))")
}
task.resume()
}
catch {
print("[Shark] cannot encode the device data")
}
}
}
struct DeviceDTO: Codable {
var iosIdentifierForVendor: String = UIDevice.current.identifierForVendor?.uuidString ?? ""
var iosName: String = UIDevice.current.name
var iosSystemName: String = UIDevice.current.systemName
var iosSystemVersion: String = UIDevice.current.systemVersion
var iosModel: String = UIDevice.current.model
var iosUserInterfaceIdiom: String = UIDevice.current.userInterfaceIdiom.description
var iosBundleIdentifier: String = Bundle.main.bundleIdentifier ?? ""
var iosDeviceToken: String = UserDefaults.standard.string(forKey: "deviceToken")!
}
extension Data {
// for device token
var hexString: String {
let hexString = map { String(format: "%02.2hhx", $0) }.joined()
return hexString
}
}
extension UIUserInterfaceIdiom: CustomStringConvertible{
public var description: String {
switch self {
case .carPlay:
return "carPlay"
case .mac:
return "mac"
case .pad:
return "pad"
case .phone:
return "phone"
case .tv:
return "tv"
case .unspecified:
return "unspecified"
@unknown default:
fatalError()
}
}
}
main.swift
struct KidsLearnHindiApp: App {
@UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
@ObservedObject var manager = APNSAuthManager()
init() {
// apn
manager.requestAuth()
manager.getSetting()
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
var manager = APNSAuthManager()
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("[shark-APNS] register successfully")
print(deviceToken.hexString)
manager.updateDeviceToken(deviceToken: deviceToken.hexString)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("[shark-APNS] register failed: ", error)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
print("[shark-APNS] push note received")
}
}