如何在应用未运行/ app时获取推送通知

时间:2016-03-24 08:08:38

标签: ios swift apple-push-notifications terminate system-tray

我试过了 "在iOS上设置GCM客户端应用程序"来自Google开发者。 我的应用程序有一个Android版本,服务器成功发送推送通知到Android。在ios中,我可以将消息检索到didRecieveRemoteNotification函数。打印时看起来如下所示

aps: {
    alert =     {
        body = tyyy;
        title = "2 is going out at 03/24/2016 15:02:48";
    };
    badge = 2;
    sound = default;
}

当应用处于前台和后台时,它会收到此消息。当应用程序是后台时,系统托盘中没有显示任何内容。

当应用程序终止并且服务器正在发送推送通知时,我什么也没收到,没有显示任何活动。

我的代码如下。

AppDelegate.swift

import UIKit

 @UIApplicationMain



class AppDelegate: UIResponder, UIApplicationDelegate, GGLInstanceIDDelegate,  GCMReceiverDelegate {

var window: UIWindow?

var connectedToGCM = false
var subscribedToTopic = false
var gcmSenderID: String?
var registrationToken = "AIzaSy-.....-11bSP6v72UvyKY"
var registrationOptions = [String: AnyObject]()

let registrationKey = "onRegistrationCompleted"
let messageKey = "onMessageReceived"
let subscriptionTopic = "/topics/global"


func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

    NSUserDefaults.standardUserDefaults().setBool(true, forKey: "APP_RUNNING")


    // Override point for customization after application launch.

    // [START_EXCLUDE]
    // Configure the Google context: parses the GoogleService-Info.plist, and initializes
    // the services that have entries in the file
    var configureError:NSError?
    GGLContext.sharedInstance().configureWithError(&configureError)
    assert(configureError == nil, "Error configuring Google services: \(configureError)")
    gcmSenderID = GGLContext.sharedInstance().configuration.gcmSenderID
    // [END_EXCLUDE]
    // Register for remote notifications
    if #available(iOS 8.0, *) {
        let settings: UIUserNotificationSettings =
        UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
        application.registerUserNotificationSettings(settings)
        application.registerForRemoteNotifications()
    } else {
        // Fallback
        let types: UIRemoteNotificationType = [.Alert, .Badge, .Sound]
        application.registerForRemoteNotificationTypes(types)
    }

    // [END register_for_remote_notifications]
    // [START start_gcm_service]
    let gcmConfig = GCMConfig.defaultConfig()
    gcmConfig.receiverDelegate = self
    GCMService.sharedInstance().startWithConfig(gcmConfig)
    // [END start_gcm_service]

    application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound],categories: nil))

    if let options = launchOptions {
        if let notification = options[UIApplicationLaunchOptionsLocalNotificationKey] as? UILocalNotification {
            if let userInfo = notification.userInfo {

                // do something neat here
            }
        }
    }

    return true
}

func subscribeToTopic() {
    // If the app has a registration token and is connected to GCM, proceed to subscribe to the
    // topic
    if(registrationToken != "" && connectedToGCM) {
        GCMPubSub.sharedInstance().subscribeWithToken(self.registrationToken, topic: subscriptionTopic,
            options: nil, handler: {(error:NSError?) -> Void in
                if let error = error {
                    // Treat the "already subscribed" error more gently
                    if error.code == 3001 {
                        print("Already subscribed to \(self.subscriptionTopic)")
                    } else {
                        print("Subscription failed: \(error.localizedDescription)");
                    }
                } else {
                    self.subscribedToTopic = true;
                    NSLog("Subscribed to \(self.subscriptionTopic)");
                }
        })
    }
}

func application( application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken
    deviceToken: NSData ) {

        // [END receive_apns_token]
        // [START get_gcm_reg_token]
        // Create a config and set a delegate that implements the GGLInstaceIDDelegate protocol.
        let instanceIDConfig = GGLInstanceIDConfig.defaultConfig()
        instanceIDConfig.delegate = self
        // Start the GGLInstanceID shared instance with that config and request a registration
        // token to enable reception of notifications
        GGLInstanceID.sharedInstance().startWithConfig(instanceIDConfig)
        registrationOptions = [kGGLInstanceIDRegisterAPNSOption:deviceToken,
            kGGLInstanceIDAPNSServerTypeSandboxOption:true]
        GGLInstanceID.sharedInstance().tokenWithAuthorizedEntity(gcmSenderID,
            scope: kGGLInstanceIDScopeGCM, options: registrationOptions, handler: registrationHandler)
        // [END get_gcm_reg_token]


}

// [START receive_apns_token_error]
func application( application: UIApplication, didFailToRegisterForRemoteNotificationsWithError
    error: NSError ) {
        print("Registration for remote notification failed with error: \(error.localizedDescription)")
        // [END receive_apns_token_error]
        let userInfo = ["error": error.localizedDescription]
        NSNotificationCenter.defaultCenter().postNotificationName(
            registrationKey, object: nil, userInfo: userInfo)
}


// [START ack_message_reception]
func application( application: UIApplication,
    didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
        print("Notification received: \(userInfo)")
        // This works only if the app started the GCM service
        GCMService.sharedInstance().appDidReceiveMessage(userInfo);
        // Handle the received message
        // [START_EXCLUDE]
        NSNotificationCenter.defaultCenter().postNotificationName("reloadTableEvent", object: nil)
        NSNotificationCenter.defaultCenter().postNotificationName(messageKey, object: nil,
            userInfo: userInfo)
        // [END_EXCLUDE]
}

func application( application: UIApplication,
    didReceiveRemoteNotification userInfo: [NSObject : AnyObject],
    fetchCompletionHandler handler: (UIBackgroundFetchResult) -> Void) {
        print("Notification received: \(userInfo)")
        // This works only if the app started the GCM service
        GCMService.sharedInstance().appDidReceiveMessage(userInfo);
        // Handle the received message
        // Invoke the completion handler passing the appropriate UIBackgroundFetchResult value
        // [START_EXCLUDE]
        NSNotificationCenter.defaultCenter().postNotificationName(messageKey, object: nil,
            userInfo: userInfo)
        handler(UIBackgroundFetchResult.NoData);
        // [END_EXCLUDE]
}
// [END ack_message_reception]

func registrationHandler(registrationToken: String!, error: NSError!) {
    if (registrationToken != nil) {
        self.registrationToken = registrationToken
        print("Registration Token: \(registrationToken)")
        NSUserDefaults.standardUserDefaults().setValue(registrationToken, forKey: "registrationToken")
        self.subscribeToTopic()
        let userInfo = ["registrationToken": registrationToken]
        NSNotificationCenter.defaultCenter().postNotificationName(
            self.registrationKey, object: nil, userInfo: userInfo)
    } else {
        print("Registration to GCM failed with error: \(error.localizedDescription)")
        let userInfo = ["error": error.localizedDescription]
        NSNotificationCenter.defaultCenter().postNotificationName(
            self.registrationKey, object: nil, userInfo: userInfo)
    }
}

// [START on_token_refresh]
func onTokenRefresh() {
    // A rotation of the registration tokens is happening, so the app needs to request a new token.
    print("The GCM registration token needs to be changed.")
    GGLInstanceID.sharedInstance().tokenWithAuthorizedEntity(gcmSenderID,
        scope: kGGLInstanceIDScopeGCM, options: registrationOptions, handler: registrationHandler)
}
// [END on_token_refresh]

// [START upstream_callbacks]
func willSendDataMessageWithID(messageID: String!, error: NSError!) {
    if (error != nil) {
        // Failed to send the message.
    } else {
        // Will send message, you can save the messageID to track the message
    }
}

func didSendDataMessageWithID(messageID: String!) {
    // Did successfully send message identified by messageID
}
// [END upstream_callbacks]

func didDeleteMessagesOnServer() {
    // Some messages sent to this device were deleted on the GCM server before reception, likely
    // because the TTL expired. The client should notify the app server of this, so that the app
    // server can resend those messages.
}

func applicationWillResignActive(application: UIApplication) {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}

func applicationDidEnterBackground(application: UIApplication) {

    NSUserDefaults.standardUserDefaults().setBool(false, forKey: "APP_RUNNING")

    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.

    GCMService.sharedInstance().disconnect()
    // [START_EXCLUDE]
    self.connectedToGCM = false
    // [END_EXCLUDE]
}

func applicationWillEnterForeground(application: UIApplication) {

    NSUserDefaults.standardUserDefaults().setBool(true, forKey: "APP_RUNNING")

    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}

func applicationDidBecomeActive(application: UIApplication) {

    NSUserDefaults.standardUserDefaults().setBool(true, forKey: "APP_RUNNING")

    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.

    // Connect to the GCM server to receive non-APNS notifications
    GCMService.sharedInstance().connectWithHandler({(error:NSError?) -> Void in
        if let error = error {
            print("Could not connect to GCM: \(error.localizedDescription)")
        } else {
            self.connectedToGCM = true
            print("Connected to GCM")
            // [START_EXCLUDE]
            self.subscribeToTopic()
            // [END_EXCLUDE]
        }
    })
}

func applicationWillTerminate(application: UIApplication) {

    NSUserDefaults.standardUserDefaults().setBool(false, forKey: "APP_RUNNING")

    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {
    if let userInfo = notification.userInfo {
        NSNotificationCenter.defaultCenter().postNotificationName(
            "LoadEventViewController", object: nil, userInfo: userInfo)
    }
}




}

ViewController,弹出本地通知

func scheduleLocal(message: String) {
    let settings = UIApplication.sharedApplication().currentUserNotificationSettings()

    if settings!.types == .None {
        let ac = UIAlertController(title: "Can't schedule", message: "Either we don't have permission to schedule notifications, or we haven't asked yet.", preferredStyle: .Alert)
        ac.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
        presentViewController(ac, animated: true, completion: nil)
        return
    }

    // create a corresponding local notification
    let notification = UILocalNotification()
    notification.alertBody = message // text that will be displayed in the notification
    notification.alertAction = "open" // text that is displayed after "slide to..." on the lock screen - defaults to "slide to view"
    notification.fireDate = NSDate(timeIntervalSinceNow: 0) // todo item due date (when notification will be fired)
    notification.soundName = UILocalNotificationDefaultSoundName // play default sound
    notification.userInfo = ["UUID": 1, ] // assign a unique identifier to the notification so that we can retrieve it later
    notification.category = "TODO_CATEGORY"
    UIApplication.sharedApplication().scheduleLocalNotification(notification)
}

我有2个问题,

  1. 即使app处于终止状态,是否可以接收并显示推送通知?如果是这样,怎么样?
  2. 我在代码中做错了吗?

2 个答案:

答案 0 :(得分:1)

这是你的推送通知JSON?

aps: { "content-available" = 1; }

如果是,那么你发送的是无声推送。静音推送意味着用户无法获得视觉通知,只需调用应用程序代表回调您的应用程序。删除内容可用标记并改为传递消息文本。

如果应用程序位于前台,则iOS不会显示推送通知,而只是调用该委托。然后,您可以显示警报视图或您喜欢的其他内容。

或者你可以证明这一点:https://github.com/avielg/AGPushNote

关于状态"由用户"终止,这里是Apple doc(用于静默推送):

Apple documentation

  

使用此方法处理应用的传入远程通知。   与应用程序不同:didReceiveRemoteNotification:方法,即   只有当您的应用程序在前台即系统中运行时才会调用   当您的应用在前台运行或运行时调用此方法   背景。此外,如果您启用了远程通知   在后台模式下,系统启动你的应用程序(或从中唤醒它)   暂停状态)并将其置于远程时的后台状态   通知到了。但是,系统不会自动进行   如果用户强行退出,请启动您的应用。在那种情况下,   用户必须重新启动应用程序或在系统之前重新启动设备   尝试再次自动启动您的应用。

答案 1 :(得分:-4)

如果您还没有,请在Apple开发者上注册并配置您的应用以接收推送通知。您必须支付订阅费用(99美元/年)。

GCM在应用程序处于前台时直接向您的应用发送消息,但在后台GCM依靠APN(Apple推送通知服务)来访问您的设备。

因此,您的服务器发送到GCM的邮件格式必须精确格式化this finally worked for me