Flutter苹果推送通知插件flutter_apns_x的使用

发布于 1周前 作者 phonegap100 来自 Flutter

Flutter苹果推送通知插件flutter_apns_x的使用

flutter_apns_x 是一个用于在iOS设备上使用APNs令牌发送通知的新Flutter项目。该插件已经解决了已弃用问题。

为什么创建这个插件?

目前,唯一可用的推送通知插件是 firebase_messaging。这意味着即使在iOS上,也需要设置Firebase并与Google通信以发送推送通知。这个插件通过提供原生APNs实现来解决这个问题,同时保持为Android配置的Firebase。

使用方法

  1. 在Android上配置Firebase 根据以下说明配置Firebase:https://pub.dartlang.org/packages/firebase_messaging

  2. 在iOS上配置 确保你的应用正确配置支持推送通知,并生成证书/令牌用于发送推送通知。更多详细信息参见如何在iOS上运行示例应用部分。

  3. 在AppDelegate.m/AppDelegate.swift文件中添加以下代码

    Objective-C:

    if ([@available](/user/available)(iOS 11.0, *)) {
      [UNUserNotificationCenter currentNotificationCenter].delegate = (id<UNUserNotificationCenterDelegate>) self;
    }
    

    Swift:

    if #available(iOS 11.0, *) {
      UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
    }
    
  4. flutter_apns作为依赖项添加到你的pubspec.yaml文件中

    dependencies:
      flutter_apnsX: ^版本号
    
  5. 使用createPushConnector()方法配置推送服务

    import 'package:flutter_apnsX/flutter_apns/flutter_apns_only_x.dart';
    
    final connector = createPushConnector();
    
    connector.configureApns(
      onLaunch: (data) => onPush('onResume', data.payload.values.single),
      onResume: (data) => onPush('onResume', data.payload.values.single),
      onMessage: (data) => onPush('onResume', data.payload.values.single),
      onBackgroundMessage: (data) => onPush('onMessage', data.payload.values.single,)
    );
    
    // 请求通知权限
    connector.requestNotificationPermissions();
    
    // 获取token值
    connector.token.addListener(() {
        print('Token ${connector.token.value}');
    });
    
  6. 在设备上构建并测试你的解决方案 使用Firebase控制台(Android)和CURL(iOS)测试你的解决方案。

额外的APNs功能

在前台显示通知

final connector = createPushConnector();
if (connector is ApnsPushConnector) {
  connector.shouldPresent = (x) => Future.value(true);
}

处理预定义的动作

首先,配置支持的动作:

final connector = createPushConnector();
if (connector is ApnsPushConnector) {
  connector.setNotificationCategories([
    UNNotificationCategory(
      identifier: 'MEETING_INVITATION',
      actions: [
        UNNotificationAction(
          identifier: 'ACCEPT_ACTION',
          title: '接受',
          options: [],
        ),
        UNNotificationAction(
          identifier: 'DECLINE_ACTION',
          title: '拒绝',
          options: [],
        ),
      ],
      intentIdentifiers: [],
      options: [],
    ),
  ]);
}

然后,在推送处理程序中处理可能的操作:

Future<dynamic> onPush(String name, RemoteMessage payload) {
  final action = UNNotificationAction.getIdentifier(payload.data);

  if (action == 'MEETING_INVITATION') {
    // 做一些事情
  }

  return Future.value(true);
}

注意:如果用户在应用程序处于后台时点击了你的通知,推送将通过onResume传递,而不会实际唤醒应用程序。确保你的操作处理快速且无错误,因为后台运行的应用程序执行时间非常有限。

启用FirebaseCore

如果你想使用Firebase但不使用Firebase Messaging,可以在你的Info.plist中添加此配置条目以避免MissingPluginException

<key>flutter_apns.disable_firebase_core</key>
<false/>

flutter_apns_only - 不使用Firebase的APNs

如果你只关心APNs,可以使用flutter_apns_only插件。它不依赖于Firebase。为了确保没有swizzling(这是原始插件禁用Firebase所需的操作),可以在你的Info.plist中添加此配置条目:

<key>flutter_apns.disable_swizzling</key>
<true/>

排查问题

  1. 确保你是在实际设备上进行测试。注意:从11.4开始可能不再需要。

  2. 如果onToken方法未被调用,请在AppDelegate中添加错误日志记录,详见代码。

    Swift:

    import UIKit
    import Flutter
    
    [@UIApplicationMain](/user/UIApplicationMain)
    [@objc](/user/objc) class AppDelegate: FlutterAppDelegate {
      override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
      ) -> Bool {
        GeneratedPluginRegistrant.register(with: self)
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
      }
    
      func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
         NSLog("PUSH registration failed: \(error)")
      }
    }
    

    Objective-C:

    #include "AppDelegate.h"
    #include "GeneratedPluginRegistrant.h"
    
    [@implementation](/user/implementation) AppDelegate
    
    - (BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
      [GeneratedPluginRegistrant registerWithRegistry:self];
      // 应用程序启动后的自定义覆盖点。
      return [super application:application didFinishLaunchingWithOptions:launchOptions];
    }
    
    -(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
        NSLog(@"%@", error);
    }
    
    [@end](/user/end)
    
  3. 打开macOS上的Console应用程序,连接你的设备并运行你的应用程序。搜索日志中的“PUSH registration failed”字符串。错误消息会告诉你哪里出了问题。

如何在iOS上运行示例应用

在iOS上设置推送通知可能会比较棘手,因为它需要复杂的证书设置。以下指南描述了如何使用本包的示例应用程序从Mac向iPhone发送推送通知。此指南仅描述调试环境设置。

  1. 用Xcode打开示例ios文件夹。

  2. 选择Runner -> Signing & Capabilities。

  3. 选择你的开发团队并添加一个全局唯一的Bundle Identifier。图中的那个已经被占用: Xcode设置

  4. 访问https://developer.apple.com/account/resources/identifiers/list/bundleId,并按下加号按钮。

  5. 选择"App IDs"并按继续 注册应用

  6. 选择类型"App"。

  7. 选择"App ID Prefix",应与"Team ID"相同。

  8. 输入描述和Bundle ID。后者需要与步骤3中指定的Bundle ID相同。

  9. 选择推送通知功能 选择功能

  10. 按下"继续",然后按下"注册"。

  11. 访问https://developer.apple.com/account/resources/certificates,并通过按下加号按钮添加新证书。

  12. 选择"Apple Push Notification service SSL (Sandbox & Production)" 推送通知设置

  13. 选择你在第4-10步中定义的应用ID。

  14. 选择一个证书签名请求(CSR)文件。查看https://help.apple.com/developer-account/#/devbfa00fef7了解如何创建此证书。

  15. 完成后,下载新创建的Apple Push Services证书。

  16. 通过打开新下载的文件将证书添加到你的本地钥匙串。

  17. 按下钥匙链窗口左上角的"login",选择"我的证书"标签页 钥匙串

  18. 右键单击Apple-Push-Services证书并将其导出为.p12文件。

  19. 通过以下命令将.p12文件转换为.pem文件。请注意,"flutterApns"需要替换为你相应的证书名称。

    openssl pkcs12 -in flutterApns.p12 -out flutterApns.pem -nodes -clcerts
    
  20. 从Xcode或你最喜欢的IDE启动示例应用到物理iPhone设备。

  21. 当应用程序能够从APNs检索推送令牌时,设备令牌会自动打印出来。这发生在接受通知权限提示之后。

  22. 从你的开发Mac发送以下CURL。你可以通过将它们复制粘贴到终端并按回车键来执行CURL。

    curl -v \
    -d '{"aps":{"alert":"<你的消息>","badge":2}}' \
    -H "apns-topic: <已注册应用的bundle标识符>" \
    -H "apns-priority: 10" \
    --http2 \
    --cert <下载并转换的证书文件路径>.pem \
    https://api.development.push.apple.com/3/device/<设备令牌>
    

更多关于Flutter苹果推送通知插件flutter_apns_x的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter苹果推送通知插件flutter_apns_x的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用flutter_apns_x插件来配置苹果推送通知的示例代码。这个插件允许你通过Apple Push Notification Service (APNs)发送推送通知给iOS设备。

首先,确保你已经添加了flutter_apns_x到你的pubspec.yaml文件中:

dependencies:
  flutter:
    sdk: flutter
  flutter_apns_x: ^x.x.x  # 替换为最新版本号

然后运行flutter pub get来安装依赖。

iOS 配置

  1. 配置Info.plist

    确保你的Info.plist文件中包含必要的推送通知权限请求:

    <key>UIBackgroundModes</key>
    <array>
        <string>remote-notification</string>
    </array>
    <key>UIApplicationSupportsIndirectInputEvents</key>
    <true/>
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>
    <key>UNNotificationBreakthroughPriority</key>
    <integer>1</integer>
    
  2. 配置AppDelegate.swift

    AppDelegate.swift中,确保你已经注册了推送通知,并处理了APNs的回调:

    import UIKit
    import UserNotifications
    import Flutter
    import flutter_apns_x
    
    [@UIApplicationMain](/user/UIApplicationMain)
    [@objc](/user/objc) class AppDelegate: FlutterAppDelegate {
        override func application(
            _ application: UIApplication,
            didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
        ) -> Bool {
            GeneratedPluginRegistrant.register(with: self)
    
            // 注册推送通知
            UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in
                if granted {
                    DispatchQueue.main.async {
                        application.registerForRemoteNotifications()
                    }
                }
            }
    
            return super.application(application, didFinishLaunchingWithOptions: launchOptions)
        }
    
        override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
            // 将deviceToken发送到你的服务器以保存
            let flutterEngine = (UIApplication.shared.delegate as? FlutterAppDelegate)?.window??.rootViewController as? FlutterEngine
            let channel = FlutterMethodChannel(name: "your_channel_name", binaryMessenger: flutterEngine!.binaryMessenger)
            channel.invokeMethod("registerDeviceToken", arguments: deviceToken.hexString)
        }
    
        override func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
            print("Failed to register for remote notifications: \(error.localizedDescription)")
        }
    
        override func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
            // 处理接收到的推送通知
            print("Received remote notification: \(userInfo)")
            completionHandler(.newData)
        }
    }
    
    // 将Data类型转换为hex字符串的扩展
    extension Data {
        var hexString: String {
            return reduce("", { $0 + String(format: "%02x", $1) })
        }
    }
    
  3. Flutter端代码

    在你的Flutter项目中,你可以使用flutter_apns_x插件来发送推送通知。下面是一个简单的示例:

    import 'package:flutter/material.dart';
    import 'package:flutter_apns_x/flutter_apns_x.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(
              title: Text('Flutter APNs X Example'),
            ),
            body: Center(
              child: ElevatedButton(
                onPressed: () async {
                  // 发送推送通知的示例
                  String deviceToken = "your_device_token_here";  // 替换为实际的device token
    
                  var payload = APNSPayload(
                    aps: APS(
                      alert: Alert(title: "Hello", body: "This is a test notification!"),
                      sound: "default",
                      badge: 1,
                    ),
                    customData: {
                      "key1": "value1",
                      "key2": "value2"
                    },
                  );
    
                  var apnsClient = APNSClient(
                    teamId: "your_team_id",
                    keyId: "your_key_id",
                    keyPath: "path/to/your/AuthKey_your_key_id.p8",  // 替换为你的.p8文件路径
                  );
    
                  try {
                    await apnsClient.sendNotification(deviceToken, payload);
                    print("Notification sent successfully");
                  } catch (e) {
                    print("Failed to send notification: $e");
                  }
                },
                child: Text('Send Notification'),
              ),
            ),
          ),
        );
      }
    }
    
    class APS {
      Alert? alert;
      String? sound;
      int? badge;
    
      APS({this.alert, this.sound, this.badge});
    }
    
    class Alert {
      String? title;
      String? body;
    
      Alert({this.title, this.body});
    }
    
    class APNSPayload {
      APS? aps;
      Map<String, dynamic>? customData;
    
      APNSPayload({this.aps, this.customData});
    }
    
    class APNSClient {
      String teamId;
      String keyId;
      String keyPath;
    
      APNSClient({required this.teamId, required this.keyId, required this.keyPath});
    
      Future<void> sendNotification(String deviceToken, APNSPayload payload) async {
        // 发送通知的逻辑,这里需要实际的HTTP/2请求代码
        // 使用Dart的http2或类似库来发送请求
        // 注意:这只是一个示例,你需要实现完整的发送逻辑
      }
    }
    

注意

  • APNSClientsendNotification方法需要实现完整的HTTP/2请求来发送APNs通知。这通常涉及读取.p8文件,使用它创建JWT token,并发送HTTP/2请求到APNs服务器。由于这涉及到复杂的网络和加密逻辑,建议使用现有的Dart库如http2dart:http(结合适当的加密库)来实现。
  • 示例中的APNSClientsendNotification方法仅提供了框架,你需要根据APNs文档和库文档来完成实际的实现。
  • 确保你的.p8文件路径和权限正确设置。
  • 替换示例代码中的占位符(如your_team_id, your_key_id, path/to/your/AuthKey_your_key_id.p8)为实际的值。

希望这个示例能帮助你在Flutter项目中集成和使用flutter_apns_x插件。

回到顶部