Repository navigation
feat(messaging, ios): add a notification service extension from the Expo config plugin #9401
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
base: main
Are you sure you want to change the base?
Changes from all commits
d3926e4
ac2262f
5a16d8e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,37 @@ This is a quick guide to display an image in an incoming notification. Android h | |
| **🚨 Before you start** | ||
| Be sure you already have Cloud Messaging installed and set up. In case you don't [get started here](/messaging/usage). | ||
|
|
||
| ## Expo | ||
|
|
||
| Expo projects can let the `@react-native-firebase/messaging` config plugin add the extension instead of following the steps below: | ||
|
|
||
| ```json | ||
| { | ||
| "expo": { | ||
| "plugins": [ | ||
| [ | ||
| "@react-native-firebase/messaging", | ||
| { | ||
| "ios": { | ||
| "notificationServiceExtension": true | ||
| } | ||
| } | ||
| ] | ||
| ] | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| On `npx expo prebuild`, the plugin adds a `NotificationServiceExtension` target embedded in the app, with its source and `Info.plist` in `ios/NotificationServiceExtension/`. The extension links `FirebaseMessaging` through Swift Package Manager at the exact Firebase Apple SDK version of `@react-native-firebase/app`, the same package reference `pod install` links `FirebaseCore` into the app with (see [iOS SPM Support](/ios-spm)). Its bundle identifier is `<ios.bundleIdentifier>.NotificationServiceExtension`, and the plugin declares it to EAS Build (`extra.eas.build.experimental.ios.appExtensions`) so that EAS provisions and signs it. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add a sentence for apps that already get a notification service extension from another plugin (OneSignal's, for example)? Enabling this option as well gives them two extensions, so the docs should tell people not to combine them, or what to expect if they do.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added in 5a16d8e: iOS hands an app's notifications to a single notification service extension, so the docs now say not to enable this option when another plugin (OneSignal's, for example) already adds one, and to use one or the other. |
||
|
|
||
| To name the target differently, pass `{ "targetName": "ImageNotification" }` instead of `true`. The name is also the last component of the bundle identifier, so it may only contain letters, digits and hyphens. | ||
|
|
||
| iOS hands an app's notifications to a single notification service extension. If another config plugin already adds one (OneSignal's, for example), don't enable this option as well: only one of the two would handle your notifications. Use that extension, or this one, alone. | ||
|
|
||
| After turning the option off, run `npx expo prebuild --clean`: like other config plugins' changes, prebuild without `--clean` only adds to the native project, and leaves the target in it. | ||
|
|
||
| ## React Native CLI | ||
|
|
||
| **🏁 Ready to start** | ||
| The following steps will guide you through how to add a new target to your application to support payloads with an image. Open Xcode and let's get started. | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,205 @@ | ||
| // !$*UTF8*$! | ||
| { | ||
| archiveVersion = 1; | ||
| classes = { | ||
| }; | ||
| objectVersion = 54; | ||
| objects = { | ||
|
|
||
| /* Begin PBXBuildFile section */ | ||
| 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; | ||
| F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11748412D0307B40044C1D9 /* AppDelegate.swift */; }; | ||
| /* End PBXBuildFile section */ | ||
|
|
||
| /* Begin PBXFileReference section */ | ||
| 13B07F961A680F5B00A75B9A /* HelloWorld.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloWorld.app; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
| 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = HelloWorld/Images.xcassets; sourceTree = "<group>"; }; | ||
| 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = HelloWorld/Info.plist; sourceTree = "<group>"; }; | ||
| F11748412D0307B40044C1D9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = HelloWorld/AppDelegate.swift; sourceTree = "<group>"; }; | ||
| /* End PBXFileReference section */ | ||
|
|
||
| /* Begin PBXFrameworksBuildPhase section */ | ||
| 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { | ||
| isa = PBXFrameworksBuildPhase; | ||
| buildActionMask = 2147483647; | ||
| files = ( | ||
| ); | ||
| runOnlyForDeploymentPostprocessing = 0; | ||
| }; | ||
| /* End PBXFrameworksBuildPhase section */ | ||
|
|
||
| /* Begin PBXGroup section */ | ||
| 13B07FAE1A68108700A75B9A /* HelloWorld */ = { | ||
| isa = PBXGroup; | ||
| children = ( | ||
| F11748412D0307B40044C1D9 /* AppDelegate.swift */, | ||
| 13B07FB51A68108700A75B9A /* Images.xcassets */, | ||
| 13B07FB61A68108700A75B9A /* Info.plist */, | ||
| ); | ||
| name = HelloWorld; | ||
| sourceTree = "<group>"; | ||
| }; | ||
| 83CBB9F61A601CBA00E9B192 = { | ||
| isa = PBXGroup; | ||
| children = ( | ||
| 13B07FAE1A68108700A75B9A /* HelloWorld */, | ||
| 83CBBA001A601CBA00E9B192 /* Products */, | ||
| ); | ||
| indentWidth = 2; | ||
| sourceTree = "<group>"; | ||
| tabWidth = 2; | ||
| usesTabs = 0; | ||
| }; | ||
| 83CBBA001A601CBA00E9B192 /* Products */ = { | ||
| isa = PBXGroup; | ||
| children = ( | ||
| 13B07F961A680F5B00A75B9A /* HelloWorld.app */, | ||
| ); | ||
| name = Products; | ||
| sourceTree = "<group>"; | ||
| }; | ||
| /* End PBXGroup section */ | ||
|
|
||
| /* Begin PBXNativeTarget section */ | ||
| 13B07F861A680F5B00A75B9A /* HelloWorld */ = { | ||
| isa = PBXNativeTarget; | ||
| buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "HelloWorld" */; | ||
| buildPhases = ( | ||
| 13B07F871A680F5B00A75B9A /* Sources */, | ||
| 13B07F8C1A680F5B00A75B9A /* Frameworks */, | ||
| 13B07F8E1A680F5B00A75B9A /* Resources */, | ||
| ); | ||
| buildRules = ( | ||
| ); | ||
| dependencies = ( | ||
| ); | ||
| name = HelloWorld; | ||
| productName = HelloWorld; | ||
| productReference = 13B07F961A680F5B00A75B9A /* HelloWorld.app */; | ||
| productType = "com.apple.product-type.application"; | ||
| }; | ||
| /* End PBXNativeTarget section */ | ||
|
|
||
| /* Begin PBXProject section */ | ||
| 83CBB9F71A601CBA00E9B192 /* Project object */ = { | ||
| isa = PBXProject; | ||
| attributes = { | ||
| LastUpgradeCheck = 1130; | ||
| TargetAttributes = { | ||
| 13B07F861A680F5B00A75B9A = { | ||
| LastSwiftMigration = 1250; | ||
| }; | ||
| }; | ||
| }; | ||
| buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "HelloWorld" */; | ||
| compatibilityVersion = "Xcode 12.0"; | ||
| developmentRegion = en; | ||
| hasScannedForEncodings = 0; | ||
| knownRegions = ( | ||
| en, | ||
| Base, | ||
| ); | ||
| mainGroup = 83CBB9F61A601CBA00E9B192; | ||
| productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; | ||
| projectDirPath = ""; | ||
| projectRoot = ""; | ||
| targets = ( | ||
| 13B07F861A680F5B00A75B9A /* HelloWorld */, | ||
| ); | ||
| }; | ||
| /* End PBXProject section */ | ||
|
|
||
| /* Begin PBXResourcesBuildPhase section */ | ||
| 13B07F8E1A680F5B00A75B9A /* Resources */ = { | ||
| isa = PBXResourcesBuildPhase; | ||
| buildActionMask = 2147483647; | ||
| files = ( | ||
| 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, | ||
| ); | ||
| runOnlyForDeploymentPostprocessing = 0; | ||
| }; | ||
| /* End PBXResourcesBuildPhase section */ | ||
|
|
||
| /* Begin PBXSourcesBuildPhase section */ | ||
| 13B07F871A680F5B00A75B9A /* Sources */ = { | ||
| isa = PBXSourcesBuildPhase; | ||
| buildActionMask = 2147483647; | ||
| files = ( | ||
| F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */, | ||
| ); | ||
| runOnlyForDeploymentPostprocessing = 0; | ||
| }; | ||
| /* End PBXSourcesBuildPhase section */ | ||
|
|
||
| /* Begin XCBuildConfiguration section */ | ||
| 13B07F941A680F5B00A75B9A /* Debug */ = { | ||
| isa = XCBuildConfiguration; | ||
| buildSettings = { | ||
| ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||
| CURRENT_PROJECT_VERSION = 1; | ||
| INFOPLIST_FILE = HelloWorld/Info.plist; | ||
| IPHONEOS_DEPLOYMENT_TARGET = 16.4; | ||
| PRODUCT_BUNDLE_IDENTIFIER = com.example.helloworld; | ||
| PRODUCT_NAME = HelloWorld; | ||
| SWIFT_VERSION = 5.0; | ||
| TARGETED_DEVICE_FAMILY = 1; | ||
| }; | ||
| name = Debug; | ||
| }; | ||
| 13B07F951A680F5B00A75B9A /* Release */ = { | ||
| isa = XCBuildConfiguration; | ||
| buildSettings = { | ||
| ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||
| CURRENT_PROJECT_VERSION = 1; | ||
| INFOPLIST_FILE = HelloWorld/Info.plist; | ||
| IPHONEOS_DEPLOYMENT_TARGET = 16.4; | ||
| PRODUCT_BUNDLE_IDENTIFIER = com.example.helloworld; | ||
| PRODUCT_NAME = HelloWorld; | ||
| SWIFT_VERSION = 5.0; | ||
| TARGETED_DEVICE_FAMILY = 1; | ||
| }; | ||
| name = Release; | ||
| }; | ||
| 83CBBA201A601CBA00E9B192 /* Debug */ = { | ||
| isa = XCBuildConfiguration; | ||
| buildSettings = { | ||
| CLANG_ENABLE_MODULES = YES; | ||
| IPHONEOS_DEPLOYMENT_TARGET = 15.1; | ||
| SDKROOT = iphoneos; | ||
| }; | ||
| name = Debug; | ||
| }; | ||
| 83CBBA211A601CBA00E9B192 /* Release */ = { | ||
| isa = XCBuildConfiguration; | ||
| buildSettings = { | ||
| CLANG_ENABLE_MODULES = YES; | ||
| IPHONEOS_DEPLOYMENT_TARGET = 15.1; | ||
| SDKROOT = iphoneos; | ||
| }; | ||
| name = Release; | ||
| }; | ||
| /* End XCBuildConfiguration section */ | ||
|
|
||
| /* Begin XCConfigurationList section */ | ||
| 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "HelloWorld" */ = { | ||
| isa = XCConfigurationList; | ||
| buildConfigurations = ( | ||
| 13B07F941A680F5B00A75B9A /* Debug */, | ||
| 13B07F951A680F5B00A75B9A /* Release */, | ||
| ); | ||
| defaultConfigurationIsVisible = 0; | ||
| defaultConfigurationName = Release; | ||
| }; | ||
| 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "HelloWorld" */ = { | ||
| isa = XCConfigurationList; | ||
| buildConfigurations = ( | ||
| 83CBBA201A601CBA00E9B192 /* Debug */, | ||
| 83CBBA211A601CBA00E9B192 /* Release */, | ||
| ); | ||
| defaultConfigurationIsVisible = 0; | ||
| defaultConfigurationName = Release; | ||
| }; | ||
| /* End XCConfigurationList section */ | ||
| }; | ||
| rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a good build-time check. The CI log shows Firebase's resource bundles being copied into the extension, so
FirebaseMessagingis linked statically and the extension is self-contained. Nothing exercises the extension with a notification, though. Could you confirm once, by hand, that an image actually shows? Either a push to a device, orxcrun simctl pushon a simulator with"mutable-content": 1inapsand an image URL (fcm_options.imagein the FCM payload). No need to automate it here.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, confirmed by hand on a real app: with the extension added by the plugin, an FCM notification with an image (
fcm_options.image) shows the image. It takes a built app receiving a real push:xcrun simctl pushdoesn't trigger notification service extensions.