Ios 应用未运行/应用终止时如何获取推送通知

Ios 应用未运行/应用终止时如何获取推送通知,ios,swift,apple-push-notifications,terminate,system-tray,Ios,Swift,Apple Push Notifications,Terminate,System Tray,我试过了 谷歌开发者的“在iOS上设置GCM客户端应用程序”。 我的应用程序有android版本,服务器成功地向android发送推送通知。在ios中,我可以将消息检索到DidReceiveEmotentification函数。打印时如下图所示 aps: { alert = { body = tyyy; title = "2 is going out at 03/24/2016 15:02:48"; }; badge = 2;

我试过了 谷歌开发者的“在iOS上设置GCM客户端应用程序”。 我的应用程序有android版本,服务器成功地向android发送推送通知。在ios中,我可以将消息检索到DidReceiveEmotentification函数。打印时如下图所示

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)
}
我有两个问题

  • 即使应用程序处于终止状态,也可以接收和显示推送通知吗?如果是,怎么做
  • 我是否在代码中做错了什么

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

    GCM在前台直接向应用发送消息,但在后台,GCM依靠APNs(Apple推送通知服务)到达您的设备


    因此,服务器发送给GCM的消息格式必须精确格式化。

    这是推送通知JSON

    aps: { "content-available" = 1; }
    
    如果是,则发送无声推送。静默推送意味着用户没有收到可视通知,只调用应用程序的应用程序委托回调。删除“内容可用”标记并传递消息文本

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

    或者您可以显示以下内容:

    关于“由用户终止”的状态,这里是Apple文档(用于静默推送):

    使用此方法处理应用程序的传入远程通知。 与application:didReceiveMotonification:method不同,它是 仅当应用程序在前台运行时调用,系统 当应用程序在前台或后台运行时调用此方法 背景。此外,如果启用了远程通知 后台模式下,系统启动应用程序(或从 挂起状态),并在远程 通知来了。但是,该系统不会自动运行 如果用户已强制退出,则启动应用程序。在这种情况下 用户必须重新启动应用程序或重新启动设备,然后才能启动系统 尝试再次自动启动应用程序


    1) 是的,有可能吗2)你应该格式化你的代码以提高可读性你在apple Developer上配置了所有必要的东西了吗?你有证书吗?您需要同时配置GCM和Apple Developer。当我在iOS上使用GCM实现通知时,我很难实现后台通知。这是因为服务器发送给您的格式必须精确。我可以给你指出我的问题。当你的应用程序被终止或处于后台时,这是为你显示通知的操作系统。你创建的推送通知环境是什么?沙箱还是生产?你的代码不处理来自APN服务器的消息你说的“你的代码不处理来自APN服务器的消息”是什么意思?当应用程序位于后台时,我通过引用Mayerz链接来实现这一点。但在应用程序运行和终止时仍然没有显示通知。编辑问题以获得清晰的代码谢谢。完成。不过,当应用程序处于运行状态时,不会触发任何通知。终止状态时相同。当应用程序处于后台/手机锁定时,推送通知将成功显示。有什么想法吗?我现在收到的是,aps:{alert={body=tyyy;title=“2将于2016年3月24日15:02:48发布”};badge=2;“内容可用”=1;sound=default;}但是,当应用程序处于运行状态或终止时,不会显示推送通知。只有当应用程序位于后台时,才会显示推送通知。我不明白为什么一种状态有效,而另一种状态无效。我不是在问重新启动应用程序的问题,我是在问为什么在终止状态和运行状态下推送通知不显示在系统托盘中。你读了我的答案了吗?我已经回答了这个问题。在运行和终止状态下不显示推送通知。事情就是这样,iOS就是这样运行的。这是正确的行为。但是像gmail、facebook这样的应用程序即使在运行状态或终止状态下也会发送推送通知。他们是如何做到的?前台或后台推送通知都需要APNs。没有任何服务器可以直接向apple mobile发送通知,只能通过apple的APN