Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for handling invalidated PushKit tokens #11

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
# Flutter Voip Push Notification

## 0.0.4

- Add support for invalidating tokens

## 0.0.3
* Rename IOSNotificationSettings to NotificationSettings and refactored example

- Rename IOSNotificationSettings to NotificationSettings and refactored example

## 0.0.2
* Add support for showing local notification, uses app delegate as push delegate

- Add support for showing local notification, uses app delegate as push delegate

## 0.0.1

* Initial release.
- Initial release.
39 changes: 31 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Flutter VoIP Push Notification

[![pub package](https://img.shields.io/pub/v/flutter_voip_push_notification.svg)](https://pub.dartlang.org/packages/flutter_voip_push_notification)
Flutter VoIP Push Notification - Currently iOS >= 8.0 only

Expand All @@ -22,10 +23,8 @@ Please refer to [VoIP Best Practices][2].

**Note**: Do NOT follow the `Configure VoIP Push Notification` part from the above link, use the instruction below instead.


#### AppDelegate.swift


```swift

...
Expand All @@ -50,6 +49,11 @@ import flutter_voip_push_notification /* <------ add this line */
FlutterVoipPushNotificationPlugin.didReceiveIncomingPush(with: payload, forType: type.rawValue)
}

// Handle invalidated push tokens
func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {
FlutterVoipPushNotificationPlugin.didInvalidatePushToken(forType: type.rawValue)
}

// Handle incoming pushes
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
// Process the received push
Expand All @@ -62,7 +66,6 @@ import flutter_voip_push_notification /* <------ add this line */

#### AppDelegate.m Modification


```objective-c

...
Expand All @@ -87,6 +90,12 @@ import flutter_voip_push_notification /* <------ add this line */
[FlutterVoipPushNotificationPlugin didUpdatePushCredentials:credentials forType:(NSString *)type];
}

// The system calls this method when a previously provided push token is no longer valid for use. No action is necessary on your part to reregister the push type. Instead, use this method to notify your server not to send push notifications using the matching push token.
- (void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(PKPushType)type
{
[FlutterVoipPushNotificationPlugin didInvalidatePushTokenForType:(NSString *)type];
}

// Handle incoming pushes
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type {
// Process the received push
Expand All @@ -100,14 +109,13 @@ import flutter_voip_push_notification /* <------ add this line */
```

## Usage

Add `flutter_voip_push_notification` as a [dependency in your pubspec.yaml file](https://flutter.io/using-packages/).

### Example


```dart

import 'package:flutter/material.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter_voip_push_notification/flutter_voip_push_notification.dart';
Expand Down Expand Up @@ -137,7 +145,11 @@ class _MyAppState extends State<MyApp> {
_voipPush.onTokenRefresh.listen(onToken);

// do configure voip push
_voipPush.configure(onMessage: onMessage, onResume: onResume);
_voipPush.configure(
onMessage: onMessage,
onResume: onResume,
onInvalidToken: onInvalidToken,
);
}

/// Called when the device token changes
Expand Down Expand Up @@ -169,9 +181,20 @@ class _MyAppState extends State<MyApp> {
return null;
}

showLocalNotification(Map<String, dynamic> notification) {
/// Call to receive an PushKit invalid token
///
/// [invalidToken] is the token that is no longer valid
/// Check out why a token could no longer be valid
/// https://stackoverflow.com/questions/46977380/voip-push-under-what-circumstances-does-didinvalidatepushtokenfortype-get-calle#47015401
Future<dynamic> onInvalidToken(String invalidToken) {
// Tell the server to remove the invalid token
print("received on background invalidToken: $invalidToken");
return null;
}

Future<void> showLocalNotification(Map<String, dynamic> notification) {
String alert = notification["aps"]["alert"];
_voipPush.presentLocalNotification(LocalNotification(
return _voipPush.presentLocalNotification(LocalNotification(
alertBody: "Hello $alert",
));
}
Expand Down
5 changes: 5 additions & 0 deletions example/ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ import flutter_voip_push_notification
FlutterVoipPushNotificationPlugin.didReceiveIncomingPush(with: payload, forType: type.rawValue)
}

// Handle invalidated push tokens
func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {
FlutterVoipPushNotificationPlugin.didInvalidatePushToken(forType: type.rawValue)
}

// Handle incoming pushes
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
// Process the received push
Expand Down
21 changes: 18 additions & 3 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ class _MyAppState extends State<MyApp> {
_voipPush.onTokenRefresh.listen(onToken);

// do configure voip push
_voipPush.configure(onMessage: onMessage, onResume: onResume);
_voipPush.configure(
onMessage: onMessage,
onResume: onResume,
onInvalidToken: onInvalidToken,
);
}

/// Called when the device token changes
Expand Down Expand Up @@ -59,9 +63,20 @@ class _MyAppState extends State<MyApp> {
return null;
}

showLocalNotification(Map<String, dynamic> notification) {
/// Call to receive an PushKit invalid token
///
/// [invalidToken] is the token that is no longer valid
/// Check out why a token could no longer be valid
/// https://stackoverflow.com/questions/46977380/voip-push-under-what-circumstances-does-didinvalidatepushtokenfortype-get-calle#47015401
Future<dynamic> onInvalidToken(String invalidToken) {
// Tell the server to remove the invalid token
print("received on background invalidToken: $invalidToken");
return null;
}

Future<void> showLocalNotification(Map<String, dynamic> notification) {
String alert = notification["aps"]["alert"];
_voipPush.presentLocalNotification(LocalNotification(
return _voipPush.presentLocalNotification(LocalNotification(
alertBody: "Hello $alert",
));
}
Expand Down
1 change: 1 addition & 0 deletions ios/Classes/FlutterVoipPushNotificationPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
@interface FlutterVoipPushNotificationPlugin : NSObject<FlutterPlugin>
+ (void)didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type;
+ (void)didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type;
+ (void)didInvalidatePushTokenForType:(NSString *)type;
@end
31 changes: 30 additions & 1 deletion ios/Classes/FlutterVoipPushNotificationPlugin.m
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#import "FlutterVoipPushNotificationPlugin.h"

NSString *const FlutterVoipRemoteNotificationsRegistered = @"voipRemoteNotificationsRegistered";
NSString *const FlutterVoipPushTokenInvalidated = @"voipPushTokenInvalidated";
NSString *const FlutterVoipLocalNotificationReceived = @"voipLocalNotificationReceived";
NSString *const FlutterVoipRemoteNotificationReceived = @"voipRemoteNotificationReceived";

Expand Down Expand Up @@ -42,6 +43,10 @@ - (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar
selector:@selector(handleRemoteNotificationReceived:)
name:FlutterVoipRemoteNotificationReceived
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handlePushTokenInvalidated:)
name:FlutterVoipPushTokenInvalidated
object:nil];
}
return self;
}
Expand Down Expand Up @@ -105,7 +110,9 @@ - (NSDictionary *)checkPermissions

- (void)voipRegistration
{
#ifdef DEBUG
NSLog(@"[FlutterVoipPushNotificationPlugin] voipRegistration");
#endif
dispatch_queue_t mainQueue = dispatch_get_main_queue();
// Create a push registry object
_voipRegistry = [[PKPushRegistry alloc] initWithQueue: mainQueue];
Expand Down Expand Up @@ -153,8 +160,9 @@ - (NSString*)getToken

+ (void)didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type
{
#ifdef DEBUG
NSLog(@"[FlutterVoipPushNotificationPlugin] didUpdatePushCredentials credentials.token = %@, type = %@", credentials.token, type);
#endif
NSMutableString *hexString = [NSMutableString string];
NSUInteger voipTokenLength = credentials.token.length;
const unsigned char *bytes = credentials.token.bytes;
Expand All @@ -167,17 +175,30 @@ + (void)didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSStr

}

+ (void)didInvalidatePushTokenForType:(NSString *)type
{
#ifdef DEBUG
NSLog(@"[FlutterVoipPushNotificationPlugin] didInvalidatePushTokenFor type = %@", type);
#endif
[[NSNotificationCenter defaultCenter] postNotificationName:FlutterVoipPushTokenInvalidated
object:self];
}

+ (void)didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type
{
#ifdef DEBUG
NSLog(@"[FlutterVoipPushNotificationPlugin] didReceiveIncomingPushWithPayload payload.dictionaryPayload = %@, type = %@", payload.dictionaryPayload, type);
#endif
[[NSNotificationCenter defaultCenter] postNotificationName:FlutterVoipRemoteNotificationReceived
object:self
userInfo:payload.dictionaryPayload];
}

- (void)handleRemoteNotificationsRegistered:(NSNotification *)notification
{
#ifdef DEBUG
NSLog(@"[FlutterVoipPushNotificationPlugin] handleRemoteNotificationsRegistered notification.userInfo = %@", notification.userInfo);
#endif
[_channel invokeMethod:@"onToken" arguments:notification.userInfo];
}

Expand Down Expand Up @@ -205,4 +226,12 @@ - (void)handleRemoteNotificationReceived:(NSNotification *)notification
}
}

- (void)handlePushTokenInvalidated:(NSNotification *)notification
{
#ifdef DEBUG
NSLog(@"[FlutterVoipPushNotificationPlugin] handlePushTokenInvalidated notification.userInfo = %@", notification.userInfo);
#endif
[_channel invokeMethod:@"onTokenInvalidated" arguments:@{@"deviceToken": [self getToken]}];
}

@end
39 changes: 29 additions & 10 deletions lib/flutter_voip_push_notification.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ import 'package:flutter/widgets.dart';
typedef Future<dynamic> MessageHandler(
bool isLocal, Map<String, dynamic> notification);

/// Handler for invalid PushKit tokens
///
/// [invalidToken] is the token that has been invalidated and should be removed from your server
/// https://stackoverflow.com/questions/46977380/voip-push-under-what-circumstances-does-didinvalidatepushtokenfortype-get-calle#47015401
typedef Future<dynamic> InvalidTokenHandler(String invalidToken);

class NotificationSettings {
const NotificationSettings({
this.sound = true,
Expand Down Expand Up @@ -92,7 +98,7 @@ class FlutterVoipPushNotification {
String _token;
MessageHandler _onMessage;
MessageHandler _onResume;

InvalidTokenHandler _onInvalidToken;

final StreamController<String> _tokenStreamController =
StreamController<String>.broadcast();
Expand All @@ -106,9 +112,11 @@ class FlutterVoipPushNotification {
void configure({
MessageHandler onMessage,
MessageHandler onResume,
InvalidTokenHandler onInvalidToken,
}) {
_onMessage = onMessage;
_onResume = onResume;
_onInvalidToken = onInvalidToken;
_channel.setMethodCallHandler(_handleMethod);
//_channel.invokeMethod<void>('configure');
}
Expand All @@ -120,12 +128,19 @@ class FlutterVoipPushNotification {
_token = map["deviceToken"];
_tokenStreamController.add(_token);
return null;
case "onTokenInvalidated":
final String invalidToken = map["deviceToken"];
return _onInvalidToken?.call(invalidToken);
case "onMessage":
return _onMessage(
map["local"], map["notification"].cast<String, dynamic>());
final bool isLocal = map["local"];
final Map<String, dynamic> notification =
map["notification"].cast<String, dynamic>();
return _onMessage?.call(isLocal, notification);
case "onResume":
return _onResume(
map["local"], map["notification"].cast<String, dynamic>());
final bool isLocal = map["local"];
final Map<String, dynamic> notification =
map["notification"].cast<String, dynamic>();
return _onResume?.call(isLocal, notification);
default:
throw UnsupportedError("Unrecognized JSON message");
}
Expand All @@ -138,16 +153,20 @@ class FlutterVoipPushNotification {

/// Prompts the user for notification permissions the first time
/// it is called.
Future<void> requestNotificationPermissions(
[NotificationSettings iosSettings =
const NotificationSettings()]) async {
Future<void> requestNotificationPermissions([
NotificationSettings iosSettings = const NotificationSettings(),
]) async {
_channel.invokeMethod<void>(
'requestNotificationPermissions', iosSettings.toMap());
'requestNotificationPermissions',
iosSettings.toMap(),
);
}

/// Schedules the local [notification] for immediate presentation.
Future<void> presentLocalNotification(LocalNotification notification) async {
await _channel.invokeMethod<void>(
'presentLocalNotification', notification.toMap());
'presentLocalNotification',
notification.toMap(),
);
}
}