推送和通知

2020-10-24  本文已影响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
    }
上一篇下一篇

猜你喜欢

热点阅读