Skip to content

Commit 2eca350

Browse files
committed
Share code between iOS and macOS
1 parent 5a953bc commit 2eca350

File tree

3 files changed

+350
-537
lines changed

3 files changed

+350
-537
lines changed
Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
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]
169+
registerForRemoteNotificationTypes:NSRemoteNotificationTypeSound |
170+
NSRemoteNotificationTypeAlert |
171+
NSRemoteNotificationTypeBadge];
172+
#endif
173+
if (_launchNotification != nil) {
174+
[_channel invokeMethod:@"onLaunch" arguments:_launchNotification];
175+
}
176+
result(nil);
177+
} else if ([@"subscribeToTopic" isEqualToString:method]) {
178+
NSString *topic = call.arguments;
179+
[[FIRMessaging messaging] subscribeToTopic:topic
180+
completion:^(NSError *error) {
181+
result(getFlutterError(error));
182+
}];
183+
} else if ([@"unsubscribeFromTopic" isEqualToString:method]) {
184+
NSString *topic = call.arguments;
185+
[[FIRMessaging messaging] unsubscribeFromTopic:topic
186+
completion:^(NSError *error) {
187+
result(getFlutterError(error));
188+
}];
189+
} else if ([@"getToken" isEqualToString:method]) {
190+
[[FIRInstanceID instanceID]
191+
instanceIDWithHandler:^(FIRInstanceIDResult *_Nullable instanceIDResult,
192+
NSError *_Nullable error) {
193+
if (error != nil) {
194+
NSLog(@"getToken, error fetching instanceID: %@", error);
195+
result(nil);
196+
} else {
197+
result(instanceIDResult.token);
198+
}
199+
}];
200+
} else if ([@"deleteInstanceID" isEqualToString:method]) {
201+
[[FIRInstanceID instanceID] deleteIDWithHandler:^void(NSError *_Nullable error) {
202+
if (error.code != 0) {
203+
NSLog(@"deleteInstanceID, error: %@", error);
204+
result([NSNumber numberWithBool:NO]);
205+
} else {
206+
#if TARGET_OS_IPHONE
207+
[[UIApplication sharedApplication] unregisterForRemoteNotifications];
208+
#else
209+
[[NSApplication sharedApplication] unregisterForRemoteNotifications];
210+
#endif
211+
result([NSNumber numberWithBool:YES]);
212+
}
213+
}];
214+
} else if ([@"autoInitEnabled" isEqualToString:method]) {
215+
BOOL value = [[FIRMessaging messaging] isAutoInitEnabled];
216+
result([NSNumber numberWithBool:value]);
217+
} else if ([@"setAutoInitEnabled" isEqualToString:method]) {
218+
NSNumber *value = call.arguments;
219+
[FIRMessaging messaging].autoInitEnabled = value.boolValue;
220+
result(nil);
221+
} else {
222+
result(FlutterMethodNotImplemented);
223+
}
224+
}
225+
226+
#if TARGET_OS_OSX || (defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0)
227+
// Receive data message on iOS 10 devices while app is in the foreground.
228+
- (void)applicationReceivedRemoteMessage:(FIRMessagingRemoteMessage *)remoteMessage {
229+
[self didReceiveRemoteNotification:remoteMessage.appData];
230+
}
231+
#endif
232+
233+
- (void)didReceiveRemoteNotification:(NSDictionary *)userInfo {
234+
if (_resumingFromBackground) {
235+
[_channel invokeMethod:@"onResume" arguments:userInfo];
236+
} else {
237+
[_channel invokeMethod:@"onMessage" arguments:userInfo];
238+
}
239+
}
240+
241+
#pragma mark - AppDelegate
242+
#if TARGET_OS_IPHONE
243+
- (BOOL)application:(UIApplication *)application
244+
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
245+
if (launchOptions != nil) {
246+
_launchNotification = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
247+
}
248+
return YES;
249+
}
250+
251+
- (void)applicationDidEnterBackground:(UIApplication *)application {
252+
_resumingFromBackground = YES;
253+
}
254+
255+
- (void)applicationDidBecomeActive:(UIApplication *)application {
256+
_resumingFromBackground = NO;
257+
// Clears push notifications from the notification center, with the
258+
// side effect of resetting the badge count. We need to clear notifications
259+
// because otherwise the user could tap notifications in the notification
260+
// center while the app is in the foreground, and we wouldn't be able to
261+
// distinguish that case from the case where a message came in and the
262+
// user dismissed the notification center without tapping anything.
263+
// TODO(goderbauer): Revisit this behavior once we provide an API for managing
264+
// the badge number, or if we add support for running Dart in the background.
265+
// Setting badgeNumber to 0 is a no-op (= notifications will not be cleared)
266+
// if it is already 0,
267+
// therefore the next line is setting it to 1 first before clearing it again
268+
// to remove all
269+
// notifications.
270+
application.applicationIconBadgeNumber = 1;
271+
application.applicationIconBadgeNumber = 0;
272+
}
273+
274+
- (BOOL)application:(UIApplication *)application
275+
didReceiveRemoteNotification:(NSDictionary *)userInfo
276+
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
277+
[self didReceiveRemoteNotification:userInfo];
278+
completionHandler(UIBackgroundFetchResultNoData);
279+
return YES;
280+
}
281+
282+
- (void)application:(UIApplication *)application
283+
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
284+
#ifdef DEBUG
285+
[[FIRMessaging messaging] setAPNSToken:deviceToken type:FIRMessagingAPNSTokenTypeSandbox];
286+
#else
287+
[[FIRMessaging messaging] setAPNSToken:deviceToken type:FIRMessagingAPNSTokenTypeProd];
288+
#endif
289+
290+
[_channel invokeMethod:@"onToken" arguments:[FIRMessaging messaging].FCMToken];
291+
}
292+
293+
// This will only be called for iOS < 10. For iOS >= 10, we make this call when we request
294+
// permissions.
295+
- (void)application:(UIApplication *)application
296+
didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
297+
NSDictionary *settingsDictionary = @{
298+
@"sound" : [NSNumber numberWithBool:notificationSettings.types & UIUserNotificationTypeSound],
299+
@"badge" : [NSNumber numberWithBool:notificationSettings.types & UIUserNotificationTypeBadge],
300+
@"alert" : [NSNumber numberWithBool:notificationSettings.types & UIUserNotificationTypeAlert],
301+
@"provisional" : [NSNumber numberWithBool:NO],
302+
};
303+
[_channel invokeMethod:@"onIosSettingsRegistered" arguments:settingsDictionary];
304+
}
305+
306+
- (void)messaging:(nonnull FIRMessaging *)messaging
307+
didReceiveRegistrationToken:(nonnull NSString *)fcmToken {
308+
[_channel invokeMethod:@"onToken" arguments:fcmToken];
309+
}
310+
311+
- (void)messaging:(FIRMessaging *)messaging
312+
didReceiveMessage:(FIRMessagingRemoteMessage *)remoteMessage {
313+
[_channel invokeMethod:@"onMessage" arguments:remoteMessage.appData];
314+
}
315+
316+
#else
317+
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
318+
if (notification != nil) {
319+
_launchNotification = notification.userInfo[NSApplicationLaunchUserNotificationKey];
320+
}
321+
}
322+
323+
- (void)applicationDidEnterBackground:(NSApplication *)application {
324+
_resumingFromBackground = YES;
325+
}
326+
327+
- (void)applicationDidBecomeActive:(NSApplication *)application {
328+
_resumingFromBackground = NO;
329+
}
330+
331+
- (BOOL)application:(NSApplication *)application
332+
didReceiveRemoteNotification:(NSDictionary *)userInfo {
333+
[self didReceiveRemoteNotification:userInfo];
334+
return YES;
335+
}
336+
337+
- (void)messaging:(nonnull FIRMessaging *)messaging
338+
didReceiveRegistrationToken:(nonnull NSString *)fcmToken {
339+
[_channel invokeMethod:@"onToken" arguments:fcmToken];
340+
}
341+
342+
- (void)messaging:(FIRMessaging *)messaging
343+
didReceiveMessage:(FIRMessagingRemoteMessage *)remoteMessage {
344+
[_channel invokeMethod:@"onMessage" arguments:remoteMessage.appData];
345+
}
346+
#endif
347+
348+
@end

0 commit comments

Comments
 (0)