Skip to content

Commit 4c4ee06

Browse files
committed
Share code between iOS and macOS
1 parent 5a953bc commit 4c4ee06

File tree

3 files changed

+348
-537
lines changed

3 files changed

+348
-537
lines changed
Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
// Copyright 2017 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
#include <TargetConditionals.h>
5+
6+
#import <UserNotifications/UserNotifications.h>
7+
8+
#import "FLTFirebaseMessagingPlugin.h"
9+
#import "UserAgent.h"
10+
11+
#import "Firebase/Firebase.h"
12+
13+
#if TARGET_OS_OSX || (defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0)
14+
@interface FLTFirebaseMessagingPlugin () <FIRMessagingDelegate>
15+
@end
16+
#endif
17+
18+
static FlutterError *getFlutterError(NSError *error) {
19+
if (error == nil) return nil;
20+
return [FlutterError errorWithCode:[NSString stringWithFormat:@"Error %ld", (long)error.code]
21+
message:error.domain
22+
details:error.localizedDescription];
23+
}
24+
25+
static NSObject<FlutterPluginRegistrar> *_registrar;
26+
27+
@implementation FLTFirebaseMessagingPlugin {
28+
FlutterMethodChannel *_channel;
29+
NSDictionary *_launchNotification;
30+
BOOL _resumingFromBackground;
31+
}
32+
33+
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
34+
_registrar = registrar;
35+
FlutterMethodChannel *channel =
36+
[FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/firebase_messaging"
37+
binaryMessenger:[registrar messenger]];
38+
FLTFirebaseMessagingPlugin *instance =
39+
[[FLTFirebaseMessagingPlugin alloc] initWithChannel:channel];
40+
// TODO(cbenhagen): Enable for macOS when https://github.com/flutter/flutter/issues/41471 is done.
41+
#if TARGET_OS_IPHONE
42+
[registrar addApplicationDelegate:instance];
43+
#endif
44+
[registrar addMethodCallDelegate:instance channel:channel];
45+
46+
SEL sel = NSSelectorFromString(@"registerLibrary:withVersion:");
47+
if ([FIRApp respondsToSelector:sel]) {
48+
[FIRApp performSelector:sel withObject:LIBRARY_NAME withObject:LIBRARY_VERSION];
49+
}
50+
}
51+
52+
- (instancetype)initWithChannel:(FlutterMethodChannel *)channel {
53+
self = [super init];
54+
55+
if (self) {
56+
_channel = channel;
57+
_resumingFromBackground = NO;
58+
if (![FIRApp appNamed:@"__FIRAPP_DEFAULT"]) {
59+
NSLog(@"Configuring the default Firebase app...");
60+
[FIRApp configure];
61+
NSLog(@"Configured the default Firebase app %@.", [FIRApp defaultApp].name);
62+
}
63+
[FIRMessaging messaging].delegate = self;
64+
}
65+
return self;
66+
}
67+
68+
- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
69+
NSString *method = call.method;
70+
if ([@"requestNotificationPermissions" isEqualToString:method]) {
71+
NSDictionary *arguments = call.arguments;
72+
if (@available(macOS 10.14, iOS 10.0, *)) {
73+
UNAuthorizationOptions authOptions = 0;
74+
NSNumber *provisional = arguments[@"provisional"];
75+
if ([arguments[@"sound"] boolValue]) {
76+
authOptions |= UNAuthorizationOptionSound;
77+
}
78+
if ([arguments[@"alert"] boolValue]) {
79+
authOptions |= UNAuthorizationOptionAlert;
80+
}
81+
if ([arguments[@"badge"] boolValue]) {
82+
authOptions |= UNAuthorizationOptionBadge;
83+
}
84+
85+
NSNumber *isAtLeastVersion12;
86+
if (@available(macOS 10.14, iOS 12, *)) {
87+
isAtLeastVersion12 = [NSNumber numberWithBool:YES];
88+
if ([provisional boolValue]) authOptions |= UNAuthorizationOptionProvisional;
89+
} else {
90+
isAtLeastVersion12 = [NSNumber numberWithBool:NO];
91+
}
92+
93+
[[UNUserNotificationCenter currentNotificationCenter]
94+
requestAuthorizationWithOptions:authOptions
95+
completionHandler:^(BOOL granted, NSError *_Nullable error) {
96+
if (error) {
97+
result(getFlutterError(error));
98+
return;
99+
}
100+
// This works for iOS >= 10. See
101+
// [UIApplication:didRegisterUserNotificationSettings:notificationSettings]
102+
// for ios < 10.
103+
[[UNUserNotificationCenter currentNotificationCenter]
104+
getNotificationSettingsWithCompletionHandler:^(
105+
UNNotificationSettings *_Nonnull settings) {
106+
NSDictionary *settingsDictionary = @{
107+
@"sound" : [NSNumber numberWithBool:settings.soundSetting ==
108+
UNNotificationSettingEnabled],
109+
@"badge" : [NSNumber numberWithBool:settings.badgeSetting ==
110+
UNNotificationSettingEnabled],
111+
@"alert" : [NSNumber numberWithBool:settings.alertSetting ==
112+
UNNotificationSettingEnabled],
113+
@"provisional" :
114+
[NSNumber numberWithBool:granted && [provisional boolValue] &&
115+
isAtLeastVersion12],
116+
};
117+
[self->_channel invokeMethod:@"onIosSettingsRegistered"
118+
arguments:settingsDictionary];
119+
}];
120+
result([NSNumber numberWithBool:granted]);
121+
}];
122+
123+
#if TARGET_OS_IPHONE
124+
[[UIApplication sharedApplication] registerForRemoteNotifications];
125+
#else
126+
[[NSApplication sharedApplication] registerForRemoteNotifications];
127+
#endif
128+
} else {
129+
#if TARGET_OS_IPHONE
130+
UIUserNotificationType notificationTypes = 0;
131+
if ([arguments[@"sound"] boolValue]) {
132+
notificationTypes |= UIUserNotificationTypeSound;
133+
}
134+
if ([arguments[@"alert"] boolValue]) {
135+
notificationTypes |= UIUserNotificationTypeAlert;
136+
}
137+
if ([arguments[@"badge"] boolValue]) {
138+
notificationTypes |= UIUserNotificationTypeBadge;
139+
}
140+
141+
UIUserNotificationSettings *settings =
142+
[UIUserNotificationSettings settingsForTypes:notificationTypes categories:nil];
143+
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
144+
145+
[[UIApplication sharedApplication] registerForRemoteNotifications];
146+
result([NSNumber numberWithBool:YES]);
147+
#else
148+
NSRemoteNotificationType notificationTypes = 0;
149+
if ([arguments[@"sound"] boolValue]) {
150+
notificationTypes |= NSRemoteNotificationTypeSound;
151+
}
152+
if ([arguments[@"alert"] boolValue]) {
153+
notificationTypes |= NSRemoteNotificationTypeAlert;
154+
}
155+
if ([arguments[@"badge"] boolValue]) {
156+
notificationTypes |= NSRemoteNotificationTypeBadge;
157+
}
158+
159+
[[NSApplication sharedApplication] registerForRemoteNotificationTypes:notificationTypes];
160+
result([NSNumber numberWithBool:YES]);
161+
#endif
162+
}
163+
} else if ([@"configure" isEqualToString:method]) {
164+
[FIRMessaging messaging].shouldEstablishDirectChannel = true;
165+
#if TARGET_OS_IPHONE
166+
[[UIApplication sharedApplication] registerForRemoteNotifications];
167+
#else
168+
[[NSApplication sharedApplication] registerForRemoteNotificationTypes:NSRemoteNotificationTypeSound | NSRemoteNotificationTypeAlert | NSRemoteNotificationTypeBadge];
169+
#endif
170+
if (_launchNotification != nil) {
171+
[_channel invokeMethod:@"onLaunch" arguments:_launchNotification];
172+
}
173+
result(nil);
174+
} else if ([@"subscribeToTopic" isEqualToString:method]) {
175+
NSString *topic = call.arguments;
176+
[[FIRMessaging messaging] subscribeToTopic:topic
177+
completion:^(NSError *error) {
178+
result(getFlutterError(error));
179+
}];
180+
} else if ([@"unsubscribeFromTopic" isEqualToString:method]) {
181+
NSString *topic = call.arguments;
182+
[[FIRMessaging messaging] unsubscribeFromTopic:topic
183+
completion:^(NSError *error) {
184+
result(getFlutterError(error));
185+
}];
186+
} else if ([@"getToken" isEqualToString:method]) {
187+
[[FIRInstanceID instanceID]
188+
instanceIDWithHandler:^(FIRInstanceIDResult *_Nullable instanceIDResult,
189+
NSError *_Nullable error) {
190+
if (error != nil) {
191+
NSLog(@"getToken, error fetching instanceID: %@", error);
192+
result(nil);
193+
} else {
194+
result(instanceIDResult.token);
195+
}
196+
}];
197+
} else if ([@"deleteInstanceID" isEqualToString:method]) {
198+
[[FIRInstanceID instanceID] deleteIDWithHandler:^void(NSError *_Nullable error) {
199+
if (error.code != 0) {
200+
NSLog(@"deleteInstanceID, error: %@", error);
201+
result([NSNumber numberWithBool:NO]);
202+
} else {
203+
#if TARGET_OS_IPHONE
204+
[[UIApplication sharedApplication] unregisterForRemoteNotifications];
205+
#else
206+
[[NSApplication sharedApplication] unregisterForRemoteNotifications];
207+
#endif
208+
result([NSNumber numberWithBool:YES]);
209+
}
210+
}];
211+
} else if ([@"autoInitEnabled" isEqualToString:method]) {
212+
BOOL value = [[FIRMessaging messaging] isAutoInitEnabled];
213+
result([NSNumber numberWithBool:value]);
214+
} else if ([@"setAutoInitEnabled" isEqualToString:method]) {
215+
NSNumber *value = call.arguments;
216+
[FIRMessaging messaging].autoInitEnabled = value.boolValue;
217+
result(nil);
218+
} else {
219+
result(FlutterMethodNotImplemented);
220+
}
221+
}
222+
223+
#if TARGET_OS_OSX || (defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0)
224+
// Receive data message on iOS 10 devices while app is in the foreground.
225+
- (void)applicationReceivedRemoteMessage:(FIRMessagingRemoteMessage *)remoteMessage {
226+
[self didReceiveRemoteNotification:remoteMessage.appData];
227+
}
228+
#endif
229+
230+
- (void)didReceiveRemoteNotification:(NSDictionary *)userInfo {
231+
if (_resumingFromBackground) {
232+
[_channel invokeMethod:@"onResume" arguments:userInfo];
233+
} else {
234+
[_channel invokeMethod:@"onMessage" arguments:userInfo];
235+
}
236+
}
237+
238+
#pragma mark - AppDelegate
239+
#if TARGET_OS_IPHONE
240+
- (BOOL)application:(UIApplication *)application
241+
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
242+
if (launchOptions != nil) {
243+
_launchNotification = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
244+
}
245+
return YES;
246+
}
247+
248+
- (void)applicationDidEnterBackground:(UIApplication *)application {
249+
_resumingFromBackground = YES;
250+
}
251+
252+
- (void)applicationDidBecomeActive:(UIApplication *)application {
253+
_resumingFromBackground = NO;
254+
// Clears push notifications from the notification center, with the
255+
// side effect of resetting the badge count. We need to clear notifications
256+
// because otherwise the user could tap notifications in the notification
257+
// center while the app is in the foreground, and we wouldn't be able to
258+
// distinguish that case from the case where a message came in and the
259+
// user dismissed the notification center without tapping anything.
260+
// TODO(goderbauer): Revisit this behavior once we provide an API for managing
261+
// the badge number, or if we add support for running Dart in the background.
262+
// Setting badgeNumber to 0 is a no-op (= notifications will not be cleared)
263+
// if it is already 0,
264+
// therefore the next line is setting it to 1 first before clearing it again
265+
// to remove all
266+
// notifications.
267+
application.applicationIconBadgeNumber = 1;
268+
application.applicationIconBadgeNumber = 0;
269+
}
270+
271+
- (BOOL)application:(UIApplication *)application
272+
didReceiveRemoteNotification:(NSDictionary *)userInfo
273+
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
274+
[self didReceiveRemoteNotification:userInfo];
275+
completionHandler(UIBackgroundFetchResultNoData);
276+
return YES;
277+
}
278+
279+
- (void)application:(UIApplication *)application
280+
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
281+
#ifdef DEBUG
282+
[[FIRMessaging messaging] setAPNSToken:deviceToken type:FIRMessagingAPNSTokenTypeSandbox];
283+
#else
284+
[[FIRMessaging messaging] setAPNSToken:deviceToken type:FIRMessagingAPNSTokenTypeProd];
285+
#endif
286+
287+
[_channel invokeMethod:@"onToken" arguments:[FIRMessaging messaging].FCMToken];
288+
}
289+
290+
// This will only be called for iOS < 10. For iOS >= 10, we make this call when we request
291+
// permissions.
292+
- (void)application:(UIApplication *)application
293+
didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
294+
NSDictionary *settingsDictionary = @{
295+
@"sound" : [NSNumber numberWithBool:notificationSettings.types & UIUserNotificationTypeSound],
296+
@"badge" : [NSNumber numberWithBool:notificationSettings.types & UIUserNotificationTypeBadge],
297+
@"alert" : [NSNumber numberWithBool:notificationSettings.types & UIUserNotificationTypeAlert],
298+
@"provisional" : [NSNumber numberWithBool:NO],
299+
};
300+
[_channel invokeMethod:@"onIosSettingsRegistered" arguments:settingsDictionary];
301+
}
302+
303+
- (void)messaging:(nonnull FIRMessaging *)messaging
304+
didReceiveRegistrationToken:(nonnull NSString *)fcmToken {
305+
[_channel invokeMethod:@"onToken" arguments:fcmToken];
306+
}
307+
308+
- (void)messaging:(FIRMessaging *)messaging
309+
didReceiveMessage:(FIRMessagingRemoteMessage *)remoteMessage {
310+
[_channel invokeMethod:@"onMessage" arguments:remoteMessage.appData];
311+
}
312+
313+
#else
314+
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
315+
if (notification != nil) {
316+
_launchNotification = notification.userInfo[NSApplicationLaunchUserNotificationKey];
317+
}
318+
}
319+
320+
- (void)applicationDidEnterBackground:(NSApplication *)application {
321+
_resumingFromBackground = YES;
322+
}
323+
324+
- (void)applicationDidBecomeActive:(NSApplication *)application {
325+
_resumingFromBackground = NO;
326+
}
327+
328+
- (BOOL)application:(NSApplication *)application
329+
didReceiveRemoteNotification:(NSDictionary *)userInfo {
330+
[self didReceiveRemoteNotification:userInfo];
331+
return YES;
332+
}
333+
334+
- (void)messaging:(nonnull FIRMessaging *)messaging
335+
didReceiveRegistrationToken:(nonnull NSString *)fcmToken {
336+
[_channel invokeMethod:@"onToken" arguments:fcmToken];
337+
}
338+
339+
- (void)messaging:(FIRMessaging *)messaging
340+
didReceiveMessage:(FIRMessagingRemoteMessage *)remoteMessage {
341+
[_channel invokeMethod:@"onMessage" arguments:remoteMessage.appData];
342+
}
343+
#endif
344+
345+
@end
346+

0 commit comments

Comments
 (0)