catalyst-core-internal 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/catalyst.js +1 -8
- package/changelog.md +21 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/BridgeMessageValidator.kt +1 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +1 -12
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +3 -18
- package/dist/native/buildAppAndroid.js +2 -2
- package/dist/native/buildAppIos.js +17 -10
- package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +2 -13
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +0 -6
- package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.pbxproj +0 -4
- package/mcp_v2/conversion-tasks.json +326 -0
- package/mcp_v2/knowledge-base.json +1068 -0
- package/mcp_v2/lib/helpers.js +170 -0
- package/mcp_v2/mcp.js +563 -0
- package/mcp_v2/package.json +13 -0
- package/mcp_v2/schema.sql +88 -0
- package/mcp_v2/setup.js +282 -0
- package/mcp_v2/tools/build.js +686 -0
- package/mcp_v2/tools/config.js +453 -0
- package/mcp_v2/tools/conversion.js +799 -0
- package/mcp_v2/tools/debug.js +113 -0
- package/mcp_v2/tools/knowledge.js +219 -0
- package/mcp_v2/tools/sync.js +23 -0
- package/mcp_v2/tools/tasks.js +945 -0
- package/package.json +7 -15
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/CatalystPlugin.kt +0 -7
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/GeneratedPluginIndex.kt +0 -6
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/PluginBridge.kt +0 -253
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/plugins/PluginBridgeTest.kt +0 -139
- package/dist/native/internal-plugins/device-info-plugin/android/DeviceInfoPlugin.kt +0 -43
- package/dist/native/internal-plugins/device-info-plugin/ios/DeviceInfoPlugin.swift +0 -28
- package/dist/native/internal-plugins/device-info-plugin/manifest.json +0 -19
- package/dist/native/internalPluginUtils.js +0 -1
- package/dist/native/iosnativeWebView/Sources/Core/Plugins/CatalystPlugin.swift +0 -5
- package/dist/native/iosnativeWebView/Sources/Core/Plugins/GeneratedPluginIndex.swift +0 -6
- package/dist/native/iosnativeWebView/Sources/Core/Plugins/PluginBridge.swift +0 -364
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WeakScriptMessageHandler.swift +0 -14
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/PluginBridgeTests.swift +0 -160
- package/dist/native/plugin-bridge/PluginBridge.js +0 -1
- package/dist/native/pluginComposerAndroid.js +0 -9
- package/dist/native/pluginComposerIos.js +0 -7
- package/dist/scripts/plugins.js +0 -1
- package/license +0 -10
package/bin/catalyst.js
CHANGED
|
@@ -13,8 +13,6 @@ const validCommands = [
|
|
|
13
13
|
"serve",
|
|
14
14
|
"devBuild",
|
|
15
15
|
"devServe",
|
|
16
|
-
"plugin",
|
|
17
|
-
"plugins",
|
|
18
16
|
"buildApp",
|
|
19
17
|
"buildApp:ios",
|
|
20
18
|
"buildApp:android",
|
|
@@ -69,14 +67,11 @@ const scriptIndex = args.findIndex(
|
|
|
69
67
|
x === "serve" ||
|
|
70
68
|
x === "devBuild" ||
|
|
71
69
|
x === "devServe" ||
|
|
72
|
-
x === "plugin" ||
|
|
73
|
-
x === "plugins" ||
|
|
74
70
|
isPlatformCommand(x, "buildApp") ||
|
|
75
71
|
isPlatformCommand(x, "setupEmulator")
|
|
76
72
|
)
|
|
77
73
|
const script = scriptIndex === -1 ? args[0] : args[scriptIndex]
|
|
78
74
|
const nodeArgs = scriptIndex > 0 ? args.slice(0, scriptIndex) : []
|
|
79
|
-
const resolvedScript = script === "plugin" ? "plugins" : script
|
|
80
75
|
|
|
81
76
|
if (validCommands.includes(script)) {
|
|
82
77
|
// Handle platform-specific or combined commands
|
|
@@ -95,9 +90,7 @@ if (validCommands.includes(script)) {
|
|
|
95
90
|
// Original commands
|
|
96
91
|
const result = spawnSync(
|
|
97
92
|
process.execPath,
|
|
98
|
-
nodeArgs
|
|
99
|
-
.concat(require.resolve("../dist/scripts/" + resolvedScript))
|
|
100
|
-
.concat(args.slice(scriptIndex + 1)),
|
|
93
|
+
nodeArgs.concat(require.resolve("../dist/scripts/" + script)).concat(args.slice(scriptIndex + 1)),
|
|
101
94
|
{ stdio: "inherit" }
|
|
102
95
|
)
|
|
103
96
|
handleProcessResult(result)
|
package/changelog.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.0-beta.1] - 2026-04-15
|
|
4
|
+
|
|
5
|
+
- Promoted `0.1.0-canary.7` to the first proper beta release after stabilization.
|
|
6
|
+
- No code changes from `0.1.0-canary.7`; this release marks the same build as production-ready beta.
|
|
7
|
+
|
|
8
|
+
## [0.1.0-canary.7] - 2026-03-31
|
|
9
|
+
|
|
10
|
+
- Introduced Catalyst MCP v2 with a new setup flow, database schema, knowledge base, and source-aware migration tooling for stronger project guidance and conversion workflows.
|
|
11
|
+
- Expanded MCP/framework knowledge coverage across SEO, observability, webpack, React Compiler, CLI, file conventions, and native hooks, while improving setup and verification messaging across supported MCP clients.
|
|
12
|
+
|
|
13
|
+
## [0.1.0-canary.6] - 2026-03-09
|
|
14
|
+
|
|
15
|
+
- Hardened URL whitelisting with thread-safety improvements, broader test coverage, and related iOS build fixes to make access-control behavior more reliable.
|
|
16
|
+
- Improved compatibility and runtime resilience by softening bridge environment mismatch failures and preserving backward compatibility for `useDataProtection` on older native binaries.
|
|
17
|
+
|
|
18
|
+
## [0.1.0-canary.5] - 2026-02-27
|
|
19
|
+
|
|
20
|
+
- Strengthened native app security with backup restrictions, screen-capture protection, web data clearing, and related Android/iOS test coverage.
|
|
21
|
+
- Improved universal app runtime behavior with safe-area inset support, edge-to-edge rendering, and notification permission override fixes.
|
|
22
|
+
- Expanded platform support with offline fallback handling, notification/access-control refinements, localhost HTTP allowances for local development, and file-picker/HTTPS server improvements.
|
|
23
|
+
|
|
3
24
|
## [0.1.0-canary.4] - 2026-02-12
|
|
4
25
|
|
|
5
26
|
- Added Google Sign-In support for both Android and iOS in Catalyst, enabling a unified native authentication experience for apps built on the framework.
|
|
@@ -60,7 +60,7 @@ data class SchemaDefinition(
|
|
|
60
60
|
object BridgeMessageValidator {
|
|
61
61
|
|
|
62
62
|
private const val TAG = "BridgeMessageValidator"
|
|
63
|
-
private const val MAX_MESSAGE_SIZE =
|
|
63
|
+
private const val MAX_MESSAGE_SIZE = 10 * 1024 * 1024 // 10MB (match iOS)
|
|
64
64
|
|
|
65
65
|
// Valid commands — single source of truth from CatalystConstants
|
|
66
66
|
private val validCommands = CatalystConstants.Bridge.VALID_COMMANDS
|
package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt
CHANGED
|
@@ -233,7 +233,7 @@ class CustomWebView(
|
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
// Additional security check: only allow whitelisted interface names
|
|
236
|
-
val allowedInterfaces = setOf("NativeBridge", "AndroidBridge"
|
|
236
|
+
val allowedInterfaces = setOf("NativeBridge", "AndroidBridge")
|
|
237
237
|
if (name !in allowedInterfaces) {
|
|
238
238
|
Log.e(TAG, "❌ Security: Interface name '$name' is not in whitelist. Refusing to add interface.")
|
|
239
239
|
return
|
|
@@ -246,17 +246,6 @@ class CustomWebView(
|
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
-
fun removeJavascriptInterface(name: String) {
|
|
250
|
-
try {
|
|
251
|
-
webView.removeJavascriptInterface(name)
|
|
252
|
-
if (BuildConfig.DEBUG) {
|
|
253
|
-
Log.d(TAG, "🔌 Removed JavaScript interface: $name")
|
|
254
|
-
}
|
|
255
|
-
} catch (e: Exception) {
|
|
256
|
-
Log.w(TAG, "⚠️ Failed to remove JavaScript interface '$name': ${e.message}")
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
249
|
fun destroy() {
|
|
261
250
|
job.cancel()
|
|
262
251
|
webView.destroy()
|
package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt
CHANGED
|
@@ -14,7 +14,6 @@ import org.json.JSONObject
|
|
|
14
14
|
import java.util.Properties
|
|
15
15
|
import io.yourname.androidproject.databinding.ActivityMainBinding
|
|
16
16
|
import io.yourname.androidproject.NativeBridge
|
|
17
|
-
import io.yourname.androidproject.plugins.PluginBridge
|
|
18
17
|
import io.yourname.androidproject.utils.BridgeUtils
|
|
19
18
|
import io.yourname.androidproject.utils.KeyboardUtil
|
|
20
19
|
import io.yourname.androidproject.utils.NetworkUtils
|
|
@@ -40,7 +39,6 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
40
39
|
|
|
41
40
|
private lateinit var binding: ActivityMainBinding
|
|
42
41
|
private lateinit var nativeBridge: NativeBridge
|
|
43
|
-
private lateinit var pluginBridge: PluginBridge
|
|
44
42
|
private lateinit var customWebView: CustomWebView
|
|
45
43
|
lateinit var properties: Properties
|
|
46
44
|
private lateinit var metricsMonitor: MetricsMonitor
|
|
@@ -330,14 +328,6 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
330
328
|
Log.e(TAG, "Failed to initialize NativeBridge: ${e.message}")
|
|
331
329
|
}
|
|
332
330
|
|
|
333
|
-
// Setup isolated PluginBridge
|
|
334
|
-
try {
|
|
335
|
-
pluginBridge = PluginBridge(this, customWebView.getWebView(), properties)
|
|
336
|
-
customWebView.addJavascriptInterface(pluginBridge, "PluginBridge")
|
|
337
|
-
} catch (e: Exception) {
|
|
338
|
-
Log.e(TAG, "Failed to initialize PluginBridge: ${e.message}")
|
|
339
|
-
}
|
|
340
|
-
|
|
341
331
|
setupSafeAreaHandling()
|
|
342
332
|
|
|
343
333
|
// Setup back press handler (modern API)
|
|
@@ -455,16 +445,11 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
455
445
|
if (::keyboardUtil.isInitialized) {
|
|
456
446
|
keyboardUtil.cleanup()
|
|
457
447
|
}
|
|
458
|
-
if (::
|
|
459
|
-
|
|
460
|
-
customWebView.removeJavascriptInterface("PluginBridge")
|
|
461
|
-
}
|
|
462
|
-
if (::nativeBridge.isInitialized) {
|
|
463
|
-
customWebView.removeJavascriptInterface("NativeBridge")
|
|
464
|
-
}
|
|
465
|
-
customWebView.destroy()
|
|
448
|
+
if (::nativeBridge.isInitialized) {
|
|
449
|
+
nativeBridge.cleanup()
|
|
466
450
|
}
|
|
467
451
|
coroutineContext.cancelChildren()
|
|
452
|
+
customWebView.destroy()
|
|
468
453
|
super.onDestroy()
|
|
469
454
|
}
|
|
470
455
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* eslint-disable no-extra-semi */"use strict";var _renameAndroidProject=require("./renameAndroidProject.js");var _child_process=require("child_process");var _fs=_interopRequireDefault(require("fs"));var _path=_interopRequireDefault(require("path"));var _utils=require("./utils.js");var _TerminalProgress=_interopRequireDefault(require("./TerminalProgress.js"))
|
|
1
|
+
/* eslint-disable no-extra-semi */"use strict";var _renameAndroidProject=require("./renameAndroidProject.js");var _child_process=require("child_process");var _fs=_interopRequireDefault(require("fs"));var _path=_interopRequireDefault(require("path"));var _utils=require("./utils.js");var _TerminalProgress=_interopRequireDefault(require("./TerminalProgress.js"));// Import the AAB builder
|
|
2
2
|
function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}const configPath=`${process.env.PWD}/config/config.json`;const publicPath=`${process.env.PWD}/public`;const catalystCorePath=_path.default.dirname(require.resolve("catalyst-core-internal/package.json"));const pwd=_path.default.join(catalystCorePath,"dist/native");const ANDROID_PACKAGE="io.yourname.androidproject";// Default values for AAB building
|
|
3
3
|
const DEFAULT_PROJECT_PATH=`${pwd}/androidProject`;const DEFAULT_DEPLOYMENT_PATH="./deployment";const DEFAULT_OLD_PROJECT_NAME="androidProject";// Use actual project name in catalyst
|
|
4
4
|
const DEFAULT_OVERWRITE_EXISTING=true;const steps={config:"Initialize Configuration",tools:"Validate Android Tools",emulator:"Check and Start Emulator",copyAssets:"Copy Build Assets",build:"Build and Install Application",aab:"Build Signed AAB"};const progressConfig={titlePaddingTop:2,titlePaddingBottom:1,stepPaddingLeft:4,stepSpacing:1,errorPaddingLeft:6,bottomMargin:2};const progress=new _TerminalProgress.default(steps,"Catalyst Android Build",progressConfig);async function initializeConfig(){const configFile=_fs.default.readFileSync(configPath,"utf8");const config=JSON.parse(configFile);const{WEBVIEW_CONFIG,BUILD_OUTPUT_PATH}=config;if(!WEBVIEW_CONFIG||Object.keys(WEBVIEW_CONFIG).length===0){throw new Error("WebView Config missing in "+configPath);}if(!WEBVIEW_CONFIG.android){throw new Error("Android config missing in WebView Config");}// Log build type information
|
|
@@ -131,7 +131,7 @@ const canInstallOnPhysical=await testPhysicalDeviceInstallation(ADB_PATH,physica
|
|
|
131
131
|
targetDevice={type:"physical",id:physicalDevice.id,model:physicalDevice.model};progress.log(`Using physical device: ${physicalDevice.model}`,"success");}else{// Physical device failed, fallback to emulator
|
|
132
132
|
progress.log("Physical device installation test failed, falling back to emulator","warning");targetDevice=await handleEmulatorSetup(ADB_PATH,EMULATOR_PATH,androidConfig);}}else{// No physical device, use emulator (current behavior)
|
|
133
133
|
targetDevice=await handleEmulatorSetup(ADB_PATH,EMULATOR_PATH,androidConfig);}progress.complete("emulator");}else{progress.log("Skipping device setup for release build","info");}// Copy build assets
|
|
134
|
-
progress.start("copyAssets");await copyBuildAssets(androidConfig,buildOptimisation);await copySplashscreenAssets();await copyOfflinePage();await copyIconAssets();await configureAppName(androidConfig);
|
|
134
|
+
progress.start("copyAssets");await copyBuildAssets(androidConfig,buildOptimisation);await copySplashscreenAssets();await copyOfflinePage();await copyIconAssets();await configureAppName(androidConfig);await processNotifications(WEBVIEW_CONFIG);progress.log(`Build optimization: ${buildOptimisation?"Enabled":"Disabled"}`,"info");progress.complete("copyAssets");// Build based on type
|
|
135
135
|
let movedApkPath=null;if(buildType==="release"){// Build signed AAB for release
|
|
136
136
|
progress.start("aab");await buildSignedAAB(androidConfig);progress.complete("aab");// Move APK to output directory
|
|
137
137
|
movedApkPath=await moveApkToOutputPath(buildType,BUILD_OUTPUT_PATH,androidConfig.appName);}else{// Install debug app for development
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
"use strict";const{exec,execSync
|
|
2
|
-
const iosConfig=WEBVIEW_CONFIG.ios;const isGoogleSignInEnabled=WEBVIEW_CONFIG.googleSignIn?.enabled??false;const protocol=WEBVIEW_CONFIG.useHttps?"https":"http";const ip=WEBVIEW_CONFIG.LOCAL_IP||"localhost";const port=WEBVIEW_CONFIG.port?WEBVIEW_CONFIG.useHttps?443:WEBVIEW_CONFIG.port:null;let url=port?`${protocol}://${ip}:${port}`:`${protocol}://${ip}`;const PUBLIC_PATH=`${process.env.PWD}/public`;const PROJECT_DIR=`${pwd}/iosnativeWebView`;const SCHEME_NAME="iosnativeWebView";const APP_BUNDLE_ID=iosConfig.appBundleId||"com.debug.webview";const PROJECT_NAME=path.basename(PROJECT_DIR);const IPHONE_MODEL=iosConfig.simulatorName;const GOOGLE_SERVICES_FILENAME="GoogleService-Info.plist";
|
|
1
|
+
"use strict";const{exec,execSync}=require("child_process");const fs=require("fs");const path=require("path");const TerminalProgress=require("./TerminalProgress.js").default;const crypto=require("crypto");const catalystCorePath=path.dirname(require.resolve("catalyst-core-internal/package.json"));const pwd=path.join(catalystCorePath,"dist/native");const{WEBVIEW_CONFIG,BUILD_OUTPUT_PATH}=require(`${process.env.PWD}/config/config.json`);// Configuration constants
|
|
2
|
+
const iosConfig=WEBVIEW_CONFIG.ios;const isGoogleSignInEnabled=WEBVIEW_CONFIG.googleSignIn?.enabled??false;const protocol=WEBVIEW_CONFIG.useHttps?"https":"http";const ip=WEBVIEW_CONFIG.LOCAL_IP||"localhost";const port=WEBVIEW_CONFIG.port?WEBVIEW_CONFIG.useHttps?443:WEBVIEW_CONFIG.port:null;let url=port?`${protocol}://${ip}:${port}`:`${protocol}://${ip}`;const PUBLIC_PATH=`${process.env.PWD}/public`;const PROJECT_DIR=`${pwd}/iosnativeWebView`;const SCHEME_NAME="iosnativeWebView";const APP_BUNDLE_ID=iosConfig.appBundleId||"com.debug.webview";const PROJECT_NAME=path.basename(PROJECT_DIR);const IPHONE_MODEL=iosConfig.simulatorName;const GOOGLE_SERVICES_FILENAME="GoogleService-Info.plist";function generateProjectObjectId(identifier,suffix=""){return crypto.createHash("md5").update(`${identifier}:${suffix}`).digest("hex").substring(0,24).toUpperCase();}function getXcodeProjectFilePath(){return path.join(PROJECT_DIR,`${PROJECT_NAME}.xcodeproj`,"project.pbxproj");}// Define build steps for progress tracking
|
|
3
3
|
const steps={config:"Generating Required Configuration for build",deviceDetection:"Detecting Physical Device",launchSimulator:"Launch iOS Simulator",clean:"Clean Build Artifacts",assets:"Process Notification Assets",build:"Build IOS Project",findApp:"Locate Built Application",install:"Install Application",launch:"Launch Application"};// Configure progress display
|
|
4
4
|
const progressConfig={titlePaddingTop:2,titlePaddingBottom:1,stepPaddingLeft:4,stepSpacing:1,errorPaddingLeft:6,bottomMargin:2};const progress=new TerminalProgress(steps,"Catalyst iOS Build",progressConfig);// Utility function to run shell commands
|
|
5
5
|
function runCommand(command,options={}){return new Promise((resolve,reject)=>{// eslint-disable-next-line security/detect-child-process
|
|
6
6
|
exec(command,{maxBuffer:1024*1024*10,...options},(error,stdout,stderr)=>{if(error){console.error(`Command failed: ${command}`);console.error(`Error: ${error.message}`);console.error(`stderr: ${stderr}`);reject(error);return;}if(stderr){console.warn(`Warning: ${stderr}`);}resolve(stdout.trim());});});}async function getBootedSimulatorUUID(modelName){try{// First try to find a booted simulator of the specified model
|
|
7
7
|
let command=`xcrun simctl list devices | grep "${modelName}" | grep "Booted" | grep -E -o -i "([0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12})" | head -n 1`;let uuid=execSync(command).toString().trim();if(uuid){console.log(`Found booted simulator of model ${modelName}`);return uuid;}// If no booted simulator of the specified model is found, check any booted simulator
|
|
8
|
-
console.log(`No booted simulator of model ${modelName} found, checking for any booted simulator...`);command=`xcrun simctl list devices | grep "Booted" | grep -E -o -i "([0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12})" | head -n 1`;uuid=execSync(command).toString().trim();if(uuid){console.log("Found another booted simulator, will use it instead");return uuid;}return null;}catch(error){console.log("No booted simulators found");return null;}}async function getBootedSimulatorInfo(){try{const listCommand="xcrun simctl list devices --json";const simulatorList=JSON.parse(execSync(listCommand).toString());for(const runtime in simulatorList.devices){const devices=simulatorList.devices[runtime];for(const device of devices){if(device.state==="Booted"){return{udid:device.udid,name:device.name,runtime:runtime};}}}return null;}catch(error){console.log("Failed to get booted simulator info:",error.message);return null;}}async function generatePackageSwift(
|
|
9
|
-
const configHash=crypto.createHash("md5").update(JSON.stringify({notifications:isNotificationsEnabled,googleSignIn:isGoogleSignInEnabled
|
|
8
|
+
console.log(`No booted simulator of model ${modelName} found, checking for any booted simulator...`);command=`xcrun simctl list devices | grep "Booted" | grep -E -o -i "([0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12})" | head -n 1`;uuid=execSync(command).toString().trim();if(uuid){console.log("Found another booted simulator, will use it instead");return uuid;}return null;}catch(error){console.log("No booted simulators found");return null;}}async function getBootedSimulatorInfo(){try{const listCommand="xcrun simctl list devices --json";const simulatorList=JSON.parse(execSync(listCommand).toString());for(const runtime in simulatorList.devices){const devices=simulatorList.devices[runtime];for(const device of devices){if(device.state==="Booted"){return{udid:device.udid,name:device.name,runtime:runtime};}}}return null;}catch(error){console.log("Failed to get booted simulator info:",error.message);return null;}}async function generatePackageSwift(){try{const isNotificationsEnabled=WEBVIEW_CONFIG.notifications?.enabled??false;progress.log(`🔧 Generating Package.swift (notifications: ${isNotificationsEnabled})`,"info");// Create hash of current config to detect changes
|
|
9
|
+
const configHash=crypto.createHash("md5").update(JSON.stringify({notifications:isNotificationsEnabled,googleSignIn:isGoogleSignInEnabled})).digest("hex");const hashFilePath=path.join(PROJECT_DIR,".package-config-hash");const targetPath=path.join(PROJECT_DIR,"Package.swift");let shouldUpdate=true;// Check if we need to update based on config change
|
|
10
10
|
if(fs.existsSync(hashFilePath)){const previousHash=fs.readFileSync(hashFilePath,"utf8");if(previousHash===configHash){shouldUpdate=false;progress.log("Package.swift already up to date","info");}}// Ensure Package.swift exists even if hash matches
|
|
11
11
|
if(!fs.existsSync(targetPath)){shouldUpdate=true;progress.log("Package.swift missing, will generate it now","info");}if(shouldUpdate){progress.log("Generating Package.swift dynamically","info");// Build the Package.swift content dynamically
|
|
12
12
|
let packageContent=`// swift-tools-version: 5.9
|
|
@@ -23,7 +23,10 @@ if(isNotificationsEnabled){packageContent+=`,
|
|
|
23
23
|
.library(name: "CatalystNotifications", targets: ["CatalystNotifications"])`;}packageContent+=`
|
|
24
24
|
],
|
|
25
25
|
dependencies: [
|
|
26
|
-
|
|
26
|
+
.package(url: "https://github.com/kylef/JSONSchema.swift", from: "0.6.0"),
|
|
27
|
+
.package(url: "https://github.com/google/GoogleSignIn-iOS", from: "7.0.0")`;// Add Firebase dependency only if notifications enabled
|
|
28
|
+
if(isNotificationsEnabled){packageContent+=`,
|
|
29
|
+
.package(url: "https://github.com/firebase/firebase-ios-sdk", from: "12.3.0")`;}packageContent+=`
|
|
27
30
|
],
|
|
28
31
|
targets: [
|
|
29
32
|
// Core functionality (WebView, bridge, utils, constants)
|
|
@@ -31,7 +34,8 @@ ${formatSwiftPackageEntries(packageDependencies).join(",\n")}`;packageContent+=`
|
|
|
31
34
|
.target(
|
|
32
35
|
name: "CatalystCore",
|
|
33
36
|
dependencies: [
|
|
34
|
-
|
|
37
|
+
.product(name: "JSONSchema", package: "JSONSchema.swift"),
|
|
38
|
+
.product(name: "GoogleSignIn", package: "GoogleSignIn-iOS")
|
|
35
39
|
],
|
|
36
40
|
path: "Sources/Core"
|
|
37
41
|
)`;// Add CatalystNotifications target only if enabled
|
|
@@ -41,7 +45,8 @@ if(isNotificationsEnabled){packageContent+=`,
|
|
|
41
45
|
name: "CatalystNotifications",
|
|
42
46
|
dependencies: [
|
|
43
47
|
"CatalystCore",
|
|
44
|
-
|
|
48
|
+
.product(name: "FirebaseCore", package: "firebase-ios-sdk"),
|
|
49
|
+
.product(name: "FirebaseMessaging", package: "firebase-ios-sdk")
|
|
45
50
|
],
|
|
46
51
|
path: "Sources/CatalystNotifications"
|
|
47
52
|
)`;}packageContent+=`
|
|
@@ -60,7 +65,9 @@ projectContent=projectContent.replace(/(C99974342E97D56900C25611 \/\* CatalystCo
|
|
|
60
65
|
projectContent=projectContent.replace(/(C99974342E97D56900C25611 \/\* CatalystCore in Frameworks \*\/,)/,`$1\n\t\t\t\t${NOTIF_BUILD_FILE_ID} /* CatalystNotifications in Frameworks */,`);// 3. Add to packageProductDependencies array
|
|
61
66
|
projectContent=projectContent.replace(/(packageProductDependencies = \(\s*C99974332E97D56900C25611 \/\* CatalystCore \*\/,)/,`$1\n\t\t\t\t${NOTIF_PRODUCT_ID} /* CatalystNotifications */,`);// 4. Add to XCSwiftPackageProductDependency section
|
|
62
67
|
projectContent=projectContent.replace(/(\/\* End XCSwiftPackageProductDependency section \*\/)/,`\t\t${NOTIF_PRODUCT_ID} /* CatalystNotifications */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = C99974322E97D56900C25611 /* XCLocalSwiftPackageReference "." */;\n\t\t\tproductName = CatalystNotifications;\n\t\t};\n$1`);fs.writeFileSync(projectFilePath,projectContent,"utf8");progress.log("✅ CatalystNotifications added to Xcode project","success");}else if(!isNotificationsEnabled&&hasNotifications){progress.log("Removing CatalystNotifications from Xcode project","info");// Remove all CatalystNotifications entries
|
|
63
|
-
projectContent=projectContent.replace(/\t\t[A-F0-9]+ \/\* CatalystNotifications in Frameworks \*\/ = {isa = PBXBuildFile; productRef = [A-F0-9]+ \/\* CatalystNotifications \*\/; };\n/g,"");projectContent=projectContent.replace(/\t\t\t\t[A-F0-9]+ \/\* CatalystNotifications in Frameworks \*\/,\n/g,"");projectContent=projectContent.replace(/\t\t\t\t[A-F0-9]+ \/\* CatalystNotifications \*\/,\n/g,"");projectContent=projectContent.replace(/\t\t[A-F0-9]+ \/\* CatalystNotifications \*\/ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = [A-F0-9]+ \/\* XCLocalSwiftPackageReference "." \*\/;\n\t\t\tproductName = CatalystNotifications;\n\t\t};\n/g,"");fs.writeFileSync(projectFilePath,projectContent,"utf8");progress.log("✅ CatalystNotifications removed from Xcode project","success");}else{progress.log("Package dependencies already correct","info");}}catch(error){progress.log(`❌ Failed to update package dependencies: ${error.message}`,"error");throw error;}}async function updateInfoPlist(
|
|
68
|
+
projectContent=projectContent.replace(/\t\t[A-F0-9]+ \/\* CatalystNotifications in Frameworks \*\/ = {isa = PBXBuildFile; productRef = [A-F0-9]+ \/\* CatalystNotifications \*\/; };\n/g,"");projectContent=projectContent.replace(/\t\t\t\t[A-F0-9]+ \/\* CatalystNotifications in Frameworks \*\/,\n/g,"");projectContent=projectContent.replace(/\t\t\t\t[A-F0-9]+ \/\* CatalystNotifications \*\/,\n/g,"");projectContent=projectContent.replace(/\t\t[A-F0-9]+ \/\* CatalystNotifications \*\/ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = [A-F0-9]+ \/\* XCLocalSwiftPackageReference "." \*\/;\n\t\t\tproductName = CatalystNotifications;\n\t\t};\n/g,"");fs.writeFileSync(projectFilePath,projectContent,"utf8");progress.log("✅ CatalystNotifications removed from Xcode project","success");}else{progress.log("Package dependencies already correct","info");}}catch(error){progress.log(`❌ Failed to update package dependencies: ${error.message}`,"error");throw error;}}async function updateInfoPlist(){try{const infoPlistPath=path.join(PROJECT_DIR,PROJECT_NAME,"Info.plist");const infoReleasePlistPath=path.join(PROJECT_DIR,PROJECT_NAME,"Info-Release.plist");const googleServicesPlistPath=path.join(PROJECT_DIR,PROJECT_NAME,GOOGLE_SERVICES_FILENAME);const googleClientId=WEBVIEW_CONFIG.googleSignIn?.clientId||WEBVIEW_CONFIG.googleSignIn?.webClientId||"";const iosClientId=WEBVIEW_CONFIG.googleSignIn?.iosClientId||"";const googleServicesContent=fs.existsSync(googleServicesPlistPath)?fs.readFileSync(googleServicesPlistPath,"utf8"):null;const reversedClientIdFromServices=googleServicesContent?(googleServicesContent.match(/<key>REVERSED_CLIENT_ID<\/key>\s*<string>([^<]+)<\/string>/)||[])[1]:null;const clientIdFromServices=googleServicesContent?(googleServicesContent.match(/<key>CLIENT_ID<\/key>\s*<string>([^<]+)<\/string>/)||[])[1]:null;const computeReversed=value=>{if(!value)return"";return value.split(".").reverse().join(".");};const resolvedClientIdForScheme=iosClientId||googleClientId||clientIdFromServices||"";const resolvedReversedClientId=reversedClientIdFromServices||computeReversed(resolvedClientIdForScheme);if(isGoogleSignInEnabled&&!resolvedReversedClientId){progress.fail("config","Google Sign-In enabled but no valid clientId found");process.exit(1);}const plistTargets=[infoPlistPath,infoReleasePlistPath];const findMatchingArrayCloseTag=(content,arrayStartIndex)=>{if(arrayStartIndex<0)return-1;const tokenRegex=/<array>|<\/array>/g;tokenRegex.lastIndex=arrayStartIndex;let depth=0;let match;while((match=tokenRegex.exec(content))!==null){if(match[0]==="<array>"){depth+=1;}else{depth-=1;if(depth===0){return match.index;}}}return-1;};const ensureGoogleUrlScheme=plistPath=>{if(!isGoogleSignInEnabled||!resolvedReversedClientId){return;}if(!fs.existsSync(plistPath)){return;}let plistContent=fs.readFileSync(plistPath,"utf8");if(plistContent.includes(resolvedReversedClientId)){return;}const urlTypeEntry=`\n\t<key>CFBundleURLTypes</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>CFBundleURLName</key>\n\t\t\t<string>googleSignIn</string>\n\t\t\t<key>CFBundleURLSchemes</key>\n\t\t\t<array>\n\t\t\t\t<string>${resolvedReversedClientId}</string>\n\t\t\t</array>\n\t\t</dict>\n\t</array>\n`;const urlTypeDictEntry=`\n\t\t<dict>\n\t\t\t<key>CFBundleURLName</key>\n\t\t\t<string>googleSignIn</string>\n\t\t\t<key>CFBundleURLSchemes</key>\n\t\t\t<array>\n\t\t\t\t<string>${resolvedReversedClientId}</string>\n\t\t\t</array>\n\t\t</dict>\n`;if(plistContent.includes("<key>CFBundleURLTypes</key>")){const urlTypesKeyIndex=plistContent.indexOf("<key>CFBundleURLTypes</key>");const urlTypesArrayStart=plistContent.indexOf("<array>",urlTypesKeyIndex);const urlTypesArrayClose=findMatchingArrayCloseTag(plistContent,urlTypesArrayStart);if(urlTypesArrayStart>=0&&urlTypesArrayClose>=0){plistContent=plistContent.slice(0,urlTypesArrayClose)+urlTypeDictEntry+plistContent.slice(urlTypesArrayClose);}else{const insertPoint=plistContent.lastIndexOf("</dict>");plistContent=plistContent.slice(0,insertPoint)+urlTypeEntry+plistContent.slice(insertPoint);progress.log(`CFBundleURLTypes block could not be parsed in ${path.basename(plistPath)}; appended block instead`,"warning");}}else{const insertPoint=plistContent.lastIndexOf("</dict>");plistContent=plistContent.slice(0,insertPoint)+urlTypeEntry+plistContent.slice(insertPoint);}fs.writeFileSync(plistPath,plistContent,"utf8");progress.log(`Added Google Sign-In URL scheme to ${path.basename(plistPath)}`,"info");};const ensureLSApplicationQueriesSchemes=plistPath=>{if(!isGoogleSignInEnabled||!resolvedReversedClientId){return;}if(!fs.existsSync(plistPath)){return;}let plistContent=fs.readFileSync(plistPath,"utf8");const matches=plistContent.match(/<key>LSApplicationQueriesSchemes<\/key>\s*<array>([\s\S]*?)<\/array>/);const existingSchemes=new Set();if(matches&&matches[1]){const schemeRegex=/<string>([^<]+)<\/string>/g;let match;while((match=schemeRegex.exec(matches[1]))!==null){existingSchemes.add(match[1]);}}const targetSchemes=["google",resolvedReversedClientId];targetSchemes.forEach(scheme=>{if(scheme&&!existingSchemes.has(scheme)){existingSchemes.add(scheme);}});const schemesArrayContent=Array.from(existingSchemes).map(s=>`\n\t\t<string>${s}</string>`).join("");const newLSBlock=`\n\t<key>LSApplicationQueriesSchemes</key>\n\t<array>${schemesArrayContent}\n\t</array>\n`;if(matches){plistContent=plistContent.replace(/<key>LSApplicationQueriesSchemes<\/key>\s*<array>[\s\S]*?<\/array>/,newLSBlock.trimEnd());}else{const insertPoint=plistContent.lastIndexOf("</dict>");plistContent=plistContent.slice(0,insertPoint)+newLSBlock+plistContent.slice(insertPoint);}fs.writeFileSync(plistPath,plistContent,"utf8");progress.log(`Ensured LSApplicationQueriesSchemes for Google in ${path.basename(plistPath)}`,"info");};const ensureDisplayName=plistPath=>{if(!fs.existsSync(plistPath))return;let plistContent=fs.readFileSync(plistPath,"utf8");// Add CFBundleDisplayName if it doesn't exist
|
|
69
|
+
if(!plistContent.includes("CFBundleDisplayName")){const insertPoint=plistContent.lastIndexOf("</dict>");const newEntry=`\t<key>CFBundleDisplayName</key>\n\t<string>${iosConfig.appName||"Catalyst Application"}</string>\n`;plistContent=plistContent.slice(0,insertPoint)+newEntry+plistContent.slice(insertPoint);fs.writeFileSync(plistPath,plistContent,"utf8");}else{// Update existing CFBundleDisplayName with new appName
|
|
70
|
+
const displayNameRegex=/(<key>CFBundleDisplayName<\/key>\s*<string>)([^<]*)(<\/string>)/;if(displayNameRegex.test(plistContent)){plistContent=plistContent.replace(displayNameRegex,`$1${iosConfig.appName||"Catalyst Application"}$3`);fs.writeFileSync(plistPath,plistContent,"utf8");}}};plistTargets.forEach(plistPath=>{ensureDisplayName(plistPath);ensureGoogleUrlScheme(plistPath);ensureLSApplicationQueriesSchemes(plistPath);});}catch(err){progress.fail("config",err);process.exit(1);}}// Function to convert JSON value to Swift property
|
|
64
71
|
function generateSwiftProperty(key,value,indent=" "){if(value===null||value===undefined){return`${indent}public static let ${key}: String? = nil`;}// Special handling for cachePattern - always convert to array
|
|
65
72
|
if(key==="cachePattern"){if(typeof value==="string"){// Convert single string to array
|
|
66
73
|
return`${indent}public static let ${key}: [String] = ["${value}"]`;}else if(Array.isArray(value)){const arrayValues=value.map(v=>`"${v}"`).join(", ");return`${indent}public static let ${key}: [String] = [${arrayValues}]`;}}if(typeof value==="string"){return`${indent}public static let ${key} = "${value}"`;}if(typeof value==="number"){return Number.isInteger(value)?`${indent}public static let ${key} = ${value}`:`${indent}public static let ${key} = ${value}`;}if(typeof value==="boolean"){return`${indent}public static let ${key} = ${value}`;}if(Array.isArray(value)){if(value.length===0){return`${indent}public static let ${key}: [String] = []`;}// Determine array type from first element
|
|
@@ -275,7 +282,7 @@ progress.log("Opening Simulator.app...");await runCommand("open -a Simulator");/
|
|
|
275
282
|
await new Promise(resolve=>setTimeout(resolve,1000));// Activate the Simulator.app window to bring it to front
|
|
276
283
|
await runCommand("osascript -e 'tell application \"Simulator\" to activate'");progress.log("iOS Simulator launched successfully.","success");progress.complete("launchSimulator");}catch(error){progress.fail("launchSimulator",error.message);// Show detailed troubleshooting info
|
|
277
284
|
progress.printTreeContent("Simulator Troubleshooting",["iOS Simulator failed to launch. Common solutions:",{text:"Delete and recreate the simulator in Xcode",indent:1,prefix:"├─ ",color:"yellow"},{text:"Reset simulator content: Device > Erase All Content and Settings",indent:1,prefix:"├─ ",color:"yellow"},{text:"Check available simulators: xcrun simctl list devices",indent:1,prefix:"├─ ",color:"yellow"},{text:"Restart Xcode and Simulator app",indent:1,prefix:"└─ ",color:"yellow"},"","Error Details:",{text:`Simulator: ${simulatorName}`,indent:1,prefix:"├─ ",color:"gray"},{text:`Error: ${error.message}`,indent:1,prefix:"└─ ",color:"red"}]);console.error("Failed to launch iOS Simulator. Error:",error.message);process.exit(1);}}// Separate build function with proper device routing
|
|
278
|
-
async function buildForIOS(
|
|
285
|
+
async function buildForIOS(){const originalDir=process.cwd();try{await generatePackageSwift();await updateXcodeProjectPackageDependencies();// Process notification assets
|
|
279
286
|
progress.start("assets");await processNotificationAssets(WEBVIEW_CONFIG);await copyOfflinePage();progress.complete("assets");await generateXCConfig();await copySplashscreenAssets();await copyAppIcon();progress.log("Changing directory to: "+PROJECT_DIR,"info");process.chdir(PROJECT_DIR);// Force physical device detection (bypass shouldUsePhysicalDevice check)
|
|
280
287
|
const physicalDevice=await detectPhysicalDevices();let APP_PATH;let targetInfo;if(physicalDevice){// Physical device workflow
|
|
281
288
|
progress.log("🔥 Building for physical device workflow","success");targetInfo={type:"physical",name:physicalDevice.name,udid:physicalDevice.udid};await cleanBuildArtifacts();progress.start("build");try{await buildProjectForPhysicalDevice(SCHEME_NAME,APP_BUNDLE_ID,path.join(process.env.HOME,"Library/Developer/Xcode/DerivedData"),PROJECT_NAME,physicalDevice);progress.complete("build");}catch(error){progress.fail("build",error.message);progress.printTreeContent("Physical Device Build Failed",["Build failed. Please check:",{text:"Code signing certificates are properly installed",indent:1,prefix:"├─ ",color:"yellow"},{text:"Provisioning profile matches your bundle ID",indent:1,prefix:"├─ ",color:"yellow"},{text:"Device is connected and trusted",indent:1,prefix:"└─ ",color:"yellow"}]);throw error;}progress.start("findApp");try{APP_PATH=await findPhysicalDeviceAppPath();progress.log("Found app at: "+APP_PATH,"success");progress.complete("findApp");}catch(error){progress.fail("findApp",error.message);throw error;}await installAndLaunchOnPhysicalDevice(APP_PATH,physicalDevice);}else{// Simulator workflow (with moveAppToBuildOutput improvement)
|
|
@@ -302,4 +309,4 @@ fs.copyFileSync(foundImage.path,destinationPath);foundIcons.push({size,scale,fil
|
|
|
302
309
|
const iconKey=`${size}-${idiom}-${scale}`;addedIcons.add(iconKey);// Remove existing entry with same size/idiom/scale
|
|
303
310
|
contents.images=contents.images.filter(img=>`${img.size}-${img.idiom}-${img.scale}`!==iconKey);// Add new entry
|
|
304
311
|
contents.images.push({size,idiom,scale,filename});}}if(foundIcons.length>0){// Write updated Contents.json
|
|
305
|
-
fs.writeFileSync(contentsPath,JSON.stringify(contents,null,2));progress.log(`Updated AppIcon.appiconset with ${foundIcons.length} icon(s):`,"success");foundIcons.forEach(icon=>{progress.log(` • ${icon.size} @${icon.scale} (${icon.idiom})`,"info");});}else{progress.log("No app icon files found in public folder","info");progress.log("Expected naming pattern: icon-{size}-{scale}.{ext}","info");progress.log("Example: icon-20x20-2x.png, icon-60x60-3x.png, icon-1024x1024-1x.png","info");}}catch(error){progress.log(`Warning: Error copying app icons: ${error.message}`,"warning");}}async function main(){try{progress.log("Starting build process...","info");
|
|
312
|
+
fs.writeFileSync(contentsPath,JSON.stringify(contents,null,2));progress.log(`Updated AppIcon.appiconset with ${foundIcons.length} icon(s):`,"success");foundIcons.forEach(icon=>{progress.log(` • ${icon.size} @${icon.scale} (${icon.idiom})`,"info");});}else{progress.log("No app icon files found in public folder","info");progress.log("Expected naming pattern: icon-{size}-{scale}.{ext}","info");progress.log("Example: icon-20x20-2x.png, icon-60x60-3x.png, icon-1024x1024-1x.png","info");}}catch(error){progress.log(`Warning: Error copying app icons: ${error.message}`,"warning");}}async function main(){try{progress.log("Starting build process...","info");await generateConfigConstants();await updateInfoPlist();await buildForIOS();}catch(error){progress.log("Build failed: "+error.message,"error");process.exit(1);}process.exit(0);}main();
|
|
@@ -17,8 +17,6 @@ class NativeBridge: NSObject, BridgeCommandHandlerDelegate, BridgeFileHandlerDel
|
|
|
17
17
|
weak var webView: WKWebView?
|
|
18
18
|
private weak var viewController: UIViewController?
|
|
19
19
|
private weak var webViewModel: WebViewModel?
|
|
20
|
-
private var messageHandlerProxy: WeakScriptMessageHandler?
|
|
21
|
-
private var isRegistered = false
|
|
22
20
|
|
|
23
21
|
// Protocol-based notification handler (injected at runtime)
|
|
24
22
|
private var notificationHandler: NotificationHandlerProtocol = NullNotificationHandler.shared
|
|
@@ -135,16 +133,10 @@ class NativeBridge: NSObject, BridgeCommandHandlerDelegate, BridgeFileHandlerDel
|
|
|
135
133
|
|
|
136
134
|
// Register the JavaScript interface with the WebView
|
|
137
135
|
func register() {
|
|
138
|
-
guard !isRegistered else { return }
|
|
139
136
|
let registerStart = CFAbsoluteTimeGetCurrent()
|
|
140
137
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
let proxy = WeakScriptMessageHandler(delegate: self)
|
|
145
|
-
userContentController.add(proxy, name: "NativeBridge")
|
|
146
|
-
messageHandlerProxy = proxy
|
|
147
|
-
isRegistered = true
|
|
138
|
+
let userContentController = webView?.configuration.userContentController
|
|
139
|
+
userContentController?.add(self, name: "NativeBridge")
|
|
148
140
|
|
|
149
141
|
let registerTime = (CFAbsoluteTimeGetCurrent() - registerStart) * 1000
|
|
150
142
|
logWithTimestamp("✅ NativeBridge registered (took \(String(format: "%.2f", registerTime))ms)")
|
|
@@ -152,10 +144,7 @@ class NativeBridge: NSObject, BridgeCommandHandlerDelegate, BridgeFileHandlerDel
|
|
|
152
144
|
|
|
153
145
|
// Unregister to prevent memory leaks
|
|
154
146
|
func unregister() {
|
|
155
|
-
guard isRegistered else { return }
|
|
156
147
|
webView?.configuration.userContentController.removeScriptMessageHandler(forName: "NativeBridge")
|
|
157
|
-
messageHandlerProxy = nil
|
|
158
|
-
isRegistered = false
|
|
159
148
|
|
|
160
149
|
if let listenerId = networkStatusListenerId {
|
|
161
150
|
NetworkMonitor.shared.removeListener(listenerId)
|
|
@@ -121,8 +121,6 @@ public struct WebView: UIViewRepresentable, Equatable {
|
|
|
121
121
|
// Clean up native bridge
|
|
122
122
|
coordinator.nativeBridge?.unregister()
|
|
123
123
|
coordinator.nativeBridge = nil
|
|
124
|
-
coordinator.pluginBridge?.unregister()
|
|
125
|
-
coordinator.pluginBridge = nil
|
|
126
124
|
coordinator.hostingController = nil
|
|
127
125
|
|
|
128
126
|
// Unregister custom URL protocol
|
|
@@ -134,7 +132,6 @@ public struct WebView: UIViewRepresentable, Equatable {
|
|
|
134
132
|
public class Coordinator: NSObject {
|
|
135
133
|
var parent: WebView
|
|
136
134
|
var nativeBridge: NativeBridge?
|
|
137
|
-
var pluginBridge: PluginBridge?
|
|
138
135
|
var hostingController: UIViewController?
|
|
139
136
|
var isObserverAdded = false
|
|
140
137
|
|
|
@@ -149,7 +146,6 @@ public struct WebView: UIViewRepresentable, Equatable {
|
|
|
149
146
|
|
|
150
147
|
// Create and register the native bridge
|
|
151
148
|
let bridge = NativeBridge(webView: webView, viewController: hostingController)
|
|
152
|
-
let pluginBridge = PluginBridge(webView: webView, viewController: hostingController)
|
|
153
149
|
|
|
154
150
|
// Inject WebViewModel for safe area handling
|
|
155
151
|
Task { @MainActor in
|
|
@@ -160,9 +156,7 @@ public struct WebView: UIViewRepresentable, Equatable {
|
|
|
160
156
|
bridge.setNotificationHandler(NotificationHandlerProvider.shared)
|
|
161
157
|
|
|
162
158
|
bridge.register()
|
|
163
|
-
pluginBridge.register()
|
|
164
159
|
self.nativeBridge = bridge
|
|
165
|
-
self.pluginBridge = pluginBridge
|
|
166
160
|
}
|
|
167
161
|
|
|
168
162
|
override public func observeValue(forKeyPath keyPath: String?,
|
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
E9F699E02EE065B0005E972E /* NotificationHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F699DF2EE065B0005E972E /* NotificationHandlerTests.swift */; };
|
|
24
24
|
E9F699E22EE06650005E972E /* FrameworkServerUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F699E12EE06650005E972E /* FrameworkServerUtilsTests.swift */; };
|
|
25
25
|
E9F699E42EE0696A005E972E /* BootTimingUtilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F699E32EE0696A005E972E /* BootTimingUtilityTests.swift */; };
|
|
26
|
-
F2B100012F11111100AAA001 /* PluginBridgeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2B100022F11111100AAA001 /* PluginBridgeTests.swift */; };
|
|
27
26
|
F1234AC22E990180008C7F58 /* localhost.p12 in Resources */ = {isa = PBXBuildFile; fileRef = F1234AC32E990180008C7F58 /* localhost.p12 */; };
|
|
28
27
|
/* End PBXBuildFile section */
|
|
29
28
|
|
|
@@ -67,7 +66,6 @@
|
|
|
67
66
|
E9F699DF2EE065B0005E972E /* NotificationHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationHandlerTests.swift; sourceTree = "<group>"; };
|
|
68
67
|
E9F699E12EE06650005E972E /* FrameworkServerUtilsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FrameworkServerUtilsTests.swift; sourceTree = "<group>"; };
|
|
69
68
|
E9F699E32EE0696A005E972E /* BootTimingUtilityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BootTimingUtilityTests.swift; sourceTree = "<group>"; };
|
|
70
|
-
F2B100022F11111100AAA001 /* PluginBridgeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginBridgeTests.swift; sourceTree = "<group>"; };
|
|
71
69
|
F1234AC32E990180008C7F58 /* localhost.p12 */ = {isa = PBXFileReference; lastKnownFileType = file; path = localhost.p12; sourceTree = "<group>"; };
|
|
72
70
|
XCCONFIG001000000001 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
|
|
73
71
|
XCCONFIG001000000002 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
|
|
@@ -154,7 +152,6 @@
|
|
|
154
152
|
E9F699DB2EE06482005E972E /* CacheManagerTests.swift */,
|
|
155
153
|
E9F699D92EE063D5005E972E /* WebViewTests.swift */,
|
|
156
154
|
E9F699D72EE06068005E972E /* NativeBridgeTests.swift */,
|
|
157
|
-
F2B100022F11111100AAA001 /* PluginBridgeTests.swift */,
|
|
158
155
|
E9F699D52EE05D74005E972E /* BridgeMessageValidatorTests.swift */,
|
|
159
156
|
E9F699D32EE05984005E972E /* ConfigMappingTests.swift */,
|
|
160
157
|
E9F699D02EDEFEF2005E972E /* URLWhitelistManagerTests.swift */,
|
|
@@ -324,7 +321,6 @@
|
|
|
324
321
|
E9F699DC2EE06483005E972E /* CacheManagerTests.swift in Sources */,
|
|
325
322
|
E9F699D42EE05984005E972E /* ConfigMappingTests.swift in Sources */,
|
|
326
323
|
E9F699D82EE06068005E972E /* NativeBridgeTests.swift in Sources */,
|
|
327
|
-
F2B100012F11111100AAA001 /* PluginBridgeTests.swift in Sources */,
|
|
328
324
|
E9F699DA2EE063D5005E972E /* WebViewTests.swift in Sources */,
|
|
329
325
|
E9F699E42EE0696A005E972E /* BootTimingUtilityTests.swift in Sources */,
|
|
330
326
|
E9F699E22EE06650005E972E /* FrameworkServerUtilsTests.swift in Sources */,
|