如何为 iOS 版本 9 + 10(可能还有 8)实现 Apple 推送通知?

Posted

技术标签:

【中文标题】如何为 iOS 版本 9 + 10(可能还有 8)实现 Apple 推送通知?【英文标题】:How implement Apple Push Notifications for iOS versions 9 + 10 (and perhaps 8)? 【发布时间】:2017-06-07 11:27:20 【问题描述】:

我没有找到任何正式的 Apple 文档讨论如何同时为旧 ios 版本以及 iOS 10 正确实现推送通知。而且我看到的独立教程同样涵盖了单个 iOS 版本。

我看到了 iOS 10 的官方文档: Local and Remote Notifications Overview 但它没有评论支持早期的iOS版本。

还有一个 iOS 9 教程: Push Notifications Tutorial - Ray Wenderlich

我看到了各种关于人们必须做出改变才能让他们的旧解决方案在新版本上运行的 *** 线程:

Push notifications are not working in iOS 9 其中确实显示了处理 6 - 9 的代码。

didReceiveRemoteNotification not called , iOS 10


但我没有看到的是正确的做法,从今天开始(使用 iOS 10),但也支持旧设备。 ** 更新 ** App Store 表示只有 6% 的设备下载的应用程序比 ios 9 旧,所以 如果只支持 9 + 10 更容易,我会这样做。

(我尝试从 iOS 10 示例开始,但它立即在 iOS 9.3 模拟设备上崩溃,尽管它在 iOS 10 中运行良好。所以我得出结论,我应该从有关正确设置不同版本的信息开始。我可以发布该代码,但我认为这会将这个线程引向错误的方向。我宁愿从“应该”在多个 iOS 版本(包括 10)上工作的内容开始。)

如果我没有找到解决方案,我将开始组合来自不同 *** 代码 sn-ps 的代码......但认真的吗?我一定是遗漏了一些东西,因为大概每个 iOS 开发人员都有这个问题。


相反,我可以从一个较旧的示例开始,然后按照更改使其与 iOS 10 一起使用 - 但这会充分利用 iOS 10 吗?

注意:我正在使用 Xamarin C# 进行编程,但 Objective-C 或 Swift 答案同样有用。

【问题讨论】:

ToolmakerSteve,你想要本地通知还是远程通知?如果是远程,则应使用真机测试。 @aircraft 本地和远程。我的公司在应用商店中有一个应用,该应用在 iOS 10 设备上运行,但在 iOS 9.3 设备上崩溃。所以是的,它已经在真实设备上进行了测试。 [不幸的是,经过艰苦的测试......] 还不知道是本地还是远程崩溃了 - 不幸的是我没有编写这段代码,我只是在学习这一切,所以我可以修复应用程序。 @aircraft .. 刚刚得知,如果不越狱我的 iPhone,就无法再回到 iOS 10 之前的版本。不愿意这样做,所以尽可能在模拟器上进行测试,然后将 TestFlight 版本发送给使用 iOS 9.3 的人 iOS 6?!为什么? -___- 现在即使是 7 也有点疯狂。 Apple 建议您支持最后 2 个版本。那将是 9 和 10。对于某些项目,我仍然支持 8。即使这样也很好,但是6?!哈哈.. @Brandon - 我同意。我只提到了older,因为其中一个代码sn-ps显示了较早的版本,回到6。我将其更改为8 - 10。 【参考方案1】:

这是 Xamarin C# 代码(与 Objective-C 不同的语法和大小写,但我认为它可以逐行翻译为 Objective-C)。

在 iOS 9.3 和 iOS 10.2 上测试。

初始化“本地”和“远程”通知:

// "UIApplicationDelegate" is for "local" notifications,
// "IUNUserNotificationCenterDelegate, IMessagingDelegate" for "remote" notifications.
public class AppDelegate : UIApplicationDelegate,
    IUNUserNotificationCenterDelegate, IMessagingDelegate

    ...
    public override bool FinishedLaunching( UIApplication application, NSDictionary launchOptions )
    
        ...
        RegisterForOurRemoteNotifications( this );
        RegisterForOurLocalNotifications();
        ...
    
    ...

    // --- Comment out if not using Google FCM. ---
    public override void RegisteredForRemoteNotifications( UIApplication application, NSData deviceToken )
    
        //base.RegisteredForRemoteNotifications( application, deviceToken );
        Firebase.InstanceID.InstanceId.SharedInstance.SetApnsToken( deviceToken,
                                                                   Firebase.InstanceID.ApnsTokenType.Sandbox );
    

    ...
    // ----- "static"; Could be in another class. -----

    // These flags are for our convenience, so we know initialization was done.
    static bool IsRegisteredForNotifications;
    static bool IsRegisteredForRemoteNotifications;
    // Optional - true when we are using Google "Firebase Cloud Messaging".
    static bool HasFCM;

    public static void RegisterForOurRemoteNotifications( AppDelegate del )
    
        // Google "Firebase Cloud Messaging" (FCM) Monitor token generation
        // (Uncomment, if you are using FCM for notifications.)
        //InstanceId.Notifications.ObserveTokenRefresh( TokenRefreshNotification );

        if (UIDevice.CurrentDevice.CheckSystemVersion( 10, 0 )) 
            // iOS 10 or later
            var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound;
            UNUserNotificationCenter.Current.RequestAuthorization( authOptions, ( granted, error ) => 
                Console.WriteLine( granted );
             );

            // For iOS 10 display notification (sent via APNS)
            UNUserNotificationCenter.Current.Delegate = del;

            // For iOS 10 data message (sent via Google FCM).
            // (Uncomment, if you are using FCM for notifications.)
            // TBD: If NOT using FCM, you may need some other lines of code here.
            //Messaging.SharedInstance.RemoteMessageDelegate = del;

         else 
            // iOS 9 or before
            var allNotificationTypes = UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound;
            var settings = UIUserNotificationSettings.GetSettingsForTypes( allNotificationTypes, null );
            UIApplication.SharedApplication.RegisterUserNotificationSettings( settings );
        

        UIApplication.SharedApplication.RegisterForRemoteNotifications();
        IsRegisteredForRemoteNotifications = true;

        // Uncomment if using Google "Firebase Cloud Messaging" (FCM).
        //TokenRefreshNotification( null, null );
        //if (UIDevice.CurrentDevice.CheckSystemVersion( 9, 0 )) // Needed to call this twice on iOS 9 for some reason.
        //  TokenRefreshNotification( null, null );


        UIApplication.SharedApplication.SetMinimumBackgroundFetchInterval( UIApplication.BackgroundFetchIntervalMinimum );
    

    public static void RegisterForOurLocalNotifications()
                
        // --- Our app's notification actions. ---
        UNNotificationAction followAction = UNNotificationAction.FromIdentifier( "follow", PS.LocalizedString( "Follow" ), UNNotificationActionOptions.None );
        UNNotificationAction likeAction = UNNotificationAction.FromIdentifier( "like", PS.LocalizedString( "Like" ), UNNotificationActionOptions.None );
        // ...

        // --- Our app's notification categories ---
        UNNotificationCategory followCategory = UNNotificationCategory.FromIdentifier( "followCategory", new UNNotificationAction[]  followAction, likeAction ,
                                                                                new string[]  , UNNotificationCategoryOptions.None );
        // ...

        // --- All of the app's categories from above ---
        var categories = new UNNotificationCategory[]  followCategory /*, ...*/ ;


        // --- Same for all apps ---
        UIUserNotificationSettings settings = UIUserNotificationSettings.GetSettingsForTypes(
                                                  UIUserNotificationType.Alert |
                                                  UIUserNotificationType.Badge |
                                                  UIUserNotificationType.Sound
            , new NSSet( categories ) );
        UIApplication.SharedApplication.RegisterUserNotificationSettings( settings );


        if (UIDevice.CurrentDevice.CheckSystemVersion( 10, 0 )) 
            UNUserNotificationCenter.Current.SetNotificationCategories( new NSSet<UNNotificationCategory>( categories ) );

            UNUserNotificationCenter.Current.RequestAuthorization( UNAuthorizationOptions.Alert | UNAuthorizationOptions.Sound | UNAuthorizationOptions.Badge,
                                                                  ( result, err ) => 
                                                                      Console.WriteLine( result.ToString() );
                                                                   );
        

        IsRegisteredForNotifications = true;
    



    // -------------------------------------------------------
    // --- These are for Google "Firebase Cloud Messaging" ---
    // (Comment out if not using FCM.)

    public static string Token;

    static void TokenRefreshNotification( object sender, NSNotificationEventArgs e )
    
        // This method will be fired every time a new token is generated, including the first
        // time. So if you need to retrieve the token as soon as it is available this is where that
        // should be done.
        //var refreshedToken = InstanceId.SharedInstance.Token;

        ConnectToFCM( UIApplication.SharedApplication.KeyWindow.RootViewController );

        // TODO: If necessary send token to application server.
    


    public static void ConnectToFCM( UIViewController fromViewController )
    
        Messaging.SharedInstance.Connect( error => 
            if (error != null) 
                Helper.logD( "Unable to connect to FCM", error.LocalizedDescription );
             else 
                //var options = new NSDictionary();
                //options.SetValueForKey( DeviceToken, Constants.RegisterAPNSOption );
                //options.SetValueForKey( new NSNumber( true ), Constants.APNSServerTypeSandboxOption );

                //InstanceId.SharedInstance.GetToken("", InstanceId.ScopeFirebaseMessaging 
                Token = InstanceId.SharedInstance.Token;

                Console.WriteLine( $"Token: InstanceId.SharedInstance.Token" );
                HasFCM = true;
            
         );
    
    // ------------------ End Google FCM ---------------------
    // -------------------------------------------------------

上面的代码初始化您的应用程序,以便它可以接收通知。

重要提示:您还需要为您的应用设置适当的权限;请参阅 Apple 文档或问题中提到的链接。你需要这个文件:

权利.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>aps-environment</key>
    <string>development</string>
</dict>
</plist>

&lt;string&gt; 以上必须包含“开发”或“生产”。 (我不知道我们的应用程序在这里仍然说“开发”的意义;我还没有检查构建的内容以查看它是否在提交给 Apple 之前被 Xcode 自动更改为“生产”。根据https://***.com/a/40857877/199364 它确实。)


然后你需要代码来发送 [例如您的应用告诉您的服务器通知您朋友的设备您现在正在做什么] 并接收本地或远程通知。在我们的应用程序中,该代码与我们特定的通知操作和类别相结合;我没有时间提取一个简洁的版本在这里发布。有关完整详细信息,请参阅 Apple 文档或原始问题中提到的链接。

以下是接收通知的基本方法(添加到上面的class AppDelegate):

    public override void ReceivedLocalNotification( UIApplication application, UILocalNotification notification )
    
        ...
    

    public override void DidReceiveRemoteNotification( UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler )
    
        ...
    


    [Export( "userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:" )]
    public void DidReceiveNotificationResponse( UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler )
    
        ...
    

您可能想要/需要覆盖或实现的其他方法(另请参阅上面class AppDelegate 上声明的接口);其中一些可能特定于 FCM:

ApplicationReceivedRemoteMessage
ReceivedRemoteNotification
WillPresentNotification
PerformFetch (for background notifications)
HandleAction

【讨论】:

以上是关于如何为 iOS 版本 9 + 10(可能还有 8)实现 Apple 推送通知?的主要内容,如果未能解决你的问题,请参考以下文章

如何为 PHP 8 设置合适的 Apache 版本

如何为不同的客户端在应用商店中管理相同iOS应用的不同版本

如何为我的 iOS 应用程序集成维护模式

如何在 Swift 中为 iOS 10 和 iOS 9.3 初始化 NSManagedObject 子类

如何为 iOS 版本运行特定委托?

如何为 iOS 7 和 iOS 8 实现搜索控制器