Skip to content

Commit f33be1f

Browse files
committed
fix(ios): port Braze native setup from AppDelegate.m to AppDelegate.swift
The RN 0.81 upgrade replaced the ObjC AppDelegate with Swift but did not port the Braze SDK initialization. Without it, iOS cold-start deeplinks from Braze push notifications are silently lost. Adds BrazeHelper.mm — a small ObjC++ shim that calls BrazeReactBridge and BrazeReactUtils without exposing BrazeKit-Swift.h to the bridging header (which would create type-identity conflicts with ).
1 parent 856b700 commit f33be1f

4 files changed

Lines changed: 79 additions & 0 deletions

File tree

ios/MetaMask-Bridging-Header.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,10 @@
44

55
#import <React/RCTBridgeModule.h>
66
#import <Expo/Expo.h>
7+
8+
// Thin C wrappers around BrazeReactBridge / BrazeReactUtils.
9+
// Implemented in BrazeHelper.mm.
10+
// Uses id (AnyObject in Swift) to avoid importing BrazeKit-Swift.h here,
11+
// which would create type-identity conflicts with `import BrazeKit` in Swift.
12+
id _Nonnull BrazeHelperInit(id _Nonnull configuration);
13+
void BrazeHelperPopulateInitialPayload(NSDictionary * _Nullable launchOptions);

ios/MetaMask.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@
8888
E4B580762E33A001008165E1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B580752E33A001008165E1 /* AppDelegate.swift */; };
8989
E4B580772E33A001008165E1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B580752E33A001008165E1 /* AppDelegate.swift */; };
9090
E4B580782E33A001008165E1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B580752E33A001008165E1 /* AppDelegate.swift */; };
91+
F0B2A3E101000001000000A1 /* BrazeHelper.mm in Sources */ = {isa = PBXBuildFile; fileRef = F0B2A3E101000001000000A0 /* BrazeHelper.mm */; };
92+
F0B2A3E101000001000000A2 /* BrazeHelper.mm in Sources */ = {isa = PBXBuildFile; fileRef = F0B2A3E101000001000000A0 /* BrazeHelper.mm */; };
93+
F0B2A3E101000001000000A3 /* BrazeHelper.mm in Sources */ = {isa = PBXBuildFile; fileRef = F0B2A3E101000001000000A0 /* BrazeHelper.mm */; };
9194
E83DB5522BBDF2AA00536063 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = E83DB5392BBDB14700536063 /* PrivacyInfo.xcprivacy */; };
9295
E83DB5532BBDF2AE00536063 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = E83DB5392BBDB14700536063 /* PrivacyInfo.xcprivacy */; };
9396
E83DB5542BBDF2AF00536063 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = E83DB5392BBDB14700536063 /* PrivacyInfo.xcprivacy */; };
@@ -239,6 +242,7 @@
239242
D3350113F0764105B1E60002 /* MMSans-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "MMSans-Bold.otf"; path = "../app/fonts/MMSans-Bold.otf"; sourceTree = "<group>"; };
240243
E4B580712E32F462008165E1 /* Expo.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; };
241244
E4B580752E33A001008165E1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetaMask/AppDelegate.swift; sourceTree = "<group>"; };
245+
F0B2A3E101000001000000A0 /* BrazeHelper.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MetaMask/BrazeHelper.mm; sourceTree = "<group>"; };
242246
E7EEA32C976A46B991D55FD4 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-MetaMask-QA/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
243247
E83DB5392BBDB14700536063 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = MetaMask/PrivacyInfo.xcprivacy; sourceTree = SOURCE_ROOT; };
244248
E9629905BA1940ADA4189921 /* Feather.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Feather.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Feather.ttf"; sourceTree = "<group>"; };
@@ -329,6 +333,7 @@
329333
158B0639211A72F500DF3C74 /* InpageBridgeWeb3.js */,
330334
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
331335
E4B580752E33A001008165E1 /* AppDelegate.swift */,
336+
F0B2A3E101000001000000A0 /* BrazeHelper.mm */,
332337
13B07FB51A68108700A75B9A /* Images.xcassets */,
333338
13B07FB61A68108700A75B9A /* Info.plist */,
334339
FE3C9A2458A1416290DEDAD4 /* branch.json */,
@@ -1201,6 +1206,7 @@
12011206
buildActionMask = 2147483647;
12021207
files = (
12031208
E4B580762E33A001008165E1 /* AppDelegate.swift in Sources */,
1209+
F0B2A3E101000001000000A1 /* BrazeHelper.mm in Sources */,
12041210
2EF283322B17EC1A00D7B4B1 /* RNTar.m in Sources */,
12051211
654378B0243E2ADC00571B9C /* File.swift in Sources */,
12061212
2EF2832A2B17EBD600D7B4B1 /* RnTar.swift in Sources */,
@@ -1216,6 +1222,7 @@
12161222
buildActionMask = 2147483647;
12171223
files = (
12181224
E4B580772E33A001008165E1 /* AppDelegate.swift in Sources */,
1225+
F0B2A3E101000001000000A2 /* BrazeHelper.mm in Sources */,
12191226
2EF283342B17EC1A00D7B4B1 /* RNTar.m in Sources */,
12201227
2EF2825B2B0FF86900D7B4B1 /* File.swift in Sources */,
12211228
2EF2832C2B17EBD600D7B4B1 /* RnTar.swift in Sources */,
@@ -1233,6 +1240,7 @@
12331240
CFD8DFC828EDD4C800CC75F6 /* RCTScreenshotDetect.m in Sources */,
12341241
2EF283332B17EC1A00D7B4B1 /* RNTar.m in Sources */,
12351242
E4B580782E33A001008165E1 /* AppDelegate.swift in Sources */,
1243+
F0B2A3E101000001000000A3 /* BrazeHelper.mm in Sources */,
12361244
2EF2832B2B17EBD600D7B4B1 /* RnTar.swift in Sources */,
12371245
2EF283382B17EC7900D7B4B1 /* Light-Swift-Untar.swift in Sources */,
12381246
B339FF03289ABD70001B89FB /* File.swift in Sources */,

ios/MetaMask/AppDelegate.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import React
44
import ReactAppDependencyProvider
55
import FirebaseCore
66
import RNBranch
7+
import BrazeKit
78

89
final class MetaMaskReactNativeDelegate: ExpoReactNativeFactoryDelegate {
910
override func sourceURL(for bridge: RCTBridge) -> URL? {
@@ -26,6 +27,8 @@ class AppDelegate: ExpoAppDelegate {
2627
private var reactNativeDelegate: ExpoReactNativeFactoryDelegate?
2728
private var reactNativeFactory: RCTReactNativeFactory?
2829

30+
@objc static var braze: Braze?
31+
2932
// Detox's `+[ReactNativeSupport reloadApp]` does
3033
// `[appDelegate valueForKey:@"rootViewFactory"]` to grab RN's RootViewFactory
3134
// for hot-reload. In older RN, `RCTAppDelegate` exposed `rootViewFactory`
@@ -63,6 +66,24 @@ class AppDelegate: ExpoAppDelegate {
6366
RNBranch.branch.checkPasteboardOnInstall()
6467
RNBranch.initSession(launchOptions: launchOptions, isReferrable: true)
6568

69+
// Setup Braze
70+
if let brazeApiKey = Bundle.main.object(forInfoDictionaryKey: "braze_api_key") as? String,
71+
let brazeEndpoint = Bundle.main.object(forInfoDictionaryKey: "braze_sdk_endpoint") as? String,
72+
!brazeApiKey.isEmpty, !brazeEndpoint.isEmpty {
73+
let configuration = Braze.Configuration(apiKey: brazeApiKey, endpoint: brazeEndpoint)
74+
configuration.logger.level = .info
75+
// push.automation handles APNs token registration and Braze-originated notification display.
76+
// requestAuthorizationAtLaunch is false so the existing permission flow (Firebase/Notifee) is preserved.
77+
configuration.push.automation = true
78+
configuration.push.automation.requestAuthorizationAtLaunch = false
79+
configuration.forwardUniversalLinks = true
80+
// swiftlint:disable:next force_cast
81+
let braze = BrazeHelperInit(configuration) as! Braze
82+
braze.delegate = self
83+
AppDelegate.braze = braze
84+
BrazeHelperPopulateInitialPayload(launchOptions)
85+
}
86+
6687
factory.startReactNative(
6788
withModuleName: "MetaMask",
6889
in: window,
@@ -122,3 +143,26 @@ class AppDelegate: ExpoAppDelegate {
122143
)
123144
}
124145
}
146+
147+
// MARK: - BrazeDelegate
148+
149+
extension AppDelegate: BrazeDelegate {
150+
// Route Braze deep link URLs ourselves instead of letting BrazeKit open them
151+
// via UIApplication.open (which would cause a duplicate delivery — once from
152+
// the Braze RN bridge JS event and once from the system URL handler).
153+
//
154+
// Universal links (Branch domains) are forwarded to Branch for proper routing.
155+
// All other URLs are suppressed here; they are handled exclusively through
156+
// the JS PUSH_NOTIFICATION_EVENT, tagged with ORIGIN_BRAZE.
157+
func braze(_ braze: Braze, shouldOpenURL context: Braze.URLContext) -> Bool {
158+
if let host = context.url.host,
159+
host.contains("app.link") ||
160+
host.contains("test-app.link") ||
161+
host.contains("link.metamask.io") ||
162+
host.contains("link-test.metamask.io") {
163+
Branch.getInstance().handleDeepLink(context.url)
164+
return false
165+
}
166+
return false
167+
}
168+
}

ios/MetaMask/BrazeHelper.mm

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// ObjC++ shim: BrazeReactBridge.h imports BrazeKit-Swift.h which creates
2+
// type conflicts when exposed to Swift alongside `import BrazeKit`.
3+
// This file keeps those imports in ObjC++ and exposes plain C functions.
4+
// .mm for React’s C++ headers; extern "C" to prevent name-mangling.
5+
6+
#import <BrazeKit/BrazeKit-Swift.h>
7+
#import "BrazeReactBridge.h"
8+
#import "BrazeReactUtils.h"
9+
10+
extern "C" {
11+
12+
id BrazeHelperInit(id configuration) {
13+
return [BrazeReactBridge initBraze:(BRZConfiguration *)configuration];
14+
}
15+
16+
void BrazeHelperPopulateInitialPayload(NSDictionary *launchOptions) {
17+
[[BrazeReactUtils sharedInstance] populateInitialPayloadFromLaunchOptions:launchOptions];
18+
}
19+
20+
} // extern "C"

0 commit comments

Comments
 (0)