Home > AI > IOS > UserNotifications >

push notification

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

  1. Your app asks to be registered with APNs.
  2. On successful registration, APNs sends an app-specific device token to the device.
  3. The system delivers the device to your app by calling a method in your app delegate.
  4. 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")
        
    }
}

Leave a Reply