推送通知的新功能


推送通知越来越受欢迎。像苹果和谷歌这样的供应商正在他们的新版本中增加新的推送通知功能。

今天,几乎所有的移动应用程序都以某种方式使用推送通知,所以我们将不讨论推送通知系统的定义或基本操作。

在本文中,我们将研究iOS 10和安卓系统上的新推送通知功能

苹果在iOS 10中引入了对在通知中显示图像、GIF、视频、音频和自定义内容的支持,而安卓N有新的通知样式,如消息传递样式,以及新的通知分组方法,如捆绑通知。安卓通知最重要的创新是直接回复。

在iOS 10之前,无法在接入点和iOS应用程序之间拦截消息。现在,有两个扩展可用。这些扩展是通知服务扩展和通知内容扩展。

通知服务扩展

通知服务扩展是接入点和iOS应用程序之间的一层,在iOS显示通知之前运行。它提供定制通知内容,如加密-解密内容或下载文件,如图像、音频和视频。

图像通知示例:

GIF通知示例:

视频通知示例:

如何向您的项目添加通知服务扩展

当我们创建服务扩展时,三个新文件被添加到项目文件夹中。

通知服务扩展类有两种方法,第一种是直接接收()方法,第二种是服务扩展时间到期()方法。

//
//  UNNotificationServiceExtension.h
//  UserNotifications
//
//  Copyright © 2016 Apple Inc. All rights reserved.
//

@available(iOS 10.0, *)
open class UNNotificationServiceExtension : NSObject {


    // Call contentHandler with the modified notification content to deliver. If the handler is not called before the service's time expires then the unmodified notification will be delivered.
    // You are expected to override this method to implement push notification modification.
    open func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Swift.Void)


    // Will be called just before this extension is terminated by the system. You may choose whether to override this method.
    open func serviceExtensionTimeWillExpire()
}

在didReceive()方法中,我们可以下载文件并将这些文件添加到通知中。如果这个下载过程需要一定的时间,那么ServiceExtension TiME将作为一种处理超时的方法来运行。

通知有效负载必须具有可变内容:1运行服务扩展的键值字段。

我们的通知服务实现示例:

class NotificationService: UNNotificationServiceExtension {
    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?

    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

        if let bestAttemptContent = bestAttemptContent {

            if let attachmentUrl = bestAttemptContent.userInfo["attachment-url"] as? String {
                var url = URL(string: attachmentUrl)

                downloadFile(url: url!) { localURL in
                    if let localURL = localURL {
                        do {
                            let attachment = try UNNotificationAttachment(identifier: "", url: localURL, options: nil)
                            bestAttemptContent.attachments = [attachment]
                        } catch {
                            print(error)
                        }
                    }
                    contentHandler(bestAttemptContent)
                }
            }
        }
    }

    override func serviceExtensionTimeWillExpire() {

        if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
            contentHandler(bestAttemptContent)
        }
    }

    private func downloadFile(url: URL, handler: @escaping (_ localURL: URL?) -> Void){
        URLSession.shared.downloadTask(with: url, completionHandler: { (location, response, error) in
            guard
                let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
                let location = location, error == nil
                else { return }
            do {
                let documentsUrl = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
                let destination = documentsUrl.appendingPathComponent(url.lastPathComponent)
                try FileManager.default.moveItem(at: location, to: destination)
                handler(destination)
            } catch let error as NSError {
                print(error.localizedDescription)
            }
        }).resume()
    }

}

通知内容扩展

可以使用自定义内容创建通知。通知内容扩展提供了一种处理通知内容和创建新的自定义用户界面的方法。内容扩展的一个缺点是没有与视图的交互。对于交互,必须使用通知操作。

如何向项目添加通知内容扩展

当我们创建内容扩展时,三个新文件被添加到项目文件夹中。

NotificationViewController实现了UIViewController和UNNotificationContentExtension。

未通知内容扩展协议:

@available(iOS 10.0, *)
public protocol UNNotificationContentExtension : NSObjectProtocol {


    // This will be called to send the notification to be displayed by
    // the extension. If the extension is being displayed and more related
    // notifications arrive (eg. more messages for the same conversation)
    // the same method will be called for each new notification.
    public func didReceive(_ notification: UNNotification)


    // If implemented, the method will be called when the user taps on one
    // of the notification actions. The completion handler can be called
    // after handling the action to dismiss the notification and forward the
    // action to the app if necessary.
    optional public func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Swift.Void)


    // Implementing this method and returning a button type other that "None" will
    // make the notification attempt to draw a play/pause button correctly styled
    // for that type.
    optional public var mediaPlayPauseButtonType: UNNotificationContentExtensionMediaPlayPauseButtonType { get }


    // Implementing this method and returning a non-empty frame will make
    // the notification draw a button that allows the user to play and pause
    // media content embedded in the notification.
    optional public var mediaPlayPauseButtonFrame: CGRect { get }


    // The tint color to use for the button.
    @NSCopying optional public var mediaPlayPauseButtonTintColor: UIColor { get }


    // Called when the user taps the play or pause button.
    optional public func mediaPlay()

    optional public func mediaPause()
}

通知视图控制器必须实现didReceive()方法。在didReceive方法中,您可以访问通知内容,并根据该内容创建自己的自定义用户界面。

信息文件的未通知扩展类别字段是运行通知内容扩展的关键。要运行内容扩展,通知有效负载的类别字段和未通知扩展类别字段必须相同。

通知操作

通知操作是展开的通知视图下方的操作按钮。我们通过这些操作按钮与通知交互。

未通知操作类必须用于创建操作按钮:

let goClosestATMAction = UNNotificationAction(identifier: ActionType.goClosestATM.rawValue, title: "Go Closest ATM", options: UNNotificationActionOptions.foreground)

未通知操作对象必须设置为未通知类别的操作字段:

let mapCategory = UNNotificationCategory(identifier: "myNotificationCategory", actions: [goClosestATMAction], intentIdentifiers: [], options: [])

未通知类别对象必须设置为未用户通知中心的类别:

UNUserNotificationCenter.current().setNotificationCategories(Set([mapCategory]))

您可以在类的用户通知中心方法上处理操作按钮单击事件,该方法实现了UnUserNotificationCenterDelegate协议。

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

        if response.notification.request.content.categoryIdentifier == "myNotificationCategory" {

            if response.actionIdentifier == ActionType.goClosestATM.rawValue {

            } 
        }

        completionHandler()
    }

如果您忘记在方法签名中写入转义关键字,您的方法将不会被调用。

备注

必须启用项目的推送通知功能。

推送通知、取消用户通知中心委托、通知操作和通知类别分配的注册可以在应用程序委托的应用程序中完成。

   func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {

        registerPushNotifications()

        UNUserNotificationCenter.current().delegate = notificationHandler

        let goClosestATMAction = UNNotificationAction(identifier: ActionType.goClosestATM.rawValue, title: "Go Closest ATM", options: UNNotificationActionOptions.foreground)

        let goSecondATMAction = UNNotificationAction(identifier: ActionType.goSecondATM.rawValue, title: "Go Second ATM", options: UNNotificationActionOptions.foreground)

        let goThirdATMAction = UNNotificationAction(identifier: ActionType.goThirdATM.rawValue, title: "Go Third ATM", options: UNNotificationActionOptions.foreground)

        let mapCategory = UNNotificationCategory(identifier: "myNotificationCategory", actions: [goClosestATMAction, goSecondATMAction, goThirdATMAction], intentIdentifiers: [], options: [])

        UNUserNotificationCenter.current().setNotificationCategories(Set([mapCategory]))

        return true
    }


    func registerPushNotifications() {

        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge])
        { (granted, error) in
            let settings = UIUserNotificationSettings(types: [.badge, .sound, .alert], categories: nil)
            UIApplication.shared.registerUserNotificationSettings(settings)
        }
    }

    func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {
        if notificationSettings.types != UIUserNotificationType() {
            application.registerForRemoteNotifications()
        }
    }

安卓通知

安卓系统带来了许多推送通知功能,如直接回复、消息传递风格和捆绑通知。此外,通知的标准视图也发生了变化。

直接回复

直接回复允许通知接收内嵌文本。在安卓版本之前,我们为此使用了通知动作按钮。

直接回复文本输入在通知的底部。

因此,您不必打开应用程序来发送回复消息。

如何向通知添加直接回复

首先,我们必须创建一个远程输入对象。然后,该对象必须设置为通知。操作的addRemoteInput方法。最后,此通知。操作对象必须设置为通知生成器的添加操作方法。

RemoteInput remoteInput = new RemoteInput.Builder(Constants.KEY_TEXT_REPLY)
        .setLabel("Reply")
        .build();


Notification.Action action = new Notification.Action.Builder(Icon.createWithResource(getApplicationContext(), R.drawable.reply), "Reply", replyPendingIntent)
        .addRemoteInput(remoteInput)
        .build();

notificationBuilder.addAction(action);

如何从意向中获取回复输入值

Bundle remoteInput = RemoteInput.getResultsFromIntent(getIntent());

if (remoteInput != null) {

    Intent intent = getIntent();
    String inputText = remoteInput.getCharSequence(Constants.KEY_TEXT_REPLY).toString();
}

信息风格

安卓通知的另一个新特性是消息风格。这种风格对于消息应用程序来说非常方便。您也可以用新邮件显示旧邮件。此外,您还可以使用这种风格的直接回复功能。

如何在通知中使用消息传递样式

首先,我们必须创建一个通知。MessagingStyle对象。此类获取显示用户名参数。您可以将对话标题设置为显示在通知的顶部。您可以使用消息样式对象的添加消息方法来添加要显示的消息。最后,消息对象必须设置为通知生成器的setStyle方法。

Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("Me");

messagingStyle.setConversationTitle(messageBodiesList.size() + " new messages with " + senderUserName);

messagingStyle.addMessage(messageBodyElement, System.currentTimeMillis(), senderUserName + ": ");

notificationBuilder.setStyle(messagingStyle);

如果将直接回复操作设置为通知生成器的添加操作方法,则可以在此样式上使用直接回复功能。

备注

要接收通知,应用程序应该获得互联网许可。在Andorid . N上,将这些权限添加到manifest.xml文件是不够的。您必须编写代码来获得这些权限。

ActivityCompat.requestPermissions(this, new String[]{"android.permission.INTERNET"}, 1);

您必须重写活动类的onRequestPermissionsResult方法以获取用户对权限的回答。

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {}