Skip to content

Commit

Permalink
Expo Plugin for adding Notification Extension Target. Still very draft.
Browse files Browse the repository at this point in the history
  • Loading branch information
larcho committed Nov 13, 2024
1 parent 8dc744c commit 99dd9af
Show file tree
Hide file tree
Showing 8 changed files with 4,725 additions and 74 deletions.
8 changes: 6 additions & 2 deletions packages/react-native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
"release": "release-it",
"example": "yarn --cwd example",
"pods": "cd example/ios && pod install",
"bootstrap": "rm -rf node_modules && rm -rf example/node_modules && yarn && yarn example && yarn pods"
"bootstrap": "rm -rf node_modules && rm -rf example/node_modules && yarn && yarn example && yarn pods",
"plugin:build": "expo-module build",
"plugin:clean": "expo-module clean"
},
"main": "lib/commonjs/index",
"module": "lib/module/index",
Expand All @@ -58,6 +60,8 @@
"eslint": "^7.32.0",
"eslint-config-prettier": "^7.0.0",
"eslint-plugin-prettier": "^3.1.3",
"expo": "^52.0.4",
"expo-module-scripts": "^4.0.2",
"jest": "^26.6.3",
"pod-install": "^0.1.0",
"prettier": "^2.0.5",
Expand Down Expand Up @@ -119,4 +123,4 @@
]
]
}
}
}
205 changes: 205 additions & 0 deletions packages/react-native/plugin/src/addExtension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import * as path from "path";
import type { XcodeProject } from "expo/config-plugins";

const isaXCBuildConfiguration = "XCBuildConfiguration";
const pbxTargetDependency = "PBXTargetDependency";
const pbxContainerItemProxy = "PBXContainerItemProxy";

type AddExtensionProps = {
project: XcodeProject;
targetName: string;
bundleIdentifier: string;
};

export function addExtension({
project,
targetName,
bundleIdentifier,
}: AddExtensionProps) {
// Check if target already exists, if so, cancel

const existing = project.hash.project.objects[isaXCBuildConfiguration];

for (const [, config] of Object.entries(existing)) {
if (typeof config === "string") continue;

if (
(config as any).buildSettings.PRODUCT_BUNDLE_IDENTIFIER &&
(config as any).buildSettings.PRODUCT_BUNDLE_IDENTIFIER ===
bundleIdentifier
) {
return false;
}
}

const targetUuid = project.generateUuid();

const PRODUCT_BUNDLE_IDENTIFIER = bundleIdentifier;
const INFOPLIST_FILE = `${targetName}/Info.plist`;
const CODE_SIGN_ENTITLEMENTS = `${targetName}/${targetName}.entitlements`;

// Create Build Configurations

const commonBuildSettings = {
CODE_SIGN_ENTITLEMENTS,
CODE_SIGN_STYLE: "Automatic",
DEBUG_INFORMATION_FORMAT: "dwarf",
GCC_C_LANGUAGE_STANDARD: "gnu11",
INFOPLIST_FILE,
PRODUCT_BUNDLE_IDENTIFIER,
PRODUCT_NAME: `"$(TARGET_NAME)"`,
SKIP_INSTALL: "YES",
SWIFT_VERSION: "5.0", // TODO: Use main target version
TARGETED_DEVICE_FAMILY: `"1,2"`,
};

const buildConfigurationsList = [
{
name: "Debug",
isa: isaXCBuildConfiguration,
buildSettings: {
...commonBuildSettings,
MTL_ENABLE_DEBUG_INFO: "INCLUDE_SOURCE",
},
},
{
name: "Release",
isa: isaXCBuildConfiguration,
buildSettings: {
...commonBuildSettings,
COPY_PHASE_STRIP: "NO",
},
},
];

const buildConfigurations = project.addXCConfigurationList(
buildConfigurationsList,
"Release",
`Build configuration list for PBXNativeTarget "${targetName}"`,
);

// Create Product

const productName = targetName;
const productType = "com.apple.product-type.app-extension";
const productFileType = '"wrapper.app-extension"';
const productFile = project.addProductFile(productName, {
group: "Embed Foundation Extensions",
target: targetUuid,
explicitFileType: productFileType,
});

productFile.settings = productFile.settings || {};
productFile.settings.ATTRIBUTES = ["RemoveHeadersOnCopy"];

project.addToPbxBuildFileSection(productFile);

const strippedTargetName = path.basename(targetName, ".appex").trim();
const quotedTargetName = `"${strippedTargetName}"`;

// Create Target
const target = {
uuid: targetUuid,
pbxNativeTarget: {
isa: "PBXNativeTarget",
name: strippedTargetName,
productName: quotedTargetName,
productReference: productFile.fileRef,
productType: `"${productType}"`,
buildConfigurationList: buildConfigurations.uuid,
buildPhases: [],
buildRules: [],
dependencies: [],
},
};

project.addToPbxNativeTargetSection(target);

// Add Extension files
const extensionGroup = project.addPbxGroup(
[],
targetName,
targetName,
'"<group>"',
);
const notificationServiceSwiftFile = project.addFile(
"BreezNotificationService.swift",
extensionGroup.uuid,
{
lastKnownFileType: "sourcecode.swift",
defaultEncoding: 4,
sourceTree: '"<group>"',
},
);

notificationServiceSwiftFile.target = targetUuid;
notificationServiceSwiftFile.uuid = project.generateUuid();

project.addToPbxBuildFileSection(notificationServiceSwiftFile);
const extensionSourcesBuildPhase = project.addBuildPhase(
[],
"PBXSourcesBuildPhase",
"Sources",
targetUuid,
);
extensionSourcesBuildPhase.buildPhase.files.push(
notificationServiceSwiftFile.uuid,
);

const mainGroupUUID = project.getFirstProject().firstProject.mainGroup;
const mainGroup = project.getPBXGroupByKey(mainGroupUUID);
mainGroup.children.push({ value: extensionGroup.uuid, comment: targetName });

// Create Build Phases
const extensionCopyBuildPhase = project.addBuildPhase(
[],
"PBXCopyFilesBuildPhase",
"Embed Foundation Extension",
project.getFirstTarget().uuid,
// targetType,
"app_extension",
);

extensionCopyBuildPhase.buildPhase.dstSubfolderSpec = 13;

addToPbxCopyfilesBuildPhase(
project,
productFile,
"Embed Foundation Extension",
);
project.addBuildPhase([], "PBXResourcesBuildPhase", targetName, targetUuid);
project.addToPbxProjectSection(target);

if (!project.hash.project.objects[pbxTargetDependency]) {
project.hash.project.objects[pbxTargetDependency] = {};
}
if (!project.hash.project.objects[pbxContainerItemProxy]) {
project.hash.project.objects[pbxContainerItemProxy] = {};
}

project.addTargetDependency(project.getFirstTarget().uuid, [target.uuid]);

return target;
}

function pbxBuildPhaseObj(file: any) {
const obj = Object.create(null);

obj.value = file.uuid;
obj.comment = `${file.basename} in ${file.group}`;

return obj;
}

function addToPbxCopyfilesBuildPhase(
project: XcodeProject,
file: any,
name: string,
) {
const sources = project.buildPhaseObject(
"PBXCopyFilesBuildPhase",
name || "Copy Files",
file.target,
);
sources.files.push(pbxBuildPhaseObj(file));
}
32 changes: 32 additions & 0 deletions packages/react-native/plugin/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { ExpoConfig } from 'expo/config';
import { createRunOncePlugin } from 'expo/config-plugins';
import { warnOnce } from './utils';
import { withNotificationServiceExtension } from './withBreezIOS';
const pkg = require("@breeztech/react-native-breez-sdk-liquid");

type PluginProps = {
apiKey: string;
keyService?: string;
mnemonicKeyName?: string;
}

function withBreezPlugin(config: ExpoConfig, props?: PluginProps): ExpoConfig {
const apiKey = props?.apiKey

if(apiKey === undefined) {
warnOnce("API Key not set.");
return config;
}

const keyService = props?.keyService ?? "app:no-auth"; // This is the default name in expo-secure-store
const mnemonicKeyName = props?.keyService ?? "mnemonic";

// iOS Configuration
config = withNotificationServiceExtension(config, { apiKey, keyService, mnemonicKeyName});

// TODO: Android Configuration

return config;
}

export default createRunOncePlugin(withBreezPlugin, pkg.name, pkg.version)
21 changes: 21 additions & 0 deletions packages/react-native/plugin/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const warningMap = new Map<string, boolean>();

export function logPrefix(): string {
return `› ${bold('[@breeztech/react-native/expo]')}`;
}

export function warnOnce(message: string): void {
if (!warningMap.has(message)) {
warningMap.set(message, true);
// eslint-disable-next-line no-console
console.warn(yellow(`${logPrefix()} ${message}`));
}
}

export function yellow(message: string): string {
return `\x1b[33m${message}\x1b[0m`;
}

export function bold(message: string): string {
return `\x1b[1m${message}\x1b[22m`;
}
Loading

0 comments on commit 99dd9af

Please sign in to comment.