@digia-engage/core 1.0.0-beta.3 → 1.0.0-beta.5

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.
@@ -1,11 +1,11 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'DigiaEngageReactNative'
3
3
  s.version = '0.1.0'
4
- s.summary = 'React Native bridge for the Digia Engage SDK (Android Compose UI).'
4
+ s.summary = 'React Native bridge for the Digia Engage SDK (iOS & Android).'
5
5
  s.description = <<-DESC
6
- Provides a React Native bridge that surfaces the Digia Engage Android
7
- Compose UI SDK inside React Native applications. iOS support is stubbed
8
- and will be added in a future release.
6
+ Provides a React Native bridge that surfaces the Digia Engage SDK inside
7
+ React Native applications. Supports both iOS (SwiftUI) and Android
8
+ (Jetpack Compose) using the New Architecture (TurboModules / Fabric).
9
9
  DESC
10
10
 
11
11
  s.homepage = 'https://github.com/Digia-Technology-Private-Limited/digia_engage'
@@ -13,9 +13,28 @@ Pod::Spec.new do |s|
13
13
  s.author = { 'Digia Technology Private Limited' => '' }
14
14
  s.source = { :git => 'https://github.com/Digia-Technology-Private-Limited/digia_engage.git', :tag => s.version.to_s }
15
15
 
16
- s.ios.deployment_target = '13.0'
16
+ # DigiaEngage iOS SDK requires iOS 16+ (SwiftUI features used internally).
17
+ s.ios.deployment_target = '16.0'
17
18
 
18
19
  s.source_files = 'ios/**/*.{h,m,mm,swift}'
19
20
 
21
+ # Swift version must match the Digia iOS SDK.
22
+ s.swift_version = '5.9'
23
+
20
24
  s.dependency 'React-Core'
25
+
26
+ # ── Digia Engage iOS SDK ──────────────────────────────────────────────────
27
+ # Published Swift Package: https://swiftpackageindex.com/Digia-Technology-Private-Limited/digia_engage_iOS
28
+ # Source: https://github.com/Digia-Technology-Private-Limited/digia_engage_iOS.git
29
+ #
30
+ # When integrating via CocoaPods, add the git source to your Podfile:
31
+ # pod 'DigiaEngage', :git => 'https://github.com/Digia-Technology-Private-Limited/digia_engage_iOS.git',
32
+ # :tag => '1.0.0-beta.1'
33
+ s.dependency 'DigiaEngage', '1.0.0-beta.1'
34
+
35
+ # ── New Architecture (Fabric / TurboModules) support ─────────────────────
36
+ # install_modules_dependencies wires the pod into both Old Architecture
37
+ # (bridge) and New Architecture (JSI / Fabric) automatically when the host
38
+ # app has `use_frameworks!` / `use_react_native!` with :fabric_enabled.
39
+ install_modules_dependencies(s)
21
40
  end
@@ -70,8 +70,8 @@ android {
70
70
  }
71
71
 
72
72
  dependencies {
73
- // Digia Engage Android library (published to JitPack)
74
- implementation 'com.github.Digia-Technology-Private-Limited:digia_engage:android.1.0.0-beta.7'
73
+ // Digia Engage Android library
74
+ implementation 'tech.digia:engage:1.0.0-beta.03'
75
75
 
76
76
  // ── React Native ─────────────────────────────────────────────────────────
77
77
  // React Native is provided by the host app; mark as compileOnly so it is
@@ -17,8 +17,7 @@ dependencyResolutionManagement {
17
17
  repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
18
18
  repositories {
19
19
  google()
20
- mavenCentral()
21
- mavenLocal()
20
+ mavenCentral() // tech.digia:engage published at https://central.sonatype.com/artifact/tech.digia/engage
22
21
  maven { url 'https://jitpack.io' }
23
22
  }
24
23
  }
@@ -1,71 +1,51 @@
1
1
  /**
2
- * DigiaEngageModule (iOS stub)
2
+ * DigiaEngageModule.m
3
3
  *
4
- * iOS support is not yet implemented. This stub registers the module so that
5
- * the JS layer does not crash when imported on iOS. All methods are no-ops.
4
+ * ObjC bridge file that exports Swift implementations to the React Native
5
+ * runtime (both Old Architecture bridge and New Architecture TurboModules).
6
+ *
7
+ * All real logic lives in the Swift files alongside this one:
8
+ * DigiaModule.swift — NativeModule (RCTEventEmitter subclass)
9
+ * RNEventBridgePlugin.swift — DigiaCEPPlugin bridge
10
+ * DigiaHostViewManager.swift — ViewManager for <DigiaHostView>
11
+ * DigiaSlotViewManager.swift — ViewManager for <DigiaSlotView>
6
12
  */
13
+
7
14
  #import <React/RCTBridgeModule.h>
15
+ #import <React/RCTEventEmitter.h>
8
16
  #import <React/RCTViewManager.h>
9
17
 
10
- // ── NativeModule stub ─────────────────────────────────────────────────────────
11
-
12
- @interface DigiaEngageModule : NSObject <RCTBridgeModule>
13
- @end
18
+ // ── NativeModule ──────────────────────────────────────────────────────────────
14
19
 
15
- @implementation DigiaEngageModule
20
+ // RCT_EXTERN_MODULE wires the Swift class DigiaModule (which inherits
21
+ // RCTEventEmitter) to the React Native bridge under the name "DigiaEngageModule".
16
22
 
17
- RCT_EXPORT_MODULE(DigiaEngageModule)
23
+ @interface RCT_EXTERN_MODULE(DigiaEngageModule, RCTEventEmitter)
18
24
 
19
- RCT_EXPORT_METHOD(initialize:(NSString *)apiKey
25
+ RCT_EXTERN_METHOD(initialize:(NSString *)apiKey
20
26
  environment:(NSString *)environment
21
27
  logLevel:(NSString *)logLevel
22
28
  resolve:(RCTPromiseResolveBlock)resolve
23
29
  reject:(RCTPromiseRejectBlock)reject)
24
- {
25
- // iOS not yet implemented – resolve immediately so JS doesn't hang.
26
- resolve(nil);
27
- }
28
30
 
29
- RCT_EXPORT_METHOD(setCurrentScreen:(NSString *)name)
30
- {
31
- // no-op
32
- }
31
+ RCT_EXTERN_METHOD(registerBridge)
33
32
 
34
- RCT_EXPORT_METHOD(openNavigation:(nullable NSString *)startPageId
35
- pageArgs:(NSDictionary *)pageArgs)
36
- {
37
- // no-op
38
- }
33
+ RCT_EXTERN_METHOD(setCurrentScreen:(NSString *)name)
39
34
 
40
- RCT_EXPORT_METHOD(triggerCampaign:(NSString *)campaignId
35
+ RCT_EXTERN_METHOD(triggerCampaign:(NSString *)id
41
36
  content:(NSDictionary *)content
42
37
  cepContext:(NSDictionary *)cepContext)
43
- {
44
- // no-op — iOS Digia SDK not yet available
45
- }
46
38
 
47
- RCT_EXPORT_METHOD(invalidateCampaign:(NSString *)campaignId)
48
- {
49
- // no-op
50
- }
39
+ RCT_EXTERN_METHOD(invalidateCampaign:(NSString *)campaignId)
51
40
 
52
41
  @end
53
42
 
54
43
 
55
- // ── ViewManager stub ──────────────────────────────────────────────────────────
44
+ // ── ViewManagers ──────────────────────────────────────────────────────────────
56
45
 
57
- @interface DigiaHostViewManager : RCTViewManager
46
+ @interface RCT_EXTERN_MODULE(DigiaHostView, RCTViewManager)
58
47
  @end
59
48
 
60
- @implementation DigiaHostViewManager
61
-
62
- RCT_EXPORT_MODULE(DigiaHostView)
63
-
64
- - (UIView *)view {
65
- // Return a transparent placeholder view
66
- UIView *v = [[UIView alloc] init];
67
- v.userInteractionEnabled = NO;
68
- return v;
69
- }
70
-
49
+ @interface RCT_EXTERN_MODULE(DigiaSlotView, RCTViewManager)
50
+ RCT_EXPORT_VIEW_PROPERTY(placementKey, NSString)
71
51
  @end
@@ -0,0 +1,104 @@
1
+ /**
2
+ * DigiaHostViewManager
3
+ *
4
+ * React Native ViewManager that exposes a UIView wrapping DigiaHost (from the
5
+ * Digia iOS SDK) as the native view behind the JS <DigiaHostView> component.
6
+ *
7
+ * DigiaHost is a SwiftUI view, so we bridge it into UIKit using a
8
+ * UIHostingController embedded as a child view controller. The host view
9
+ * manages dialog and bottom-sheet overlays driven by Digia CEP plugins.
10
+ *
11
+ * Place <DigiaHostView> once at the root of your RN component tree.
12
+ */
13
+ import SwiftUI
14
+ import React
15
+ import DigiaEngage
16
+
17
+ @objc(DigiaHostView)
18
+ final class DigiaHostViewManager: RCTViewManager {
19
+
20
+ override static func requiresMainQueueSetup() -> Bool { true }
21
+
22
+ override func view() -> UIView! {
23
+ return DigiaHostUIView()
24
+ }
25
+ }
26
+
27
+ // MARK: - DigiaHostUIView
28
+
29
+ /// Lightweight UIView container that embeds a UIHostingController<DigiaHost>
30
+ /// as a child so SwiftUI's DigiaHost composable renders overlays above all
31
+ /// React Native content.
32
+ final class DigiaHostUIView: UIView {
33
+
34
+ private var hostingController: UIHostingController<DigiaHostWrapperView>?
35
+
36
+ override func didMoveToWindow() {
37
+ super.didMoveToWindow()
38
+ guard window != nil else {
39
+ // View removed from hierarchy — tear down the hosting controller.
40
+ hostingController?.willMove(toParent: nil)
41
+ hostingController?.view.removeFromSuperview()
42
+ hostingController?.removeFromParent()
43
+ hostingController = nil
44
+ return
45
+ }
46
+ guard hostingController == nil else { return }
47
+ mountHostingController()
48
+ }
49
+
50
+ private func mountHostingController() {
51
+ guard let parentVC = parentViewController() else { return }
52
+
53
+ let swiftUIView = DigiaHostWrapperView()
54
+ let hc = UIHostingController(rootView: swiftUIView)
55
+ hc.view.translatesAutoresizingMaskIntoConstraints = false
56
+ hc.view.backgroundColor = .clear
57
+ // Disable touch interception so all taps pass through to React Native
58
+ // content below. The dialog/bottom-sheet overlays are presented as
59
+ // separate UIViewControllers (via ViewControllerUtil.present) so they
60
+ // independently capture touches when visible.
61
+ hc.view.isUserInteractionEnabled = false
62
+
63
+ parentVC.addChild(hc)
64
+ addSubview(hc.view)
65
+ hc.didMove(toParent: parentVC)
66
+
67
+ NSLayoutConstraint.activate([
68
+ hc.view.leadingAnchor.constraint(equalTo: leadingAnchor),
69
+ hc.view.trailingAnchor.constraint(equalTo: trailingAnchor),
70
+ hc.view.topAnchor.constraint(equalTo: topAnchor),
71
+ hc.view.bottomAnchor.constraint(equalTo: bottomAnchor),
72
+ ])
73
+
74
+ hostingController = hc
75
+ }
76
+
77
+ // Walk the responder chain to find the nearest UIViewController.
78
+ private func parentViewController() -> UIViewController? {
79
+ var responder: UIResponder? = self
80
+ while let r = responder {
81
+ if let vc = r as? UIViewController { return vc }
82
+ responder = r.next
83
+ }
84
+ return nil
85
+ }
86
+ }
87
+
88
+ // MARK: - SwiftUI wrapper
89
+
90
+ /// A SwiftUI view that acts as the DigiaHost root. An EmptyView is used as
91
+ /// content because React Native's own navigation already manages the app's
92
+ /// view hierarchy — DigiaHost only needs to be mounted to activate the overlay
93
+ /// layer.
94
+ private struct DigiaHostWrapperView: View {
95
+ var body: some View {
96
+ DigiaHost {
97
+ EmptyView()
98
+ }
99
+ // No frame constraint here — the view fills its parent (absoluteFill
100
+ // from JS). A non-zero frame is required so UIKit calls viewDidAppear
101
+ // on the UIHostingController, which in turn triggers SwiftUI onAppear
102
+ // and establishes the onChange(activePayload) subscription.
103
+ }
104
+ }
@@ -0,0 +1,192 @@
1
+ /**
2
+ * DigiaModule
3
+ *
4
+ * React Native NativeModule that bridges the Digia Engage iOS SDK.
5
+ *
6
+ * Exposed methods (callable from JS via NativeModules.DigiaEngageModule):
7
+ * initialize(apiKey, environment, logLevel): Promise<void>
8
+ * registerBridge(): void
9
+ * setCurrentScreen(name): void
10
+ * triggerCampaign(id, content, cepContext): void
11
+ * invalidateCampaign(campaignId): void
12
+ *
13
+ * Architecture
14
+ * ────────────
15
+ * The RN bridge mirrors the native Digia.initialize / Digia.register /
16
+ * Digia.setCurrentScreen flow exactly. An internal RNEventBridgePlugin is
17
+ * the single native DigiaCEPPlugin registered via Digia.register().
18
+ *
19
+ * When the SDK calls plugin.setup(delegate:), the bridge stores that
20
+ * delegate reference. JS plugins that deliver campaigns call triggerCampaign /
21
+ * invalidateCampaign which forward to delegate.onCampaignTriggered /
22
+ * delegate.onCampaignInvalidated.
23
+ *
24
+ * Overlay lifecycle events (impressed / clicked / dismissed) are forwarded from
25
+ * the native plugin.notifyEvent() to JS via RCTEventEmitter so that pure-JS
26
+ * CEP plugins (e.g. DigiaMoEngagePlugin) can report analytics.
27
+ */
28
+ import Foundation
29
+ import React
30
+ import DigiaEngage
31
+
32
+ @objc(DigiaEngageModule)
33
+ final class DigiaModule: RCTEventEmitter {
34
+
35
+ private lazy var rnPlugin: RNEventBridgePlugin = MainActor.assumeIsolated {
36
+ RNEventBridgePlugin(eventEmitter: self)
37
+ }
38
+
39
+ // ────────────────────────────────────────────────────────────────────────
40
+ // MARK: - RCTEventEmitter
41
+
42
+ override static func requiresMainQueueSetup() -> Bool { true }
43
+
44
+ override func supportedEvents() -> [String]! {
45
+ return [
46
+ "digia_experience_impressed",
47
+ "digia_experience_clicked",
48
+ "digia_experience_dismissed",
49
+ ]
50
+ }
51
+
52
+ // ────────────────────────────────────────────────────────────────────────
53
+ // MARK: - initialize
54
+
55
+ @objc
56
+ func initialize(
57
+ _ apiKey: String,
58
+ environment: String,
59
+ logLevel: String,
60
+ resolve: @escaping RCTPromiseResolveBlock,
61
+ reject: @escaping RCTPromiseRejectBlock
62
+ ) {
63
+ let envValue: DigiaEnvironment = environment.lowercased() == "sandbox" ? .sandbox : .production
64
+ let logLevelValue: DigiaLogLevel
65
+ switch logLevel.lowercased() {
66
+ case "verbose": logLevelValue = .verbose
67
+ case "none": logLevelValue = .none
68
+ default: logLevelValue = .error
69
+ }
70
+
71
+ let config = DigiaConfig(
72
+ apiKey: apiKey,
73
+ logLevel: logLevelValue,
74
+ environment: envValue
75
+ )
76
+
77
+ Task { @MainActor in
78
+ do {
79
+ try await Digia.initialize(config)
80
+ resolve(nil)
81
+ } catch {
82
+ reject("DIGIA_INIT_ERROR", error.localizedDescription, error)
83
+ }
84
+ }
85
+ }
86
+
87
+ // ────────────────────────────────────────────────────────────────────────
88
+ // MARK: - registerBridge
89
+
90
+ /// Registers RNEventBridgePlugin with the native Digia SDK.
91
+ /// Called automatically by the JS Digia.register() wrapper on first plugin
92
+ /// registration so that the delegate is populated before any
93
+ /// triggerCampaign / invalidateCampaign calls arrive from JS.
94
+ @objc
95
+ func registerBridge() {
96
+ Task { @MainActor in
97
+ Digia.register(self.rnPlugin)
98
+ }
99
+ }
100
+
101
+ // ────────────────────────────────────────────────────────────────────────
102
+ // MARK: - setCurrentScreen
103
+
104
+ @objc
105
+ func setCurrentScreen(_ name: String) {
106
+ // Digia.setCurrentScreen is not yet part of the public iOS API; this is
107
+ // a no-op placeholder that can be activated once it is added.
108
+ // Digia.setCurrentScreen(name)
109
+ }
110
+
111
+ // ────────────────────────────────────────────────────────────────────────
112
+ // MARK: - triggerCampaign
113
+
114
+ /// Forwards a campaign payload to the native DigiaCEPDelegate.
115
+ ///
116
+ /// Called by the JS DigiaDelegate.onCampaignTriggered() when a JS CEP
117
+ /// plugin (e.g. DigiaMoEngagePlugin) delivers a campaign. The delegate
118
+ /// routes it into the SwiftUI overlay for rendering.
119
+ @objc
120
+ func triggerCampaign(
121
+ _ id: String,
122
+ content contentMap: NSDictionary,
123
+ cepContext cepContextMap: NSDictionary
124
+ ) {
125
+ let content = buildInAppPayloadContent(from: contentMap)
126
+ let cepContext = (cepContextMap as? [String: String]) ?? [:]
127
+ let payload = InAppPayload(id: id, content: content, cepContext: cepContext)
128
+
129
+ Task { @MainActor in
130
+ guard let delegate = self.rnPlugin.delegate else { return }
131
+ delegate.onCampaignTriggered(payload)
132
+ }
133
+ }
134
+
135
+ // ────────────────────────────────────────────────────────────────────────
136
+ // MARK: - invalidateCampaign
137
+
138
+ @objc
139
+ func invalidateCampaign(_ campaignId: String) {
140
+ Task { @MainActor in
141
+ guard let delegate = self.rnPlugin.delegate else { return }
142
+ delegate.onCampaignInvalidated(campaignId)
143
+ }
144
+ }
145
+
146
+ // ────────────────────────────────────────────────────────────────────────
147
+ // MARK: - Private helpers
148
+
149
+ private func buildInAppPayloadContent(from map: NSDictionary) -> InAppPayloadContent {
150
+ let type = (map["type"] as? String) ?? "dialog"
151
+ let pk = map["placementKey"] as? String
152
+ let title = map["title"] as? String
153
+ let text = map["text"] as? String
154
+ let viewId = map["viewId"] as? String
155
+ let command = map["command"] as? String
156
+ let screenId = map["screenId"] as? String
157
+ let args: [String: JSONValue] = {
158
+ guard let raw = map["args"] as? [String: Any] else { return [:] }
159
+ return raw.compactMapValues { JSONValue(rawValue: $0) }
160
+ }()
161
+
162
+ return InAppPayloadContent(
163
+ type: type,
164
+ placementKey: pk,
165
+ title: title,
166
+ text: text,
167
+ viewId: viewId,
168
+ command: command,
169
+ args: args,
170
+ screenId: screenId
171
+ )
172
+ }
173
+ }
174
+
175
+ // MARK: - JSONValue convenience init from Any
176
+ private extension JSONValue {
177
+ init?(rawValue: Any) {
178
+ switch rawValue {
179
+ case let s as String: self = .string(s)
180
+ case let b as Bool: self = .bool(b)
181
+ case let i as Int: self = .int(i)
182
+ case let d as Double: self = .double(d)
183
+ case let arr as [Any]:
184
+ self = .array(arr.compactMap { JSONValue(rawValue: $0) })
185
+ case let dict as [String: Any]:
186
+ let mapped = dict.compactMapValues { JSONValue(rawValue: $0) }
187
+ self = .object(mapped)
188
+ default:
189
+ return nil
190
+ }
191
+ }
192
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * DigiaSlotViewManager
3
+ *
4
+ * React Native ViewManager that exposes a UIView wrapping DigiaSlot (from the
5
+ * Digia iOS SDK) as the native view behind the JS <DigiaSlotView> component.
6
+ *
7
+ * DigiaSlot is a SwiftUI view, so it is bridged into UIKit via a
8
+ * UIHostingController. The placementKey prop is forwarded to the SwiftUI
9
+ * view via a StateObject/ObservableObject binding.
10
+ *
11
+ * Supported JS props:
12
+ * - `placementKey` (String) — matches the placement key set in the Digia dashboard.
13
+ */
14
+ import SwiftUI
15
+ import React
16
+ import DigiaEngage
17
+
18
+ @objc(DigiaSlotView)
19
+ final class DigiaSlotViewManager: RCTViewManager {
20
+
21
+ override static func requiresMainQueueSetup() -> Bool { true }
22
+
23
+ override func view() -> UIView! {
24
+ return DigiaSlotUIView()
25
+ }
26
+
27
+ // ── prop setter wired by ObjC runtime via RCT_EXPORT_VIEW_PROPERTY ────────
28
+ // The @objc name must match the RCT_EXPORT_VIEW_PROPERTY in the .m file.
29
+ @objc func setPlacementKey(_ placementKey: String, forView view: DigiaSlotUIView) {
30
+ view.placementKey = placementKey
31
+ }
32
+ }
33
+
34
+ // MARK: - DigiaSlotUIView
35
+
36
+ /// UIView container that embeds DigiaSlot<EmptyView> in a UIHostingController.
37
+ /// Re-creates the SwiftUI view when placementKey changes.
38
+ final class DigiaSlotUIView: UIView {
39
+
40
+ @objc var placementKey: String = "" {
41
+ didSet {
42
+ guard placementKey != oldValue else { return }
43
+ remount()
44
+ }
45
+ }
46
+
47
+ private var hostingController: UIHostingController<DigiaSlotWrapperView>?
48
+
49
+ override func didMoveToWindow() {
50
+ super.didMoveToWindow()
51
+ if window != nil {
52
+ if hostingController == nil { remount() }
53
+ } else {
54
+ teardown()
55
+ }
56
+ }
57
+
58
+ private func remount() {
59
+ teardown()
60
+ guard window != nil, !placementKey.isEmpty else { return }
61
+ guard let parentVC = parentViewController() else { return }
62
+
63
+ let swiftUIView = DigiaSlotWrapperView(placementKey: placementKey)
64
+ let hc = UIHostingController(rootView: swiftUIView)
65
+ hc.view.translatesAutoresizingMaskIntoConstraints = false
66
+ hc.view.backgroundColor = .clear
67
+
68
+ parentVC.addChild(hc)
69
+ addSubview(hc.view)
70
+ hc.didMove(toParent: parentVC)
71
+
72
+ NSLayoutConstraint.activate([
73
+ hc.view.leadingAnchor.constraint(equalTo: leadingAnchor),
74
+ hc.view.trailingAnchor.constraint(equalTo: trailingAnchor),
75
+ hc.view.topAnchor.constraint(equalTo: topAnchor),
76
+ hc.view.bottomAnchor.constraint(equalTo: bottomAnchor),
77
+ ])
78
+
79
+ hostingController = hc
80
+ }
81
+
82
+ private func teardown() {
83
+ hostingController?.willMove(toParent: nil)
84
+ hostingController?.view.removeFromSuperview()
85
+ hostingController?.removeFromParent()
86
+ hostingController = nil
87
+ }
88
+
89
+ private func parentViewController() -> UIViewController? {
90
+ var responder: UIResponder? = self
91
+ while let r = responder {
92
+ if let vc = r as? UIViewController { return vc }
93
+ responder = r.next
94
+ }
95
+ return nil
96
+ }
97
+ }
98
+
99
+ // MARK: - SwiftUI wrapper
100
+
101
+ private struct DigiaSlotWrapperView: View {
102
+ let placementKey: String
103
+
104
+ var body: some View {
105
+ DigiaSlot(placementKey)
106
+ }
107
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * RNEventBridgePlugin
3
+ *
4
+ * Implements DigiaCEPPlugin so it can be registered with the native iOS Digia
5
+ * SDK via Digia.register(). It acts as the bridge between the native SDK's
6
+ * DigiaCEPDelegate and the JS layer.
7
+ *
8
+ * Lifecycle
9
+ * ─────────
10
+ * 1. JS calls Digia.register() → native registerBridge() → this plugin is
11
+ * registered with the native SDK via Digia.register(rnPlugin).
12
+ * 2. The SDK calls setup(delegate:) storing the delegate reference.
13
+ * 3. When a JS CEP plugin delivers a campaign via onCampaignTriggered(),
14
+ * the JS side calls triggerCampaign() which forwards here via DigiaModule.
15
+ * 4. Overlay lifecycle events (impressed / clicked / dismissed) travel back to
16
+ * JS as DeviceEventEmitter events.
17
+ */
18
+ import Foundation
19
+ import React
20
+ import DigiaEngage
21
+
22
+ @objc
23
+ internal final class RNEventBridgePlugin: NSObject, DigiaCEPPlugin {
24
+
25
+ let identifier = "com.digia.rn.bridge"
26
+
27
+ /// Populated by the SDK when registered. Used to forward JS-side campaigns
28
+ /// into the native overlay rendering pipeline.
29
+ private(set) weak var delegate: DigiaCEPDelegate?
30
+
31
+ private weak var eventEmitter: RCTEventEmitter?
32
+
33
+ init(eventEmitter: RCTEventEmitter?) {
34
+ self.eventEmitter = eventEmitter
35
+ }
36
+
37
+ // MARK: - DigiaCEPPlugin
38
+
39
+ func setup(delegate: DigiaCEPDelegate) {
40
+ self.delegate = delegate
41
+ }
42
+
43
+ func notifyEvent(_ event: DigiaExperienceEvent, payload: InAppPayload) {
44
+ let eventName: String
45
+ switch event {
46
+ case .impressed:
47
+ eventName = "digia_experience_impressed"
48
+ case .clicked:
49
+ eventName = "digia_experience_clicked"
50
+ case .dismissed:
51
+ eventName = "digia_experience_dismissed"
52
+ }
53
+
54
+ let body: [String: Any] = [
55
+ "id": payload.id,
56
+ "type": payload.content.type,
57
+ ]
58
+ eventEmitter?.sendEvent(withName: eventName, body: body)
59
+ }
60
+
61
+ func healthCheck() -> DiagnosticReport {
62
+ return DiagnosticReport(
63
+ isHealthy: delegate != nil,
64
+ issue: delegate == nil ? "No delegate set" : nil,
65
+ resolution: delegate == nil ? "Call registerBridge() before triggerCampaign()" : nil
66
+ )
67
+ }
68
+
69
+ func teardown() {
70
+ delegate = nil
71
+ }
72
+ }
@@ -36,31 +36,33 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
36
36
  *
37
37
  * On Android this mounts a Jetpack Compose `DigiaHost` composable that
38
38
  * manages dialog + bottom-sheet presentation triggered by CEP plugins.
39
- * On iOS the view is a transparent no-op until iOS support is implemented.
39
+ * On iOS this mounts a SwiftUI `DigiaHost` via UIHostingController that
40
+ * manages the same overlay layer above React Native content.
40
41
  * ─────────────────────────────────────────────────────────────────────────────
41
42
  */
42
43
 
43
44
  // requireNativeComponent expects a plain ViewStyle, not StyleProp.
44
45
 
45
46
  // Fabric (New Architecture) resolves view configs lazily — no UIManager
46
- // guard needed. requireNativeComponent is called unconditionally on Android.
47
- const NativeDigiaHostView = _reactNative.Platform.OS === 'android' ? (0, _reactNative.requireNativeComponent)('DigiaHostView') : null;
47
+ // guard needed. requireNativeComponent is called unconditionally on iOS and Android.
48
+ const NativeDigiaHostView = _reactNative.Platform.OS === 'android' || _reactNative.Platform.OS === 'ios' ? (0, _reactNative.requireNativeComponent)('DigiaHostView') : null;
48
49
 
49
50
  // ── DigiaHostView ─────────────────────────────────────────────────────────────
50
51
 
51
52
  function DigiaHostView({
52
53
  style
53
54
  }) {
54
- if (_reactNative.Platform.OS === 'android' && NativeDigiaHostView) {
55
- // The native Compose DigiaHost renders dialogs / bottom sheets that
56
- // float above the view hierarchy on their own — the host view only
57
- // needs to be mounted in the tree, not take up any screen space.
55
+ if ((_reactNative.Platform.OS === 'android' || _reactNative.Platform.OS === 'ios') && NativeDigiaHostView) {
56
+ // The native DigiaHost (Compose on Android, SwiftUI on iOS) renders
57
+ // dialogs / bottom sheets that float above the view hierarchy on their
58
+ // own — the host view only needs to be mounted in the tree, not take up
59
+ // any screen space.
58
60
  return /*#__PURE__*/_react.default.createElement(NativeDigiaHostView, {
59
61
  style: _reactNative.StyleSheet.flatten([styles.host, style])
60
62
  });
61
63
  }
62
64
 
63
- // iOS / other platforms: no-op, nothing to mount.
65
+ // Other platforms: no-op.
64
66
  return null;
65
67
  }
66
68
  const styles = _reactNative.StyleSheet.create({
@@ -1 +1 @@
1
- {"version":3,"names":["_react","_interopRequireDefault","require","_reactNative","e","__esModule","default","NativeDigiaHostView","Platform","OS","requireNativeComponent","DigiaHostView","style","createElement","StyleSheet","flatten","styles","host","create","width","height","overflow"],"sourceRoot":"../../src","sources":["DigiaHostView.tsx"],"mappings":";;;;;;AAiCA,IAAAA,MAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AAMsB,SAAAD,uBAAAG,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAxCtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAeA;;AAKA;AACA;AACA,MAAMG,mBAAmB,GACrBC,qBAAQ,CAACC,EAAE,KAAK,SAAS,GACnB,IAAAC,mCAAsB,EAA2B,eAAe,CAAC,GACjE,IAAI;;AAEd;;AAEO,SAASC,aAAaA,CAAC;EAAEC;AAA0B,CAAC,EAAE;EACzD,IAAIJ,qBAAQ,CAACC,EAAE,KAAK,SAAS,IAAIF,mBAAmB,EAAE;IAClD;IACA;IACA;IACA,oBACIP,MAAA,CAAAM,OAAA,CAAAO,aAAA,CAACN,mBAAmB;MAChBK,KAAK,EAAEE,uBAAU,CAACC,OAAO,CAAC,CAACC,MAAM,CAACC,IAAI,EAAEL,KAAK,CAAC;IAAE,CACnD,CAAC;EAEV;;EAEA;EACA,OAAO,IAAI;AACf;AAEA,MAAMI,MAAM,GAAGF,uBAAU,CAACI,MAAM,CAAC;EAC7BD,IAAI,EAAE;IACFE,KAAK,EAAE,CAAC;IACRC,MAAM,EAAE,CAAC;IACTC,QAAQ,EAAE;EACd;AACJ,CAAC,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["_react","_interopRequireDefault","require","_reactNative","e","__esModule","default","NativeDigiaHostView","Platform","OS","requireNativeComponent","DigiaHostView","style","createElement","StyleSheet","flatten","styles","host","create","width","height","overflow"],"sourceRoot":"../../src","sources":["DigiaHostView.tsx"],"mappings":";;;;;;AAkCA,IAAAA,MAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AAMsB,SAAAD,uBAAAG,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAzCtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAeA;;AAKA;AACA;AACA,MAAMG,mBAAmB,GACrBC,qBAAQ,CAACC,EAAE,KAAK,SAAS,IAAID,qBAAQ,CAACC,EAAE,KAAK,KAAK,GAC5C,IAAAC,mCAAsB,EAA2B,eAAe,CAAC,GACjE,IAAI;;AAEd;;AAEO,SAASC,aAAaA,CAAC;EAAEC;AAA0B,CAAC,EAAE;EACzD,IAAI,CAACJ,qBAAQ,CAACC,EAAE,KAAK,SAAS,IAAID,qBAAQ,CAACC,EAAE,KAAK,KAAK,KAAKF,mBAAmB,EAAE;IAC7E;IACA;IACA;IACA;IACA,oBACIP,MAAA,CAAAM,OAAA,CAAAO,aAAA,CAACN,mBAAmB;MAChBK,KAAK,EAAEE,uBAAU,CAACC,OAAO,CAAC,CAACC,MAAM,CAACC,IAAI,EAAEL,KAAK,CAAC;IAAE,CACnD,CAAC;EAEV;;EAEA;EACA,OAAO,IAAI;AACf;AAEA,MAAMI,MAAM,GAAGF,uBAAU,CAACI,MAAM,CAAC;EAC7BD,IAAI,EAAE;IACFE,KAAK,EAAE,CAAC;IACRC,MAAM,EAAE,CAAC;IACTC,QAAQ,EAAE;EACd;AACJ,CAAC,CAAC","ignoreList":[]}
@@ -15,7 +15,8 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
15
15
  *
16
16
  * On Android this mounts a Jetpack Compose `DigiaSlot` composable that
17
17
  * observes the slot payload for the given placement key and renders the
18
- * matching campaign component. On iOS it is a transparent no-op.
18
+ * matching campaign component. On iOS this mounts the equivalent SwiftUI
19
+ * `DigiaSlot` view via UIHostingController.
19
20
  *
20
21
  * ─── Usage ───────────────────────────────────────────────────────────────────
21
22
  * ```tsx
@@ -51,8 +52,8 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
51
52
  */
52
53
 
53
54
  // Fabric (New Architecture) resolves view configs lazily — no UIManager
54
- // guard needed. requireNativeComponent is called unconditionally on Android.
55
- const NativeDigiaSlotView = _reactNative.Platform.OS === 'android' ? (0, _reactNative.requireNativeComponent)('DigiaSlotView') : null;
55
+ // guard needed. requireNativeComponent is called unconditionally on iOS and Android.
56
+ const NativeDigiaSlotView = _reactNative.Platform.OS === 'android' || _reactNative.Platform.OS === 'ios' ? (0, _reactNative.requireNativeComponent)('DigiaSlotView') : null;
56
57
 
57
58
  // ── DigiaSlotView ─────────────────────────────────────────────────────────────
58
59
 
@@ -60,14 +61,14 @@ function DigiaSlotView({
60
61
  placementKey,
61
62
  style
62
63
  }) {
63
- if (_reactNative.Platform.OS === 'android' && NativeDigiaSlotView) {
64
+ if ((_reactNative.Platform.OS === 'android' || _reactNative.Platform.OS === 'ios') && NativeDigiaSlotView) {
64
65
  return /*#__PURE__*/_react.default.createElement(NativeDigiaSlotView, {
65
66
  placementKey: placementKey,
66
67
  style: style
67
68
  });
68
69
  }
69
70
 
70
- // iOS / other platforms: not yet implemented — render nothing.
71
+ // Other platforms: not yet implemented — render nothing.
71
72
  return null;
72
73
  }
73
74
  //# sourceMappingURL=DigiaSlotView.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["_react","_interopRequireDefault","require","_reactNative","e","__esModule","default","NativeDigiaSlotView","Platform","OS","requireNativeComponent","DigiaSlotView","placementKey","style","createElement"],"sourceRoot":"../../src","sources":["DigiaSlotView.tsx"],"mappings":";;;;;;AA2CA,IAAAA,MAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AAKsB,SAAAD,uBAAAG,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAjDtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAgBA;AACA;AACA,MAAMG,mBAAmB,GACrBC,qBAAQ,CAACC,EAAE,KAAK,SAAS,GACnB,IAAAC,mCAAsB,EAAqB,eAAe,CAAC,GAC3D,IAAI;;AAEd;;AAEO,SAASC,aAAaA,CAAC;EAAEC,YAAY;EAAEC;AAA0B,CAAC,EAAE;EACvE,IAAIL,qBAAQ,CAACC,EAAE,KAAK,SAAS,IAAIF,mBAAmB,EAAE;IAClD,oBACIP,MAAA,CAAAM,OAAA,CAAAQ,aAAA,CAACP,mBAAmB;MAChBK,YAAY,EAAEA,YAAa;MAC3BC,KAAK,EAAEA;IAAM,CAChB,CAAC;EAEV;;EAEA;EACA,OAAO,IAAI;AACf","ignoreList":[]}
1
+ {"version":3,"names":["_react","_interopRequireDefault","require","_reactNative","e","__esModule","default","NativeDigiaSlotView","Platform","OS","requireNativeComponent","DigiaSlotView","placementKey","style","createElement"],"sourceRoot":"../../src","sources":["DigiaSlotView.tsx"],"mappings":";;;;;;AA4CA,IAAAA,MAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AAKsB,SAAAD,uBAAAG,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAlDtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAgBA;AACA;AACA,MAAMG,mBAAmB,GACrBC,qBAAQ,CAACC,EAAE,KAAK,SAAS,IAAID,qBAAQ,CAACC,EAAE,KAAK,KAAK,GAC5C,IAAAC,mCAAsB,EAAqB,eAAe,CAAC,GAC3D,IAAI;;AAEd;;AAEO,SAASC,aAAaA,CAAC;EAAEC,YAAY;EAAEC;AAA0B,CAAC,EAAE;EACvE,IAAI,CAACL,qBAAQ,CAACC,EAAE,KAAK,SAAS,IAAID,qBAAQ,CAACC,EAAE,KAAK,KAAK,KAAKF,mBAAmB,EAAE;IAC7E,oBACIP,MAAA,CAAAM,OAAA,CAAAQ,aAAA,CAACP,mBAAmB;MAChBK,YAAY,EAAEA,YAAa;MAC3BC,KAAK,EAAEA;IAAM,CAChB,CAAC;EAEV;;EAEA;EACA,OAAO,IAAI;AACf","ignoreList":[]}
@@ -27,7 +27,8 @@
27
27
  *
28
28
  * On Android this mounts a Jetpack Compose `DigiaHost` composable that
29
29
  * manages dialog + bottom-sheet presentation triggered by CEP plugins.
30
- * On iOS the view is a transparent no-op until iOS support is implemented.
30
+ * On iOS this mounts a SwiftUI `DigiaHost` via UIHostingController that
31
+ * manages the same overlay layer above React Native content.
31
32
  * ─────────────────────────────────────────────────────────────────────────────
32
33
  */
33
34
 
@@ -37,24 +38,25 @@ import { Platform, StyleSheet, requireNativeComponent } from 'react-native';
37
38
  // requireNativeComponent expects a plain ViewStyle, not StyleProp.
38
39
 
39
40
  // Fabric (New Architecture) resolves view configs lazily — no UIManager
40
- // guard needed. requireNativeComponent is called unconditionally on Android.
41
- const NativeDigiaHostView = Platform.OS === 'android' ? requireNativeComponent('DigiaHostView') : null;
41
+ // guard needed. requireNativeComponent is called unconditionally on iOS and Android.
42
+ const NativeDigiaHostView = Platform.OS === 'android' || Platform.OS === 'ios' ? requireNativeComponent('DigiaHostView') : null;
42
43
 
43
44
  // ── DigiaHostView ─────────────────────────────────────────────────────────────
44
45
 
45
46
  export function DigiaHostView({
46
47
  style
47
48
  }) {
48
- if (Platform.OS === 'android' && NativeDigiaHostView) {
49
- // The native Compose DigiaHost renders dialogs / bottom sheets that
50
- // float above the view hierarchy on their own — the host view only
51
- // needs to be mounted in the tree, not take up any screen space.
49
+ if ((Platform.OS === 'android' || Platform.OS === 'ios') && NativeDigiaHostView) {
50
+ // The native DigiaHost (Compose on Android, SwiftUI on iOS) renders
51
+ // dialogs / bottom sheets that float above the view hierarchy on their
52
+ // own — the host view only needs to be mounted in the tree, not take up
53
+ // any screen space.
52
54
  return /*#__PURE__*/React.createElement(NativeDigiaHostView, {
53
55
  style: StyleSheet.flatten([styles.host, style])
54
56
  });
55
57
  }
56
58
 
57
- // iOS / other platforms: no-op, nothing to mount.
59
+ // Other platforms: no-op.
58
60
  return null;
59
61
  }
60
62
  const styles = StyleSheet.create({
@@ -1 +1 @@
1
- {"version":3,"names":["React","Platform","StyleSheet","requireNativeComponent","NativeDigiaHostView","OS","DigiaHostView","style","createElement","flatten","styles","host","create","width","height","overflow"],"sourceRoot":"../../src","sources":["DigiaHostView.tsx"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,OAAOA,KAAK,MAAM,OAAO;AACzB,SACIC,QAAQ,EACRC,UAAU,EACVC,sBAAsB,QAGnB,cAAc;;AAMrB;;AAKA;AACA;AACA,MAAMC,mBAAmB,GACrBH,QAAQ,CAACI,EAAE,KAAK,SAAS,GACnBF,sBAAsB,CAA2B,eAAe,CAAC,GACjE,IAAI;;AAEd;;AAEA,OAAO,SAASG,aAAaA,CAAC;EAAEC;AAA0B,CAAC,EAAE;EACzD,IAAIN,QAAQ,CAACI,EAAE,KAAK,SAAS,IAAID,mBAAmB,EAAE;IAClD;IACA;IACA;IACA,oBACIJ,KAAA,CAAAQ,aAAA,CAACJ,mBAAmB;MAChBG,KAAK,EAAEL,UAAU,CAACO,OAAO,CAAC,CAACC,MAAM,CAACC,IAAI,EAAEJ,KAAK,CAAC;IAAE,CACnD,CAAC;EAEV;;EAEA;EACA,OAAO,IAAI;AACf;AAEA,MAAMG,MAAM,GAAGR,UAAU,CAACU,MAAM,CAAC;EAC7BD,IAAI,EAAE;IACFE,KAAK,EAAE,CAAC;IACRC,MAAM,EAAE,CAAC;IACTC,QAAQ,EAAE;EACd;AACJ,CAAC,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["React","Platform","StyleSheet","requireNativeComponent","NativeDigiaHostView","OS","DigiaHostView","style","createElement","flatten","styles","host","create","width","height","overflow"],"sourceRoot":"../../src","sources":["DigiaHostView.tsx"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,OAAOA,KAAK,MAAM,OAAO;AACzB,SACIC,QAAQ,EACRC,UAAU,EACVC,sBAAsB,QAGnB,cAAc;;AAMrB;;AAKA;AACA;AACA,MAAMC,mBAAmB,GACrBH,QAAQ,CAACI,EAAE,KAAK,SAAS,IAAIJ,QAAQ,CAACI,EAAE,KAAK,KAAK,GAC5CF,sBAAsB,CAA2B,eAAe,CAAC,GACjE,IAAI;;AAEd;;AAEA,OAAO,SAASG,aAAaA,CAAC;EAAEC;AAA0B,CAAC,EAAE;EACzD,IAAI,CAACN,QAAQ,CAACI,EAAE,KAAK,SAAS,IAAIJ,QAAQ,CAACI,EAAE,KAAK,KAAK,KAAKD,mBAAmB,EAAE;IAC7E;IACA;IACA;IACA;IACA,oBACIJ,KAAA,CAAAQ,aAAA,CAACJ,mBAAmB;MAChBG,KAAK,EAAEL,UAAU,CAACO,OAAO,CAAC,CAACC,MAAM,CAACC,IAAI,EAAEJ,KAAK,CAAC;IAAE,CACnD,CAAC;EAEV;;EAEA;EACA,OAAO,IAAI;AACf;AAEA,MAAMG,MAAM,GAAGR,UAAU,CAACU,MAAM,CAAC;EAC7BD,IAAI,EAAE;IACFE,KAAK,EAAE,CAAC;IACRC,MAAM,EAAE,CAAC;IACTC,QAAQ,EAAE;EACd;AACJ,CAAC,CAAC","ignoreList":[]}
@@ -6,7 +6,8 @@
6
6
  *
7
7
  * On Android this mounts a Jetpack Compose `DigiaSlot` composable that
8
8
  * observes the slot payload for the given placement key and renders the
9
- * matching campaign component. On iOS it is a transparent no-op.
9
+ * matching campaign component. On iOS this mounts the equivalent SwiftUI
10
+ * `DigiaSlot` view via UIHostingController.
10
11
  *
11
12
  * ─── Usage ───────────────────────────────────────────────────────────────────
12
13
  * ```tsx
@@ -44,8 +45,8 @@
44
45
  import React from 'react';
45
46
  import { Platform, requireNativeComponent } from 'react-native';
46
47
  // Fabric (New Architecture) resolves view configs lazily — no UIManager
47
- // guard needed. requireNativeComponent is called unconditionally on Android.
48
- const NativeDigiaSlotView = Platform.OS === 'android' ? requireNativeComponent('DigiaSlotView') : null;
48
+ // guard needed. requireNativeComponent is called unconditionally on iOS and Android.
49
+ const NativeDigiaSlotView = Platform.OS === 'android' || Platform.OS === 'ios' ? requireNativeComponent('DigiaSlotView') : null;
49
50
 
50
51
  // ── DigiaSlotView ─────────────────────────────────────────────────────────────
51
52
 
@@ -53,14 +54,14 @@ export function DigiaSlotView({
53
54
  placementKey,
54
55
  style
55
56
  }) {
56
- if (Platform.OS === 'android' && NativeDigiaSlotView) {
57
+ if ((Platform.OS === 'android' || Platform.OS === 'ios') && NativeDigiaSlotView) {
57
58
  return /*#__PURE__*/React.createElement(NativeDigiaSlotView, {
58
59
  placementKey: placementKey,
59
60
  style: style
60
61
  });
61
62
  }
62
63
 
63
- // iOS / other platforms: not yet implemented — render nothing.
64
+ // Other platforms: not yet implemented — render nothing.
64
65
  return null;
65
66
  }
66
67
  //# sourceMappingURL=DigiaSlotView.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["React","Platform","requireNativeComponent","NativeDigiaSlotView","OS","DigiaSlotView","placementKey","style","createElement"],"sourceRoot":"../../src","sources":["DigiaSlotView.tsx"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,OAAOA,KAAK,MAAM,OAAO;AACzB,SACIC,QAAQ,EACRC,sBAAsB,QAGnB,cAAc;AAQrB;AACA;AACA,MAAMC,mBAAmB,GACrBF,QAAQ,CAACG,EAAE,KAAK,SAAS,GACnBF,sBAAsB,CAAqB,eAAe,CAAC,GAC3D,IAAI;;AAEd;;AAEA,OAAO,SAASG,aAAaA,CAAC;EAAEC,YAAY;EAAEC;AAA0B,CAAC,EAAE;EACvE,IAAIN,QAAQ,CAACG,EAAE,KAAK,SAAS,IAAID,mBAAmB,EAAE;IAClD,oBACIH,KAAA,CAAAQ,aAAA,CAACL,mBAAmB;MAChBG,YAAY,EAAEA,YAAa;MAC3BC,KAAK,EAAEA;IAAM,CAChB,CAAC;EAEV;;EAEA;EACA,OAAO,IAAI;AACf","ignoreList":[]}
1
+ {"version":3,"names":["React","Platform","requireNativeComponent","NativeDigiaSlotView","OS","DigiaSlotView","placementKey","style","createElement"],"sourceRoot":"../../src","sources":["DigiaSlotView.tsx"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,OAAOA,KAAK,MAAM,OAAO;AACzB,SACIC,QAAQ,EACRC,sBAAsB,QAGnB,cAAc;AAQrB;AACA;AACA,MAAMC,mBAAmB,GACrBF,QAAQ,CAACG,EAAE,KAAK,SAAS,IAAIH,QAAQ,CAACG,EAAE,KAAK,KAAK,GAC5CF,sBAAsB,CAAqB,eAAe,CAAC,GAC3D,IAAI;;AAEd;;AAEA,OAAO,SAASG,aAAaA,CAAC;EAAEC,YAAY;EAAEC;AAA0B,CAAC,EAAE;EACvE,IAAI,CAACN,QAAQ,CAACG,EAAE,KAAK,SAAS,IAAIH,QAAQ,CAACG,EAAE,KAAK,KAAK,KAAKD,mBAAmB,EAAE;IAC7E,oBACIH,KAAA,CAAAQ,aAAA,CAACL,mBAAmB;MAChBG,YAAY,EAAEA,YAAa;MAC3BC,KAAK,EAAEA;IAAM,CAChB,CAAC;EAEV;;EAEA;EACA,OAAO,IAAI;AACf","ignoreList":[]}
@@ -27,7 +27,8 @@
27
27
  *
28
28
  * On Android this mounts a Jetpack Compose `DigiaHost` composable that
29
29
  * manages dialog + bottom-sheet presentation triggered by CEP plugins.
30
- * On iOS the view is a transparent no-op until iOS support is implemented.
30
+ * On iOS this mounts a SwiftUI `DigiaHost` via UIHostingController that
31
+ * manages the same overlay layer above React Native content.
31
32
  * ─────────────────────────────────────────────────────────────────────────────
32
33
  */
33
34
  import React from 'react';
@@ -1 +1 @@
1
- {"version":3,"file":"DigiaHostView.d.ts","sourceRoot":"","sources":["../../src/DigiaHostView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAIH,KAAK,SAAS,EACd,KAAK,SAAS,EACjB,MAAM,cAAc,CAAC;AAEtB,UAAU,kBAAkB;IACxB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAChC;AAgBD,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,EAAE,kBAAkB,4BAc1D"}
1
+ {"version":3,"file":"DigiaHostView.d.ts","sourceRoot":"","sources":["../../src/DigiaHostView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAIH,KAAK,SAAS,EACd,KAAK,SAAS,EACjB,MAAM,cAAc,CAAC;AAEtB,UAAU,kBAAkB;IACxB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAChC;AAgBD,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,EAAE,kBAAkB,4BAe1D"}
@@ -6,7 +6,8 @@
6
6
  *
7
7
  * On Android this mounts a Jetpack Compose `DigiaSlot` composable that
8
8
  * observes the slot payload for the given placement key and renders the
9
- * matching campaign component. On iOS it is a transparent no-op.
9
+ * matching campaign component. On iOS this mounts the equivalent SwiftUI
10
+ * `DigiaSlot` view via UIHostingController.
10
11
  *
11
12
  * ─── Usage ───────────────────────────────────────────────────────────────────
12
13
  * ```tsx
@@ -1 +1 @@
1
- {"version":3,"file":"DigiaSlotView.d.ts","sourceRoot":"","sources":["../../src/DigiaSlotView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAGH,KAAK,SAAS,EACd,KAAK,SAAS,EACjB,MAAM,cAAc,CAAC;AAEtB,UAAU,kBAAkB;IACxB,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAChC;AAWD,wBAAgB,aAAa,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE,kBAAkB,4BAYxE"}
1
+ {"version":3,"file":"DigiaSlotView.d.ts","sourceRoot":"","sources":["../../src/DigiaSlotView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAGH,KAAK,SAAS,EACd,KAAK,SAAS,EACjB,MAAM,cAAc,CAAC;AAEtB,UAAU,kBAAkB;IACxB,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAChC;AAWD,wBAAgB,aAAa,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE,kBAAkB,4BAYxE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digia-engage/core",
3
- "version": "1.0.0-beta.3",
3
+ "version": "1.0.0-beta.5",
4
4
  "description": "React Native bridge for Digia Engage – renders native Android Compose UI inside React Native apps",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -27,7 +27,8 @@
27
27
  *
28
28
  * On Android this mounts a Jetpack Compose `DigiaHost` composable that
29
29
  * manages dialog + bottom-sheet presentation triggered by CEP plugins.
30
- * On iOS the view is a transparent no-op until iOS support is implemented.
30
+ * On iOS this mounts a SwiftUI `DigiaHost` via UIHostingController that
31
+ * manages the same overlay layer above React Native content.
31
32
  * ─────────────────────────────────────────────────────────────────────────────
32
33
  */
33
34
 
@@ -50,19 +51,20 @@ interface NativeDigiaHostViewProps {
50
51
  }
51
52
 
52
53
  // Fabric (New Architecture) resolves view configs lazily — no UIManager
53
- // guard needed. requireNativeComponent is called unconditionally on Android.
54
+ // guard needed. requireNativeComponent is called unconditionally on iOS and Android.
54
55
  const NativeDigiaHostView =
55
- Platform.OS === 'android'
56
+ Platform.OS === 'android' || Platform.OS === 'ios'
56
57
  ? requireNativeComponent<NativeDigiaHostViewProps>('DigiaHostView')
57
58
  : null;
58
59
 
59
60
  // ── DigiaHostView ─────────────────────────────────────────────────────────────
60
61
 
61
62
  export function DigiaHostView({ style }: DigiaHostViewProps) {
62
- if (Platform.OS === 'android' && NativeDigiaHostView) {
63
- // The native Compose DigiaHost renders dialogs / bottom sheets that
64
- // float above the view hierarchy on their own — the host view only
65
- // needs to be mounted in the tree, not take up any screen space.
63
+ if ((Platform.OS === 'android' || Platform.OS === 'ios') && NativeDigiaHostView) {
64
+ // The native DigiaHost (Compose on Android, SwiftUI on iOS) renders
65
+ // dialogs / bottom sheets that float above the view hierarchy on their
66
+ // own — the host view only needs to be mounted in the tree, not take up
67
+ // any screen space.
66
68
  return (
67
69
  <NativeDigiaHostView
68
70
  style={StyleSheet.flatten([styles.host, style])}
@@ -70,7 +72,7 @@ export function DigiaHostView({ style }: DigiaHostViewProps) {
70
72
  );
71
73
  }
72
74
 
73
- // iOS / other platforms: no-op, nothing to mount.
75
+ // Other platforms: no-op.
74
76
  return null;
75
77
  }
76
78
 
@@ -6,7 +6,8 @@
6
6
  *
7
7
  * On Android this mounts a Jetpack Compose `DigiaSlot` composable that
8
8
  * observes the slot payload for the given placement key and renders the
9
- * matching campaign component. On iOS it is a transparent no-op.
9
+ * matching campaign component. On iOS this mounts the equivalent SwiftUI
10
+ * `DigiaSlot` view via UIHostingController.
10
11
  *
11
12
  * ─── Usage ───────────────────────────────────────────────────────────────────
12
13
  * ```tsx
@@ -56,16 +57,16 @@ interface DigiaSlotViewProps {
56
57
  }
57
58
 
58
59
  // Fabric (New Architecture) resolves view configs lazily — no UIManager
59
- // guard needed. requireNativeComponent is called unconditionally on Android.
60
+ // guard needed. requireNativeComponent is called unconditionally on iOS and Android.
60
61
  const NativeDigiaSlotView =
61
- Platform.OS === 'android'
62
+ Platform.OS === 'android' || Platform.OS === 'ios'
62
63
  ? requireNativeComponent<DigiaSlotViewProps>('DigiaSlotView')
63
64
  : null;
64
65
 
65
66
  // ── DigiaSlotView ─────────────────────────────────────────────────────────────
66
67
 
67
68
  export function DigiaSlotView({ placementKey, style }: DigiaSlotViewProps) {
68
- if (Platform.OS === 'android' && NativeDigiaSlotView) {
69
+ if ((Platform.OS === 'android' || Platform.OS === 'ios') && NativeDigiaSlotView) {
69
70
  return (
70
71
  <NativeDigiaSlotView
71
72
  placementKey={placementKey}
@@ -74,6 +75,6 @@ export function DigiaSlotView({ placementKey, style }: DigiaSlotViewProps) {
74
75
  );
75
76
  }
76
77
 
77
- // iOS / other platforms: not yet implemented — render nothing.
78
+ // Other platforms: not yet implemented — render nothing.
78
79
  return null;
79
80
  }