fix(push): register APNs and VoIP tokens on iOS
The local notifications plugin was initialised without iOS settings, so flutter_local_notifications threw "iOS settings must be set" before the app registered with Apple, and restoreMode swallowed the error. iPhones never sent the relay an APNs token for message wake-ups or a VoIP token for calls. - add DarwinInitializationSettings and request iOS permission explicitly - register with Apple before asking for notification permission (silent wake-ups do not need it) - send the VoIP token on its own (PLATFORM_APNS with an empty endpoint) when no APNs token is available yet - record a failed registration so the chat list shows it instead of failing silently - drop the force cast of rootViewController in AppDelegate and the unused SceneDelegate
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:sepot/core/push/push_service.dart';
|
||||
import 'package:sepot/core/services/messaging_service.dart';
|
||||
import 'package:sepot/core/storage/secure_storage.dart';
|
||||
import 'package:sepot/proto/sepot.pb.dart' as pb;
|
||||
|
||||
/// iPhony relayi neposílaly APNs ani VoIP token: init lokálních notifikací
|
||||
/// na iOS házel výjimku, ta zastavila registraci u Apple a restoreMode ji
|
||||
/// spolkl. Tady se hlídá pořadí kroků a skládání registrace bez iPhonu.
|
||||
class _FakeMessaging extends MessagingService {
|
||||
_FakeMessaging() : super(storage: SecureStorageService());
|
||||
|
||||
final registrations = <pb.RegisterPush>[];
|
||||
|
||||
@override
|
||||
void registerPush(pb.RegisterPush registration) =>
|
||||
registrations.add(registration);
|
||||
}
|
||||
|
||||
class _FakeIos extends IosPushPlatform {
|
||||
_FakeIos({
|
||||
this.apns,
|
||||
this.voip,
|
||||
this.permissionFails = false,
|
||||
this.registerFails = false,
|
||||
});
|
||||
|
||||
final String? apns;
|
||||
final String? voip;
|
||||
final bool permissionFails;
|
||||
final bool registerFails;
|
||||
final calls = <String>[];
|
||||
bool? failedFlag;
|
||||
String? mode;
|
||||
|
||||
@override
|
||||
bool get isIOS => true;
|
||||
|
||||
@override
|
||||
Future<void> registerApns() async {
|
||||
calls.add('register');
|
||||
if (registerFails) throw StateError('apns channel');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> apnsToken() async => apns;
|
||||
|
||||
@override
|
||||
Future<String?> voipToken() async => voip;
|
||||
|
||||
@override
|
||||
Future<void> requestPermission() async {
|
||||
calls.add('permission');
|
||||
if (permissionFails) {
|
||||
throw ArgumentError('iOS settings must be set');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> writeMode(String m) async => mode = m;
|
||||
|
||||
@override
|
||||
Future<void> setRegistrationFailed(bool failed) async => failedFlag = failed;
|
||||
}
|
||||
|
||||
void main() {
|
||||
setUp(() => FlutterSecureStorage.setMockInitialValues({}));
|
||||
|
||||
String text(List<int> bytes) => utf8.decode(bytes);
|
||||
|
||||
group('apnsRegistration', () {
|
||||
test('APNs i VoIP v jedné registraci', () {
|
||||
final reg = PushService.apnsRegistration(
|
||||
apnsToken: 'ab12',
|
||||
voipToken: 'cd34',
|
||||
)!;
|
||||
expect(reg.platform, pb.RegisterPush_Platform.PLATFORM_APNS);
|
||||
expect(text(reg.endpoint), 'ab12');
|
||||
expect(text(reg.voipToken), 'cd34');
|
||||
});
|
||||
|
||||
test('samotný VoIP token jde s prázdným endpointem', () {
|
||||
final reg = PushService.apnsRegistration(voipToken: 'cd34')!;
|
||||
expect(reg.platform, pb.RegisterPush_Platform.PLATFORM_APNS);
|
||||
expect(reg.endpoint, isEmpty);
|
||||
expect(text(reg.voipToken), 'cd34');
|
||||
});
|
||||
|
||||
test('bez tokenů se nic neposílá', () {
|
||||
expect(PushService.apnsRegistration(), isNull);
|
||||
});
|
||||
});
|
||||
|
||||
test(
|
||||
'selhané oprávnění nezastaví registraci u Apple ani token pro relay',
|
||||
() async {
|
||||
final messaging = _FakeMessaging();
|
||||
final ios = _FakeIos(apns: 'ab12', voip: 'cd34', permissionFails: true);
|
||||
await PushService(messaging, ios: ios).restoreMode();
|
||||
|
||||
expect(ios.calls, [
|
||||
'register',
|
||||
'permission',
|
||||
], reason: 'registrace u Apple před dotazem na oprávnění');
|
||||
expect(ios.mode, 'apns');
|
||||
expect(ios.failedFlag, isFalse);
|
||||
final reg = messaging.registrations.single;
|
||||
expect(text(reg.endpoint), 'ab12');
|
||||
expect(text(reg.voipToken), 'cd34');
|
||||
},
|
||||
);
|
||||
|
||||
test('VoIP token odejde, i když APNs token ještě nepřišel', () async {
|
||||
final messaging = _FakeMessaging();
|
||||
final push = PushService(messaging, ios: _FakeIos(voip: 'cd34'));
|
||||
await push.refreshVoipToken();
|
||||
final reg = messaging.registrations.single;
|
||||
expect(reg.endpoint, isEmpty);
|
||||
expect(text(reg.voipToken), 'cd34');
|
||||
});
|
||||
|
||||
test('selhaná registrace se nespolkne — stav pushů ukáže chybu', () async {
|
||||
final ios = _FakeIos(registerFails: true);
|
||||
await PushService(_FakeMessaging(), ios: ios).restoreMode();
|
||||
expect(ios.failedFlag, isTrue);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user