diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a739d6..6776178 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Airship Expo Plugin Changelog +## Version 1.3.2-beta - December 013, 2024 +Beta version that fixes the Notification Service Extension for EAS builds. + ## Version 1.3.1 - December 06, 2024 Patch version that fixes the support for Aiship Notification Service Extension when using `use_frameworks`. diff --git a/package.json b/package.json index 32fc3a4..9c2f98c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "airship-expo-plugin", - "version": "1.3.1", + "version": "1.3.2-beta", "description": "Airship Expo config plugin", "main": "./app.plugin.js", "scripts": { diff --git a/plugin/NotificationServiceExtension/AirshipNotificationService.swift b/plugin/NotificationServiceExtension/AirshipNotificationService.swift new file mode 100644 index 0000000..1b5ec9b --- /dev/null +++ b/plugin/NotificationServiceExtension/AirshipNotificationService.swift @@ -0,0 +1,8 @@ +// NotificationService.swift + + +import AirshipServiceExtension + +class AirshipNotificationService: UANotificationServiceExtension { + +} diff --git a/plugin/NotificationServiceExtension/AirshipNotificationServiceExtension-Info.plist b/plugin/NotificationServiceExtension/NotificationServiceExtension-Info.plist similarity index 100% rename from plugin/NotificationServiceExtension/AirshipNotificationServiceExtension-Info.plist rename to plugin/NotificationServiceExtension/NotificationServiceExtension-Info.plist diff --git a/plugin/src/withAirship.ts b/plugin/src/withAirship.ts index 5fd6977..a062b86 100644 --- a/plugin/src/withAirship.ts +++ b/plugin/src/withAirship.ts @@ -25,12 +25,20 @@ export type AirshipIOSPluginProps = { /** * Optional. The local path to a custom Notification Service Extension. */ - notificationService?: string; + notificationService?: 'DEFAULT_AIRSHIP_SERVICE_EXTENSION' | string; /** * Optional. Airship will use a default one if not provided. * The local path to a Notification Service Extension Info.plist. */ notificationServiceInfo?: string; + /** + * Optional. Defaults to NotificationServiceExtension if not provided. + */ + notificationServiceTargetName?: string; + /** + * Optional. The Apple Development Team ID used to configure the Notification Service Extension target. + */ + developmentTeamID?: string; } export type AirshipPluginProps = { diff --git a/plugin/src/withAirshipIOS.ts b/plugin/src/withAirshipIOS.ts index 0831475..dbefe10 100644 --- a/plugin/src/withAirshipIOS.ts +++ b/plugin/src/withAirshipIOS.ts @@ -9,13 +9,16 @@ import { import { readFile, writeFileSync, existsSync, mkdirSync } from 'fs'; import { basename, join } from 'path'; +import assert from 'assert'; + +import { ExpoConfig } from '@expo/config-types'; import { AirshipIOSPluginProps } from './withAirship'; import { mergeContents, MergeResults } from '@expo/config-plugins/build/utils/generateCode'; -const NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME = "AirshipNotificationServiceExtension"; +const DEFAULT_NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME = "NotificationServiceExtension"; const NOTIFICATION_SERVICE_FILE_NAME = "AirshipNotificationService.swift"; -const NOTIFICATION_SERVICE_INFO_PLIST_FILE_NAME = "AirshipNotificationServiceExtension-Info.plist"; +const NOTIFICATION_SERVICE_INFO_PLIST_FILE_NAME = "NotificationServiceExtension-Info.plist"; const withCapabilities: ConfigPlugin = (config, props) => { return withInfoPlist(config, (plist) => { @@ -54,17 +57,18 @@ async function writeNotificationServiceFilesAsync(props: AirshipIOSPluginProps, const pluginDir = require.resolve("airship-expo-plugin/package.json"); const sourceDir = join(pluginDir, "../plugin/NotificationServiceExtension/"); - - const extensionPath = join(projectRoot, "ios", NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME); + const targetName = props.notificationServiceTargetName ?? DEFAULT_NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME + const extensionPath = join(projectRoot, "ios", targetName); if (!existsSync(extensionPath)) { mkdirSync(extensionPath, { recursive: true }); } // Copy the NotificationService.swift file into the iOS expo project as AirshipNotificationService.swift. - readFile(props.notificationService, 'utf8', (err, data) => { + var notificationServiceFile = props.notificationService == "DEFAULT_AIRSHIP_SERVICE_EXTENSION" ? join(sourceDir, NOTIFICATION_SERVICE_FILE_NAME) : props.notificationService; + readFile(notificationServiceFile, 'utf8', (err, data) => { if (err || !data) { - console.error("Airship couldn't read file " + props.notificationService); + console.error("Airship couldn't read file " + notificationServiceFile); console.error(err); return; } @@ -78,31 +82,34 @@ async function writeNotificationServiceFilesAsync(props: AirshipIOSPluginProps, writeFileSync(join(extensionPath, NOTIFICATION_SERVICE_FILE_NAME), data); }); - // Copy the Info.plist (default to AirshipNotificationServiceExtension-Info.plist if null) file into the iOS expo project as AirshipNotificationServiceExtension-Info.plist. + // Copy the Info.plist (default to NotificationServiceExtension-Info.plist if null) file into the iOS expo project. readFile(props.notificationServiceInfo ?? join(sourceDir, NOTIFICATION_SERVICE_INFO_PLIST_FILE_NAME), 'utf8', (err, data) => { if (err || !data) { console.error("Airship couldn't read file " + (props.notificationServiceInfo ?? join(sourceDir, NOTIFICATION_SERVICE_INFO_PLIST_FILE_NAME))); console.error(err); return; } - writeFileSync(join(extensionPath, NOTIFICATION_SERVICE_INFO_PLIST_FILE_NAME), data); + const infoPlistFilename = props.notificationServiceTargetName ? props.notificationServiceTargetName + "-Info.plist" : NOTIFICATION_SERVICE_INFO_PLIST_FILE_NAME; + writeFileSync(join(extensionPath, infoPlistFilename), data); }); }; const withExtensionTargetInXcodeProject: ConfigPlugin = (config, props) => { + const targetName = props.notificationServiceTargetName ?? DEFAULT_NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME + return withXcodeProject(config, newConfig => { const xcodeProject = newConfig.modResults; - - if (!!xcodeProject.pbxTargetByName(NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME)) { - console.log(NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME + " already exists in project. Skipping..."); + if (!!xcodeProject.pbxTargetByName(targetName)) { + console.log(targetName + " already exists in project. Skipping..."); return newConfig; } + const infoPlistFilename = props.notificationServiceTargetName ? props.notificationServiceTargetName + "-Info.plist" : NOTIFICATION_SERVICE_INFO_PLIST_FILE_NAME; // Create new PBXGroup for the extension const extGroup = xcodeProject.addPbxGroup( - [NOTIFICATION_SERVICE_FILE_NAME, NOTIFICATION_SERVICE_INFO_PLIST_FILE_NAME], - NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME, - NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME + [NOTIFICATION_SERVICE_FILE_NAME, infoPlistFilename], + targetName, + targetName ); // Add the new PBXGroup to the top level group. This makes the @@ -125,10 +132,10 @@ const withExtensionTargetInXcodeProject: ConfigPlugin = ( // Add the Notification Service Extension Target // This adds PBXTargetDependency and PBXContainerItemProxy const notificationServiceExtensionTarget = xcodeProject.addTarget( - NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME, + targetName, "app_extension", - NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME, - `${config.ios?.bundleIdentifier}.${NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME}` + targetName, + `${config.ios?.bundleIdentifier}.${targetName}` ); // Add build phases to the new Target @@ -155,22 +162,29 @@ const withExtensionTargetInXcodeProject: ConfigPlugin = ( const configurations = xcodeProject.pbxXCBuildConfigurationSection(); for (const key in configurations) { if (typeof configurations[key].buildSettings !== "undefined" - && configurations[key].buildSettings.PRODUCT_NAME == `"${NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME}"` + && configurations[key].buildSettings.PRODUCT_NAME == `"${targetName}"` ) { const buildSettingsObj = configurations[key].buildSettings; buildSettingsObj.IPHONEOS_DEPLOYMENT_TARGET = "14.0"; buildSettingsObj.SWIFT_VERSION = "5.0"; + buildSettingsObj.DEVELOPMENT_TEAM = props?.developmentTeamID; + buildSettingsObj.CODE_SIGN_STYLE = "Automatic"; } } + // Add development teams to the target + xcodeProject.addTargetAttribute("DevelopmentTeam", props?.developmentTeamID, notificationServiceExtensionTarget); + return newConfig; }); }; const withAirshipServiceExtensionPod: ConfigPlugin = (config, props) => { + const targetName = props.notificationServiceTargetName ?? DEFAULT_NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME + return withPodfile(config, async (config) => { const airshipServiceExtensionPodfileSnippet = ` - target '${NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME}' do + target '${targetName}' do use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] pod 'AirshipServiceExtension' @@ -204,6 +218,38 @@ const withAirshipServiceExtensionPod: ConfigPlugin = (con }); }; +const withEasManagedCredentials: ConfigPlugin = (config, props) => { + assert(config.ios?.bundleIdentifier, "Missing 'ios.bundleIdentifier' in app config.") + config.extra = getEasManagedCredentialsConfigExtra(config as ExpoConfig, props); + return config; +} + +function getEasManagedCredentialsConfigExtra(config: ExpoConfig, props: AirshipIOSPluginProps): {[k: string]: any} { + return { + ...config.extra, + eas: { + ...config.extra?.eas, + build: { + ...config.extra?.eas?.build, + experimental: { + ...config.extra?.eas?.build?.experimental, + ios: { + ...config.extra?.eas?.build?.experimental?.ios, + appExtensions: [ + ...(config.extra?.eas?.build?.experimental?.ios?.appExtensions ?? []), + { + // Sync up with the new target + targetName: props.notificationServiceTargetName ?? DEFAULT_NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME, + bundleIdentifier: `${config?.ios?.bundleIdentifier}.${props.notificationServiceTargetName ?? DEFAULT_NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME}`, + } + ] + } + } + } + } + } +} + export const withAirshipIOS: ConfigPlugin = (config, props) => { config = withCapabilities(config, props); config = withAPNSEnvironment(config, props); @@ -211,6 +257,7 @@ export const withAirshipIOS: ConfigPlugin = (config, prop config = withNotificationServiceExtension(config, props); config = withExtensionTargetInXcodeProject(config, props); config = withAirshipServiceExtensionPod(config, props); + config = withEasManagedCredentials(config, props); } return config; }; \ No newline at end of file