美文网首页
推送和通知

推送和通知

作者: 纳兰沫 | 来源:发表于2020-10-24 17:52 被阅读0次

    本地通知

    修改模拟器的语言.png
    //发送本地通知
        func sendLocalNotification() {
            //本地通知的设置
            UNUserNotificationCenter.current().getNotificationSettings { (setting) in
                //本地通知的状态
                switch setting.authorizationStatus {
                case .notDetermined:
                    print("未曾向用户请求授权")
                    self.requestAuth()
                case .denied:
                    print("用户不允许通知")
                    DispatchQueue.main.async {
                        let alert = UIAlertController(title: "大额红包强", message: "建议执行以下操作:点击下方设置->通知->允许通知", preferredStyle: .alert)
                        let cancelAction = UIAlertAction(title: "取消", style: .cancel, handler: nil)
                        let settingAction = UIAlertAction(title: "设置", style: .default) { (action) in
                            UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)
                        }
                        alert.addAction(cancelAction)
                        alert.addAction(settingAction)
                        
                    }
                case .authorized:
                    print("用户允许通知 可以给用户发送通知了")
                    self.scheduleLocalNotifications(setting)
                case .provisional:
                    print("临时通知")
                case .ephemeral:
                    //iOS14以上
                    print("临时授权应用程序发布通知。仅适用于应用程序剪辑")
                @unknown default:
                    break
                }
            }
        }
        
        //请求授权
        func requestAuth() {
            //通知授权
            UNUserNotificationCenter.current().requestAuthorization(options: [.alert,.sound,.badge]) { (granted, error) in
                if let error = error {
                    print(error.localizedDescription)
                }
                //判断用户是否授权了
                if granted {
                    print("进行了授权")
                }
            }
        }
        
        //发送通知
        func scheduleLocalNotifications(_ setting: UNNotificationSettings) {
            if setting.alertSetting == .disabled {
                print("用户关闭了横幅通知")
            }
            if setting.notificationCenterSetting == .disabled {
                print("用户关闭了通知中心")
            }
            if setting.lockScreenSetting == .disabled {
                print("用户关闭了锁屏通知")
            }
            let content = UNMutableNotificationContent()
            content.title = "收到了一笔汇款"
            content.body = "xx给你汇款100w元"
            content.badge = 1
            content.sound = .default
            content.sound = UNNotificationSound(named: UNNotificationSoundName("xxx.caf"))
            //添加多媒体附件
            do{
                let image = try UNNotificationAttachment(identifier: "image", url: Bundle.main.url(forResource: "testimage", withExtension: "png")!, options: [UNNotificationAttachmentOptionsThumbnailClippingRectKey:CGRect(x: 0, y: 0, width: 0.25, height: 0.25).dictionaryRepresentation])
                /*
                 [UNNotificationAttachmentOptionsThumbnailClippingRectKey:CGRect(x: 0, y: 0, width: 0.25, height: 0.25).dictionaryRepresentation]  裁剪图片
                 */
                let video = try UNNotificationAttachment(identifier: "video", url: Bundle.main.url(forResource: "testvideo", withExtension: "mp4")!, options: [UNNotificationAttachmentOptionsThumbnailTimeKey:10])
                /*
                 [UNNotificationAttachmentOptionsThumbnailTimeKey:10] 第10秒就作为画面就会作为缩略图画面进行呈现
                 */
                let audio = try UNNotificationAttachment(identifier: "audio", url: Bundle.main.url(forResource: "testaudio", withExtension: "mp3")!)
                content.attachments = [video]//一般只能添加一个附件
                
            }catch{
                print(error.localizedDescription)
            }
            //隔多少秒之后进行发送通知
            let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
            //日历触发器
    //                let dateComponents = DateComponents(calendar: Calendar.current,hour: 20, minute: 55 weekday: 5)
    //                let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
            
            //位置触发器
    //                let regin = CLCircularRegion(
    //                    center: CLLocationCoordinate2D(latitude: 37.335400, longitude: -122.009201),
    //                    radius: 2000,
    //                    identifier: "AppleLocation")
    //                regin.notifyOnExit = false
    //                regin.notifyOnEntry = true
    //                let reginTrigger = UNLocationNotificationTrigger(region: regin, repeats: true)
            UNUserNotificationCenter.current().add(UNNotificationRequest(identifier: "x", content: content, trigger: trigger)) { (error) in
                if let error = error {
                    print(error.localizedDescription)
                }
            }
        }
    

    隐式通知

     UNUserNotificationCenter.current().requestAuthorization(options: [.provisional]) { (granted, error) in
                if let error = error {
                    print(error.localizedDescription)
                }
                //判断用户是否授权了
                if granted {
                    print("进行了授权")
                }
            }
    

    数组内只要加入provisional,那么就不需要用户授权就可以发送通知 通知内容只能显示在通知中心,用户可以修改为显示通知

    通知附加项

    UNUserNotificationCenter.current().requestAuthorization(options: [.providesAppNotificationSettings]) { (granted, error) in
                if let error = error {
                    print(error.localizedDescription)
                }
                //判断用户是否授权了
                if granted {
                    print("进行了授权")
                }
            }
    

    可以在通知界面添加出一个新的界面

    1.需要在AppDelegate里面添加
    UNUserNotificationCenter.current().delegate = self

    2.实现代理方法

    extension AppDelegate: UNUserNotificationCenterDelegate {
        func userNotificationCenter(_ center: UNUserNotificationCenter, openSettingsFor notification: UNNotification?) {
            //相关设置
        }
    }
    

    3.在func sceneDidBecomeActive(_ scene: UIScene) { }
    方法中跳转到自己的设置页面 设置可以关闭不同的通知

    image.png

    紧急通知

    1.在苹果官网进行申请
    2.根据苹果回复的文件进行下一步操作

    UNUserNotificationCenter.current().requestAuthorization(options: [.criticalAlert]) { (granted, error) in
                if let error = error {
                    print(error.localizedDescription)
                }
                //判断用户是否授权了
                if granted {
                    print("进行了授权")
                }
            }
    
    func sendNotifications() {
            UNUserNotificationCenter.current().getNotificationSettings { (setting) in
                switch setting.criticalAlertSetting {
                case .notSupported:
                    print("notSupported")
                case .disabled:
                    print("disabled")
                case .enabled:
                    print("enabled")
                    let content = UNMutableNotificationContent()
                    content.title = "xxx"
                    content.body = "xxxxxx"
                    content.sound = .defaultCritical
                    content.sound = .defaultCriticalSound(withAudioVolume: 1)
                    content.sound = .criticalSoundNamed(UNNotificationSoundName("xx"))
                    content.sound = .criticalSoundNamed(UNNotificationSoundName("xx"), withAudioVolume: 1)
                @unknown default:
                    break
                }
            }
    

    可交互式通知

    1.需要在AppDelete里面先设置好按钮

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    //请求授权
            requestAuth()
           setNotificationCategory()
            UNUserNotificationCenter.current().delegate = self
    }
    

    2.必须在AppDelegate里面提前设置好category 可在程序中其他地方调用
    identifier必须设置为整个app唯一 以免出现异常

    func setNotificationCategory() {
            //identifier 必须整个app唯一
            let acceptAction = UNNotificationAction(identifier: "ACEPT_ACTION", title: "同意", options: [.foreground])
            //.foreground 打开app
            
            let declineAction = UNNotificationAction(identifier: "DECLINE_ACTION", title: "拒绝", options: [.destructive,.authenticationRequired])
            //.destructive 拒绝按钮
            //.authenticationRequired  必须解锁手机
            
            //临时回复按钮
            let replyAction = UNTextInputNotificationAction(identifier: "REPLY_ACTION", title: "回复")
    //        let replyAction = UNTextInputNotificationAction(
    //            identifier: "REPLY_ACTION",
    //            title: "回复",
    //            options: [],
    //            textInputButtonTitle: "发送不", //发送按钮文字
    //            textInputPlaceholder: "你是谁")
            
            let categoty = UNNotificationCategory(identifier: "FRIEND_REQUEST", actions: [acceptAction,declineAction,replyAction], intentIdentifiers: [],options: [.customDismissAction])
            UNUserNotificationCenter.current().setNotificationCategories([categoty])
        }
    

    3.请求授权

    func requestAuth() {
            UNUserNotificationCenter.current().requestAuthorization(options: [.alert,.sound,.badge]) { (granted, error) in
                if let error = error {
                    print(error.localizedDescription)
                    return
                }
                if granted {
                    print("用户允许")
                    let content = UNMutableNotificationContent()
                    content.title = "好友申请"
                    content.body = "申请加为好友"
                    content.sound = .default
                    content.categoryIdentifier = "FRIEND_REQUEST"
                    content.userInfo = ["FRIEND_USER_ID": 99] //附带信息
                    
                    let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 4, repeats: false)
                    UNUserNotificationCenter.current().add(UNNotificationRequest(identifier: "x", content: content, trigger: trigger), withCompletionHandler: nil)
                }
            }
        }
    

    4.调用代理方法

    //用户在前台
        func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
            print("当app处于前台时,用户点击了通知,触发操作")
            let content = notification.request.content
            let userInfo = content.userInfo
            if content.categoryIdentifier == "FRIEND_REQUEST" {
                let requestUserID = userInfo["FRIEND_USER_ID"] as! Int
                print("FRIEND_USER_ID 为 \(requestUserID)的人向当前用户发送了好友请求 我们可以更新一些UI")
                completionHandler([.sound])
                return
            }else{
                //其他类型的通知
            }
            completionHandler([]) //展示横幅和声音
        }
        
        func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
            print("当app处于后台或关闭状态(无论前台或者后台)时,用户点击了通知,我们要做的操作")
            let content = response.notification.request.content
            let userInfo = content.userInfo
            if content.categoryIdentifier == "FRIEND_REQUEST" {
                let requestUserID = userInfo["FRIEND_USER_ID"] as! Int
                switch response.actionIdentifier {
                case "ACEPT_ACTION":
                    print("当前用户接受了FRIEND_USER_ID 为 \(requestUserID)的好友申请")
                    break
                case "DECLINE_ACTION":
                    print("当前用户拒绝了FRIEND_USER_ID 为 \(requestUserID)的好友申请")
                    break
                case "REPLY_ACTION":
                    let replyText = (response as! UNTextInputNotificationResponse).userText
                    print("当前用户向FRIEND_USER_ID 为 \(requestUserID)的人发送了'\(replyText)'的回复")
                    break
                case UNNotificationDefaultActionIdentifier:
                    print("用户单纯点击通知进入了我们的app")
                    break
                case UNNotificationDismissActionIdentifier: //必须设置UNNotificationCategory的options里面包含customDismissAction 才会起作用
                    print("用户点击了通知的清除按钮")
                    break
                default:
                    break
                }
            }
        }
    

    UNUserNotificationCenter.current().add(UNNotificationRequest(identifier: "x", content: content, trigger: trigger), withCompletionHandler: nil) 当identifier是一样的话 连续发通知 后面的通知会覆盖掉前面的

    分组通知

    如果没有设置分组 就是自动分组 通知会全部重叠在一起

    设置通知的content.threadIdentifier就可以实现通知的不同分组

    let content = UNMutableNotificationContent()
                    content.title = "好友申请"
                    content.body = "申请加为好友"
                    content.sound = .default
                    content.categoryIdentifier = "FRIEND_REQUEST"
                    content.userInfo = ["FRIEND_USER_ID": 99] //附带信息
                    content.threadIdentifier = "FRIEND"
                    
                    let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 4, repeats: false)
                    UNUserNotificationCenter.current().add(UNNotificationRequest(identifier: "x", content: content, trigger: trigger), withCompletionHandler: nil)
    

    当用户在设置里面设置了按app分通知 我们做的操作就无效了

    通知概要

    1.设置category

            let categotyFR = UNNotificationCategory(
                identifier: "MOMENTS_UPDATE",
                actions: [],
                intentIdentifiers: [],
                hiddenPreviewsBodyPlaceholder: nil,
                categorySummaryFormat: "%u %@", //概要显示
                options: [.customDismissAction])
            
            UNUserNotificationCenter.current().setNotificationCategories([categoty,categotyFR])
    

    2.发送通知

    func sendMomentsUpdateNotifications() {
            let content = UNMutableNotificationContent()
            content.title = "朋友圈更新"
            content.body = "XXX上传了9张图片"
            content.sound = .default
            content.categoryIdentifier = "MOMENTS_UPDATE"
            content.userInfo = ["MOMENTS_UPDATE_USER_ID": 100]
            content.threadIdentifier = "MOMENTS_UPDATE"
            content.summaryArgument = "999" //内容
            content.summaryArgumentCount = 10 //消息的权重
            
            let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 4, repeats: false)
            UNUserNotificationCenter.current().add(UNNotificationRequest(identifier: "MOMENTS_UPDATE", content: content, trigger: trigger), withCompletionHandler: nil)
        }
    

    用户关闭通知预览时 显示的内容

    当用户关闭预览时 点击通知会直接进入app内部 不会把折叠的通知展开显示

    设置category的hiddenPreviewsBodyPlaceholder方可修改显示内容

     let categotyFR = UNNotificationCategory(
                identifier: "MOMENTS_UPDATE",
                actions: [],
                intentIdentifiers: [],
                hiddenPreviewsBodyPlaceholder: "%u条新消息",//所有通知的数量
                categorySummaryFormat: "%u %@", //被折叠的通知的数量
                options: [.customDismissAction])
    

    获取已发送通知和未发送通知

    当app在前台 未设置时 若通知中心没有通知 是获取不到已发送通知和无法删除已发送通知的

    func getPendingNotification() {
            UNUserNotificationCenter.current().getPendingNotificationRequests { (requests) in
                print("获取到所有未发送的通知 \(requests)")
            }
        }
        
        func getDdeliveredNotification() {
            UNUserNotificationCenter.current().getDeliveredNotifications { (notifications) in
                print("获取到所有已发送的通知 \(notifications)")
            }
        }
    

    所有Pending都是指非重复通知未被trigger+重复通知未被移除
    Ddelivered是指在通知只能更新正在显示的通知

    通知的更新

    无论通知是否已经到达 都可以直接更新 只要identifier 相同就可以覆盖更新掉

    let content = UNMutableNotificationContent()
            content.title = "朋友圈更新"
            content.body = "XXX上传了9张图片"
            content.sound = .default
            content.categoryIdentifier = "MOMENTS_UPDATE"
            content.userInfo = ["MOMENTS_UPDATE_USER_ID": 100]
    
            let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 4, repeats: false)
            UNUserNotificationCenter.current().add(UNNotificationRequest(identifier: "MOMENTS_UPDATE", content: content, trigger: trigger), withCompletionHandler: nil)
    

    通知的删除

    func removePendingNotification() {
            UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ["MOMENTS_UPDATE"])
            UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
            
        }
        
        func removeDdeliveredNotification() {
            UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ["MOMENTS_UPDATE"])
            UNUserNotificationCenter.current().removeAllDeliveredNotifications()
        }
    

    自定义通知

    1.新建target

    image.png

    2.自定义通知的界面

    image.png
    设置图片的时候 如果显示的过大 可以在viewDidLoad()里面设置尺寸
    override func viewDidLoad() {
            super.viewDidLoad()
            preferredContentSize = CGSize(width: 320, height: 320)
        }
    

    如果需要设置交互操作 必须在iOS12之后才有效
    如果想要交互操作有效果 需要在plist文件添加属性UNNotificationExtensionUserInteractionEnabled

    image.png

    3.发送的通知的identifier category的identifier 和plist文件里面的UNNotificationExtensionCategory的value必须一致 才会显示自定义通知界面

    image.png

    4.由于自定义通知 下方会带上通知的内容 如果想通知的内容去掉 需要在plist里面设置UNNotificationExtensionDefaultContentHidden

    image.png

    5.修改自定义通知上的app名称 需要修改plist里面的UNNotificationExtensionOverridesDefalutTitle为true

    image.png

    修改stroryboard里面的ViewController的title就是修改自定义通知上显示的app名称

    image.png

    6.如果需要修改自定义通知上控件的赋值

    image.png

    在以上方法里面进行赋值才不会出现异常

    7.播放视频

    设置播放按钮

    var mediaPlayPauseButtonFrame: CGRect {
            CGRect(x: view.center.x - 25, y: view.center.y - 25, width: 50, height: 50)
        }
        
        var mediaPlayPauseButtonTintColor: UIColor {
            .orange
        }
        
        var mediaPlayPauseButtonType: UNNotificationContentExtensionMediaPlayPauseButtonType {
            .overlay //点击之后消失 再次点击就会出现
        }
    

    播放 暂停视频

    func mediaPlay() {
            
        }
        
        func mediaPause() {
            
        }
    

    8.如果通知带有Action按钮
    那么就会先触发
    func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) {}
    根据这个方法里面的操作
    再调用delegate里面的代理方法

    func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) {
            switch response.actionIdentifier {
            case "playAction":
                //播放视频
               extensionContext?.mediaPlayingStarted()//联动视频上的按钮
                completion(.doNotDismiss)//通知不消失
            case "inAppAction":
                completion(.dismissAndForwardAction)//进入app
            case "dismissAction":
                completion(.dismiss)//通知消失
            default:
                break
            }
        }
    
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
          completionHandler([.alert])
        }
    
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
          if response.actionIdentifier == "inAppAction" {
                print("从通知进入了app")
            }
    }
    

    远程通知

    1.添加push

    image.png
    //注册通知
            UIApplication.shared.registerForRemoteNotifications()
    
    //通知注册成功后的回调
        //deviceToken可以指定到特定的手机特定的app
        func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
            print(deviceToken.map{ String(format: "%02.2hhx",$0)}.joined())
        }
        
        //通知注册失败后的回调
        func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
            
        }
    

    2.远程通知和本地通知

     func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void)
    

    里面的
    let content = response.notification.request.content
    let userInfo = content.userInfo
    对于本地通知而言 userInfo是通知的userInfo的内容
    对于远程通知而言 userInfo是通知的所有内容

    静默推送

    1.添加backgroundMode

    image.png
    image.png

    //静默推送的配置:
    //1.Xcode中添加Background Modes的Capability,并打开Remote notifications
    //2.payload的aps的值中不能有alert,sound,badge;必须有content-available,值设为1
    //3.服务端向APNs发送的推送请求头中,设apns-push-type为background,设apns-topic为App的bundle ID,设apns-priority为5(低优先级)

    //苹果只给30秒的时间处理请求
        func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
            print(userInfo)
            completionHandler(.noData)
        }
    

    在到达用户之前进行修改alert

    1.添加push

    image

    .添加push

    2.添加target

    image.png

    3.发送的通知必须有alert 和 mutable-content 才能进行拦截修改

    {
        "aps":{
            "alert":{
                "title":"好友申请",
                "body":"马云申请加为好友"
            },
            "mutable-content":1
        },
        "requestUserID":88
    }
    
    

    4.把未达到的通知的图片改为网络图片

    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
            
            self.contentHandler = contentHandler
            bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
            
            if let bestAttemptContent = bestAttemptContent {
                // Modify the notification content here...
                
                if let imageID = bestAttemptContent.userInfo["imageID"] as? String,
                    let imageURLString = bestAttemptContent.userInfo["imageURLString"] as? String,
                    let imageRemoteURL = URL(string: imageURLString),
                    let att = saveToDisk(imageRemoteURL: imageRemoteURL, imageID: imageID)
                {
                    bestAttemptContent.attachments = [att]
                    
                }else if let att = placeholderAtt(){
                    
                    bestAttemptContent.attachments = [att]
                }
                
                contentHandler(bestAttemptContent)
            }
        }
        
        override func serviceExtensionTimeWillExpire() {
            // Called just before the extension will be terminated by the system.
            // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
            if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
                
                if let att = placeholderAtt(){
                    bestAttemptContent.attachments = [att]
                }
                
                contentHandler(bestAttemptContent)
            }
        }
        
        func saveToDisk(imageRemoteURL: URL, imageID: String)->UNNotificationAttachment?{
            //定义文件夹和图片在本地的URL(2-1和2-3)
            
            //2-1专门在Temp文件夹里面存图片的文件夹URL--temp/xxx
            let imageTempDirURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(ProcessInfo.processInfo.globallyUniqueString)
            
            //2-3图片在本地的URL--temp/xxx/yyy.jpg
            let imageLocalURL = imageTempDirURL.appendingPathComponent("\(UUID().uuidString).jpg")
            
            
            do {
                
                //2-2创建文件夹
                try FileManager.default.createDirectory(at: imageTempDirURL, withIntermediateDirectories: true)
                
                //1-1获取图片data
                let data = try Data(contentsOf: imageRemoteURL)
                
                //3-1把图片用定义好的URL存到本地,待会就可以用这个URL取到图片了
                try data.write(to: imageLocalURL)
                
                let att = try UNNotificationAttachment(identifier: imageID, url: imageLocalURL)
                
                return att
                
            } catch {
                print(error.localizedDescription)
            }
            
            return nil
        }
        
        //备选图片(下载图片失败或超时时使用)
        func placeholderAtt()->UNNotificationAttachment?{
            if let url = Bundle.main.url(forResource: "placeholder", withExtension: "jpg"),
                let att = try? UNNotificationAttachment(identifier: "placeholder", url: url){
                
                return att
                
            }
            
            return nil
        }
    

    相关文章

      网友评论

          本文标题:推送和通知

          本文链接:https://www.haomeiwen.com/subject/wyvpmktx.html