Skip to content

Commit 0091515

Browse files
add image on notification support (#2644)
1 parent 208f053 commit 0091515

File tree

9 files changed

+301
-0
lines changed

9 files changed

+301
-0
lines changed

Example/Firebase.xcodeproj/project.pbxproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@
149149
511DD2A92225C8C40094D78D /* FIRMessagingAnalyticsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE37C63A2163D5F30025D03E /* FIRMessagingAnalyticsTest.m */; };
150150
511DD2AA2225C8D50094D78D /* FIRMessagingTestUtilities.h in Sources */ = {isa = PBXBuildFile; fileRef = EDF5242A21EA364600BB24C6 /* FIRMessagingTestUtilities.h */; };
151151
511DD2AB2225C8D50094D78D /* FIRMessagingTestUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = EDF5242B21EA364600BB24C6 /* FIRMessagingTestUtilities.m */; };
152+
51284D16224ABD6A00274321 /* FIRMessagingExtensionHelperTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 51284D15224ABD6A00274321 /* FIRMessagingExtensionHelperTest.m */; };
153+
51284D17224ABE1E00274321 /* FIRMessagingExtensionHelperTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 51284D15224ABD6A00274321 /* FIRMessagingExtensionHelperTest.m */; };
152154
518854D92230652900CA4141 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 518854D82230652900CA4141 /* AppDelegate.m */; };
153155
518854DC2230652900CA4141 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 518854DB2230652900CA4141 /* ViewController.m */; };
154156
518854DF2230652900CA4141 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 518854DD2230652900CA4141 /* Main.storyboard */; };
@@ -1068,6 +1070,7 @@
10681070
511DD27F2225C4D20094D78D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
10691071
511DD2882225C5A80094D78D /* Messaging_Tests_tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Messaging_Tests_tvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
10701072
511DD2AC2226005D0094D78D /* FirebaseMessaging.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FirebaseMessaging.framework; sourceTree = BUILT_PRODUCTS_DIR; };
1073+
51284D15224ABD6A00274321 /* FIRMessagingExtensionHelperTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRMessagingExtensionHelperTest.m; sourceTree = "<group>"; };
10711074
518854D52230652900CA4141 /* InstanceID_Example_tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = InstanceID_Example_tvOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
10721075
518854D72230652900CA4141 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
10731076
518854D82230652900CA4141 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
@@ -2539,6 +2542,7 @@
25392542
DE9315C21E8738B70083EDBF /* Tests */ = {
25402543
isa = PBXGroup;
25412544
children = (
2545+
51284D15224ABD6A00274321 /* FIRMessagingExtensionHelperTest.m */,
25422546
DE8DB550221F5B470068BB0E /* FIRInstanceIDWithFCMTest.m */,
25432547
DE9315C81E8738B70083EDBF /* FIRMessagingFakeConnection.h */,
25442548
DE9315CA1E8738B70083EDBF /* FIRMessagingFakeSocket.h */,
@@ -3790,6 +3794,7 @@
37903794
developmentRegion = English;
37913795
hasScannedForEncodings = 0;
37923796
knownRegions = (
3797+
English,
37933798
en,
37943799
Base,
37953800
"es-MX",
@@ -4307,6 +4312,7 @@
43074312
511DD2972225C8C40094D78D /* FIRMessagingCodedInputStreamTest.m in Sources */,
43084313
511DD2982225C8C40094D78D /* FIRMessagingConnectionTest.m in Sources */,
43094314
511DD2992225C8C40094D78D /* FIRMessagingContextManagerServiceTest.m in Sources */,
4315+
51284D17224ABE1E00274321 /* FIRMessagingExtensionHelperTest.m in Sources */,
43104316
511DD29A2225C8C40094D78D /* FIRMessagingDataMessageManagerTest.m in Sources */,
43114317
511DD29B2225C8C40094D78D /* FIRMessagingFakeConnection.m in Sources */,
43124318
511DD29C2225C8C40094D78D /* FIRMessagingFakeSocket.m in Sources */,
@@ -4937,6 +4943,7 @@
49374943
DE9315FF1E8738E60083EDBF /* FIRMessagingRemoteNotificationsProxyTest.m in Sources */,
49384944
DEF61BFD216E8B1100A738D4 /* FIRMessagingReceiverTest.m in Sources */,
49394945
DE9315F81E8738E60083EDBF /* FIRMessagingDataMessageManagerTest.m in Sources */,
4946+
51284D16224ABD6A00274321 /* FIRMessagingExtensionHelperTest.m in Sources */,
49404947
DE9316051E8738E60083EDBF /* FIRMessagingTestNotificationUtilities.m in Sources */,
49414948
DE37C63B2163D5F30025D03E /* FIRMessagingAnalyticsTest.m in Sources */,
49424949
DE9315F61E8738E60083EDBF /* FIRMessagingConnectionTest.m in Sources */,
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* Copyright 2019 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <UIKit/UIKit.h>
18+
#import <XCTest/XCTest.h>
19+
20+
#import <OCMock/OCMock.h>
21+
22+
#import "FIRMessaging.h"
23+
#import "FIRMessagingExtensionHelper.h"
24+
25+
typedef void (^FIRMessagingContentHandler)(UNNotificationContent *content);
26+
27+
static NSString *const kFCMPayloadOptionsName = @"fcm_options";
28+
static NSString *const kFCMPayloadOptionsImageURLName = @"image";
29+
static NSString *const kValidImageURL =
30+
@"https://firebasestorage.googleapis.com/v0/b/fcm-ios-f7f9c.appspot.com/o/"
31+
@"chubbyBunny.jpg?alt=media&token=d6c56a57-c007-4b27-b20f-f267cc83e9e5";
32+
33+
@interface FIRMessagingExtensionHelper (ExposedForTest)
34+
#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
35+
- (void)loadAttachmentForURL:(NSURL *)attachmentURL
36+
completionHandler:(void (^)(UNNotificationAttachment *))completionHandler;
37+
#endif
38+
@end
39+
40+
@interface FIRMessagingExtensionHelperTest : XCTestCase {
41+
id _mockExtensionHelper;
42+
}
43+
@end
44+
45+
@implementation FIRMessagingExtensionHelperTest
46+
47+
- (void)setUp {
48+
[super setUp];
49+
FIRMessagingExtensionHelper *extensionHelper = [FIRMessaging extensionHelper];
50+
_mockExtensionHelper = OCMPartialMock(extensionHelper);
51+
}
52+
53+
- (void)tearDown {
54+
[_mockExtensionHelper stopMocking];
55+
}
56+
57+
#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
58+
- (void)testModifyNotificationWithValidPayloadData {
59+
XCTestExpectation *validPayloadExpectation =
60+
[self expectationWithDescription:@"Test payload is valid."];
61+
62+
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
63+
content.userInfo = @{kFCMPayloadOptionsName : @{kFCMPayloadOptionsImageURLName : kValidImageURL}};
64+
FIRMessagingContentHandler handler = ^(UNNotificationContent *content) {
65+
[validPayloadExpectation fulfill];
66+
};
67+
[_mockExtensionHelper populateNotificationContent:content withContentHandler:handler];
68+
69+
OCMVerify([_mockExtensionHelper loadAttachmentForURL:[OCMArg any]
70+
completionHandler:[OCMArg any]]);
71+
[self waitForExpectationsWithTimeout:1.0 handler:nil];
72+
}
73+
74+
- (void)testModifyNotificationWithInvalidPayloadData {
75+
XCTestExpectation *validPayloadExpectation =
76+
[self expectationWithDescription:@"Test payload is valid."];
77+
78+
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
79+
content.userInfo =
80+
@{kFCMPayloadOptionsName : @{kFCMPayloadOptionsImageURLName : @"a invalid URL"}};
81+
FIRMessagingContentHandler handler = ^(UNNotificationContent *content) {
82+
[validPayloadExpectation fulfill];
83+
};
84+
[_mockExtensionHelper populateNotificationContent:content withContentHandler:handler];
85+
86+
OCMReject([_mockExtensionHelper loadAttachmentForURL:[OCMArg any]
87+
completionHandler:[OCMArg any]]);
88+
[self waitForExpectationsWithTimeout:1.0 handler:nil];
89+
}
90+
91+
- (void)testModifyNotificationWithEmptyPayloadData {
92+
XCTestExpectation *validPayloadExpectation =
93+
[self expectationWithDescription:@"Test payload is valid."];
94+
95+
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
96+
content.userInfo =
97+
@{kFCMPayloadOptionsName : @{kFCMPayloadOptionsImageURLName : @"a invalid URL"}};
98+
FIRMessagingContentHandler handler = ^(UNNotificationContent *content) {
99+
[validPayloadExpectation fulfill];
100+
};
101+
[_mockExtensionHelper populateNotificationContent:content withContentHandler:handler];
102+
103+
OCMReject([_mockExtensionHelper loadAttachmentForURL:[OCMArg any]
104+
completionHandler:[OCMArg any]]);
105+
[self waitForExpectationsWithTimeout:1.0 handler:nil];
106+
}
107+
#endif
108+
109+
@end

Firebase/Messaging/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# 2019-04-02 -- v3.5.0
2+
- Add image support for notification. (#2644)
3+
14
# 2019-03-19 -- v3.4.0
25
- Adding community support for tvOS. (#2428)
36

Firebase/Messaging/FIRMMessageCode.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,4 +188,11 @@ typedef NS_ENUM(NSInteger, FIRMessagingMessageCode) {
188188
kFIRMessagingMessageCodeAnalyticsInvalidEvent = 19006, // I-FCM019006
189189
kFIRMessagingMessageCodeAnalytics007 = 19007, // I-FCM019007
190190
kFIRMessagingMessageCodeAnalyticsCouldNotInvokeAnalyticsLog = 19008, // I-FCM019008
191+
192+
// FIRMessagingExtensionHelper.m
193+
kFIRMessagingServiceExtensionImageInvalidURL = 20000,
194+
kFIRMessagingServiceExtensionImageNotDownloaded = 20001,
195+
kFIRMessagingServiceExtensionLocalFileNotCreated = 20002,
196+
kFIRMessagingServiceExtensionImageNotAttached = 20003,
197+
191198
};

Firebase/Messaging/FIRMessaging.m

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#import "FIRMessagingContextManagerService.h"
3030
#import "FIRMessagingDataMessageManager.h"
3131
#import "FIRMessagingDefines.h"
32+
#import "FIRMessagingExtensionHelper.h"
3233
#import "FIRMessagingLogger.h"
3334
#import "FIRMessagingPubSub.h"
3435
#import "FIRMessagingReceiver.h"
@@ -178,6 +179,15 @@ + (FIRMessaging *)messaging {
178179
return messaging;
179180
}
180181

182+
+ (FIRMessagingExtensionHelper *)extensionHelper {
183+
static dispatch_once_t once;
184+
static FIRMessagingExtensionHelper *extensionHelper;
185+
dispatch_once(&once, ^{
186+
extensionHelper = [[FIRMessagingExtensionHelper alloc] init];
187+
});
188+
return extensionHelper;
189+
}
190+
181191
- (instancetype)initWithAnalytics:(nullable id<FIRAnalyticsInterop>)analytics
182192
withInstanceID:(FIRInstanceID *)instanceID
183193
withUserDefaults:(GULUserDefaults *)defaults {
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright 2019 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import "FIRMessagingExtensionHelper.h"
18+
19+
#import "FIRMMessageCode.h"
20+
#import "FIRMessagingLogger.h"
21+
22+
static NSString *const kPayloadOptionsName = @"fcm_options";
23+
static NSString *const kPayloadOptionsImageURLName = @"image";
24+
25+
@interface FIRMessagingExtensionHelper ()
26+
@property(nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
27+
@property(nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
28+
29+
@end
30+
31+
@implementation FIRMessagingExtensionHelper
32+
33+
- (void)populateNotificationContent:(UNMutableNotificationContent *)content
34+
withContentHandler:(void (^)(UNNotificationContent *_Nonnull))contentHandler {
35+
self.contentHandler = [contentHandler copy];
36+
self.bestAttemptContent = content;
37+
38+
NSString *currentImageURL = content.userInfo[kPayloadOptionsName][kPayloadOptionsImageURLName];
39+
if (!currentImageURL) {
40+
[self deliverNotification];
41+
return;
42+
}
43+
#if TARGET_OS_IOS
44+
NSURL *attachmentURL = [NSURL URLWithString:currentImageURL];
45+
if (attachmentURL) {
46+
[self loadAttachmentForURL:attachmentURL
47+
completionHandler:^(UNNotificationAttachment *attachment) {
48+
self.bestAttemptContent.attachments = @[ attachment ];
49+
[self deliverNotification];
50+
}];
51+
} else {
52+
FIRMessagingLoggerError(kFIRMessagingServiceExtensionImageInvalidURL,
53+
@"The Image URL provided is invalid %@.", currentImageURL);
54+
[self deliverNotification];
55+
}
56+
#else
57+
[self deliverNotification];
58+
#endif
59+
}
60+
61+
#if TARGET_OS_IOS
62+
- (void)loadAttachmentForURL:(NSURL *)attachmentURL
63+
completionHandler:(void (^)(UNNotificationAttachment *))completionHandler {
64+
__block UNNotificationAttachment *attachment = nil;
65+
66+
NSURLSession *session = [NSURLSession
67+
sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
68+
[[session
69+
downloadTaskWithURL:attachmentURL
70+
completionHandler:^(NSURL *temporaryFileLocation, NSURLResponse *response, NSError *error) {
71+
if (error != nil) {
72+
FIRMessagingLoggerError(kFIRMessagingServiceExtensionImageNotDownloaded,
73+
@"Failed to download image given URL %@, error: %@\n",
74+
attachmentURL, error);
75+
completionHandler(attachment);
76+
return;
77+
}
78+
79+
NSFileManager *fileManager = [NSFileManager defaultManager];
80+
NSString *fileExtension =
81+
[NSString stringWithFormat:@".%@", [response.suggestedFilename pathExtension]];
82+
NSURL *localURL = [NSURL
83+
fileURLWithPath:[temporaryFileLocation.path stringByAppendingString:fileExtension]];
84+
[fileManager moveItemAtURL:temporaryFileLocation toURL:localURL error:&error];
85+
if (error) {
86+
FIRMessagingLoggerError(
87+
kFIRMessagingServiceExtensionLocalFileNotCreated,
88+
@"Failed to move the image file to local location: %@, error: %@\n", localURL,
89+
error);
90+
completionHandler(attachment);
91+
return;
92+
}
93+
94+
attachment = [UNNotificationAttachment attachmentWithIdentifier:@""
95+
URL:localURL
96+
options:nil
97+
error:&error];
98+
if (error) {
99+
FIRMessagingLoggerError(kFIRMessagingServiceExtensionImageNotAttached,
100+
@"Failed to create attachment with URL %@, error: %@\n",
101+
localURL, error);
102+
completionHandler(attachment);
103+
return;
104+
}
105+
completionHandler(attachment);
106+
}] resume];
107+
}
108+
#endif
109+
110+
- (void)deliverNotification {
111+
if (self.contentHandler) {
112+
self.contentHandler(self.bestAttemptContent);
113+
}
114+
}
115+
116+
@end
117+

Firebase/Messaging/Public/FIRMessaging.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ NS_SWIFT_NAME(MessagingRemoteMessage)
245245
@end
246246

247247
@class FIRMessaging;
248+
@class FIRMessagingExtensionHelper;
249+
248250
/**
249251
* A protocol to handle token update or data message delivery from FCM.
250252
*
@@ -331,6 +333,17 @@ NS_SWIFT_NAME(Messaging)
331333
*/
332334
+ (instancetype)messaging NS_SWIFT_NAME(messaging());
333335

336+
/**
337+
* FIRMessagingExtensionHelper
338+
*
339+
* Use FIRMessagingExtensionHelper to populate rich UI contents for your notifications.
340+
* e.g. If an image URL is set in your notification payload or on the console, call
341+
* FIRMessagingExtensionHelper API to render it on your notification.
342+
*
343+
* @return An instance of FIRMessagingExtensionHelper that handles the extensions API.
344+
*/
345+
+ (FIRMessagingExtensionHelper *)extensionHelper NS_SWIFT_NAME(serviceExtension()) NS_AVAILABLE_IOS(10.0);
346+
334347
/**
335348
* Unavailable. Use +messaging instead.
336349
*/
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2019 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
17+
#import <UserNotifications/UserNotifications.h>
18+
#endif
19+
20+
NS_ASSUME_NONNULL_BEGIN
21+
22+
__IOS_AVAILABLE(10.0)
23+
@interface FIRMessagingExtensionHelper : NSObject
24+
25+
/// Call this API to complete your notification content modification. If you like to
26+
/// overwrite some properties of the content instead of using the default payload,
27+
/// make sure to make your customized motification to the content before passing it to
28+
/// this call.
29+
- (void)populateNotificationContent:(UNMutableNotificationContent *)content
30+
withContentHandler:(void (^)(UNNotificationContent *_Nonnull))contentHandler;
31+
32+
@end
33+
34+
NS_ASSUME_NONNULL_END

Firebase/Messaging/Public/FirebaseMessaging.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@
1515
*/
1616

1717
#import "FIRMessaging.h"
18+
#import "FIRMessagingExtensionHelper.h"

0 commit comments

Comments
 (0)