react-native-daro 1.0.15 → 1.0.16

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.
Files changed (192) hide show
  1. package/android/build.gradle +11 -1
  2. package/android/src/main/java/com/daro/reactnative/DaroMAdBannerViewManager.kt +34 -11
  3. package/android/src/main/java/com/daro/reactnative/DaroMAdNativeViewManager.kt +79 -48
  4. package/android/src/main/java/com/daro/reactnative/DaroMModule.kt +80 -25
  5. package/android/src/main/java/com/daro/reactnative/DaroMPackage.kt +28 -4
  6. package/android/src/main/java/com/daro/reactnative/DaroMediaViewManager.kt +25 -0
  7. package/android/src/main/java/com/daro/reactnative/event/DaroMEvent.kt +18 -7
  8. package/android/src/main/java/com/daro/reactnative/util/EventEmitterUtil.kt +18 -5
  9. package/android/src/main/java/com/daro/reactnative/view/DaroMAdNativeViewContainer.kt +301 -52
  10. package/android/src/main/java/com/daro/reactnative/view/DaroMediaView.kt +123 -0
  11. package/android/src/newarch/java/com/daro/reactnative/DaroMModuleSpec.kt +6 -0
  12. package/android/src/oldarch/java/com/daro/reactnative/DaroMModuleSpec.kt +90 -0
  13. package/ios/DaroMModule+AppOpen.swift +13 -10
  14. package/ios/DaroMModule+Interstitial.swift +12 -9
  15. package/ios/DaroMModule+LightPopup.swift +16 -13
  16. package/ios/DaroMModule+Rewarded.swift +12 -9
  17. package/ios/DaroMModule.mm +45 -17
  18. package/ios/DaroMModule.swift +4 -4
  19. package/ios/Fabric/DaroMAdBannerComponentView.mm +207 -0
  20. package/ios/Fabric/DaroMFabricEventSink.mm +31 -0
  21. package/ios/Fabric/DaroMFabricPropApplier.mm +123 -0
  22. package/ios/Fabric/DaroMNativeAdComponentView.mm +294 -0
  23. package/ios/{DaroMAdBannerViewManager.m → Paper/DaroMAdBannerViewManager.m} +3 -1
  24. package/ios/Paper/DaroMAdBannerViewManager.swift +21 -0
  25. package/ios/{DaroMNativeAdViewManager.m → Paper/DaroMNativeAdViewManager.m} +2 -1
  26. package/ios/Paper/DaroMNativeAdViewManager.swift +22 -0
  27. package/ios/Shared/DaroMAdEventSink.swift +24 -0
  28. package/ios/Shared/DaroMBannerAdView.swift +188 -0
  29. package/ios/Shared/DaroMNativeAdLoadCoordinator.swift +49 -0
  30. package/ios/Shared/DaroMNativeAdView.swift +575 -0
  31. package/ios/Shared/DaroMNativeAssetResolver.swift +53 -0
  32. package/ios/mediation/admob/DaroMediationTypes.swift +1 -0
  33. package/lib/commonjs/AdBannerView.js +20 -39
  34. package/lib/commonjs/AdBannerView.js.map +1 -1
  35. package/lib/commonjs/AppOpenAd.js +43 -10
  36. package/lib/commonjs/AppOpenAd.js.map +1 -1
  37. package/lib/commonjs/DaroMAdBannerViewNativeComponent.ts +47 -0
  38. package/lib/commonjs/DaroMNativeAdViewNativeComponent.ts +62 -0
  39. package/lib/commonjs/DaroMediaViewNativeComponent.ts +9 -0
  40. package/lib/commonjs/EventEmitter.js +4 -5
  41. package/lib/commonjs/EventEmitter.js.map +1 -1
  42. package/lib/commonjs/InterstitialAd.js +9 -11
  43. package/lib/commonjs/InterstitialAd.js.map +1 -1
  44. package/lib/commonjs/LightPopupAd.js +11 -12
  45. package/lib/commonjs/LightPopupAd.js.map +1 -1
  46. package/lib/commonjs/NativeDaroModule.js +9 -0
  47. package/lib/commonjs/NativeDaroModule.js.map +1 -0
  48. package/lib/commonjs/RewardedAd.js +9 -11
  49. package/lib/commonjs/RewardedAd.js.map +1 -1
  50. package/lib/commonjs/codegen-env.d.js +2 -0
  51. package/lib/commonjs/codegen-env.d.js.map +1 -0
  52. package/lib/commonjs/hooks/useInterstitialAd.js +110 -0
  53. package/lib/commonjs/hooks/useInterstitialAd.js.map +1 -0
  54. package/lib/commonjs/hooks/useLightPopupAd.js +114 -0
  55. package/lib/commonjs/hooks/useLightPopupAd.js.map +1 -0
  56. package/lib/commonjs/hooks/useRewardedAd.js +113 -0
  57. package/lib/commonjs/hooks/useRewardedAd.js.map +1 -0
  58. package/lib/commonjs/index.js +29 -13
  59. package/lib/commonjs/index.js.map +1 -1
  60. package/lib/commonjs/nativeAd/NativeAdView.js +23 -22
  61. package/lib/commonjs/nativeAd/NativeAdView.js.map +1 -1
  62. package/lib/commonjs/nativeAd/NativeAdViewComponents.js +43 -31
  63. package/lib/commonjs/nativeAd/NativeAdViewComponents.js.map +1 -1
  64. package/lib/commonjs/nativeAd/NativeAdViewProvider.js +34 -7
  65. package/lib/commonjs/nativeAd/NativeAdViewProvider.js.map +1 -1
  66. package/lib/module/AdBannerView.js +19 -40
  67. package/lib/module/AdBannerView.js.map +1 -1
  68. package/lib/module/AppOpenAd.js +43 -11
  69. package/lib/module/AppOpenAd.js.map +1 -1
  70. package/lib/module/DaroMAdBannerViewNativeComponent.ts +47 -0
  71. package/lib/module/DaroMNativeAdViewNativeComponent.ts +62 -0
  72. package/lib/module/DaroMediaViewNativeComponent.ts +9 -0
  73. package/lib/module/EventEmitter.js +4 -6
  74. package/lib/module/EventEmitter.js.map +1 -1
  75. package/lib/module/InterstitialAd.js +8 -11
  76. package/lib/module/InterstitialAd.js.map +1 -1
  77. package/lib/module/LightPopupAd.js +11 -13
  78. package/lib/module/LightPopupAd.js.map +1 -1
  79. package/lib/module/NativeDaroModule.js +5 -0
  80. package/lib/module/NativeDaroModule.js.map +1 -0
  81. package/lib/module/RewardedAd.js +8 -11
  82. package/lib/module/RewardedAd.js.map +1 -1
  83. package/lib/module/codegen-env.d.js +2 -0
  84. package/lib/module/codegen-env.d.js.map +1 -0
  85. package/lib/module/hooks/useInterstitialAd.js +106 -0
  86. package/lib/module/hooks/useInterstitialAd.js.map +1 -0
  87. package/lib/module/hooks/useLightPopupAd.js +110 -0
  88. package/lib/module/hooks/useLightPopupAd.js.map +1 -0
  89. package/lib/module/hooks/useRewardedAd.js +109 -0
  90. package/lib/module/hooks/useRewardedAd.js.map +1 -0
  91. package/lib/module/index.js +7 -13
  92. package/lib/module/index.js.map +1 -1
  93. package/lib/module/nativeAd/NativeAdView.js +22 -23
  94. package/lib/module/nativeAd/NativeAdView.js.map +1 -1
  95. package/lib/module/nativeAd/NativeAdViewComponents.js +45 -34
  96. package/lib/module/nativeAd/NativeAdViewComponents.js.map +1 -1
  97. package/lib/module/nativeAd/NativeAdViewProvider.js +35 -8
  98. package/lib/module/nativeAd/NativeAdViewProvider.js.map +1 -1
  99. package/lib/typescript/commonjs/plugin/src/index.d.ts +1 -1
  100. package/lib/typescript/commonjs/plugin/src/index.d.ts.map +1 -1
  101. package/lib/typescript/commonjs/plugin/src/withAndroid.d.ts.map +1 -1
  102. package/lib/typescript/commonjs/plugin/src/withIos.d.ts.map +1 -1
  103. package/lib/typescript/commonjs/src/AdBannerView.d.ts +2 -2
  104. package/lib/typescript/commonjs/src/AdBannerView.d.ts.map +1 -1
  105. package/lib/typescript/commonjs/src/AppOpenAd.d.ts.map +1 -1
  106. package/lib/typescript/commonjs/src/DaroMAdBannerViewNativeComponent.d.ts +32 -0
  107. package/lib/typescript/commonjs/src/DaroMAdBannerViewNativeComponent.d.ts.map +1 -0
  108. package/lib/typescript/commonjs/src/DaroMNativeAdViewNativeComponent.d.ts +42 -0
  109. package/lib/typescript/commonjs/src/DaroMNativeAdViewNativeComponent.d.ts.map +1 -0
  110. package/lib/typescript/commonjs/src/DaroMediaViewNativeComponent.d.ts +6 -0
  111. package/lib/typescript/commonjs/src/DaroMediaViewNativeComponent.d.ts.map +1 -0
  112. package/lib/typescript/commonjs/src/EventEmitter.d.ts.map +1 -1
  113. package/lib/typescript/commonjs/src/InterstitialAd.d.ts.map +1 -1
  114. package/lib/typescript/commonjs/src/LightPopupAd.d.ts.map +1 -1
  115. package/lib/typescript/commonjs/src/NativeDaroModule.d.ts +61 -0
  116. package/lib/typescript/commonjs/src/NativeDaroModule.d.ts.map +1 -0
  117. package/lib/typescript/commonjs/src/RewardedAd.d.ts.map +1 -1
  118. package/lib/typescript/commonjs/src/hooks/useInterstitialAd.d.ts +19 -0
  119. package/lib/typescript/commonjs/src/hooks/useInterstitialAd.d.ts.map +1 -0
  120. package/lib/typescript/commonjs/src/hooks/useLightPopupAd.d.ts +23 -0
  121. package/lib/typescript/commonjs/src/hooks/useLightPopupAd.d.ts.map +1 -0
  122. package/lib/typescript/commonjs/src/hooks/useRewardedAd.d.ts +20 -0
  123. package/lib/typescript/commonjs/src/hooks/useRewardedAd.d.ts.map +1 -0
  124. package/lib/typescript/commonjs/src/index.d.ts +7 -1
  125. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  126. package/lib/typescript/commonjs/src/nativeAd/NativeAdView.d.ts +1 -0
  127. package/lib/typescript/commonjs/src/nativeAd/NativeAdView.d.ts.map +1 -1
  128. package/lib/typescript/commonjs/src/nativeAd/NativeAdViewComponents.d.ts.map +1 -1
  129. package/lib/typescript/commonjs/src/nativeAd/NativeAdViewProvider.d.ts +11 -5
  130. package/lib/typescript/commonjs/src/nativeAd/NativeAdViewProvider.d.ts.map +1 -1
  131. package/lib/typescript/commonjs/src/types/NativeAdViewProps.d.ts +2 -0
  132. package/lib/typescript/commonjs/src/types/NativeAdViewProps.d.ts.map +1 -1
  133. package/lib/typescript/module/plugin/src/index.d.ts +1 -1
  134. package/lib/typescript/module/plugin/src/index.d.ts.map +1 -1
  135. package/lib/typescript/module/plugin/src/withAndroid.d.ts.map +1 -1
  136. package/lib/typescript/module/plugin/src/withIos.d.ts.map +1 -1
  137. package/lib/typescript/module/src/AdBannerView.d.ts +2 -2
  138. package/lib/typescript/module/src/AdBannerView.d.ts.map +1 -1
  139. package/lib/typescript/module/src/AppOpenAd.d.ts.map +1 -1
  140. package/lib/typescript/module/src/DaroMAdBannerViewNativeComponent.d.ts +32 -0
  141. package/lib/typescript/module/src/DaroMAdBannerViewNativeComponent.d.ts.map +1 -0
  142. package/lib/typescript/module/src/DaroMNativeAdViewNativeComponent.d.ts +42 -0
  143. package/lib/typescript/module/src/DaroMNativeAdViewNativeComponent.d.ts.map +1 -0
  144. package/lib/typescript/module/src/DaroMediaViewNativeComponent.d.ts +6 -0
  145. package/lib/typescript/module/src/DaroMediaViewNativeComponent.d.ts.map +1 -0
  146. package/lib/typescript/module/src/EventEmitter.d.ts.map +1 -1
  147. package/lib/typescript/module/src/InterstitialAd.d.ts.map +1 -1
  148. package/lib/typescript/module/src/LightPopupAd.d.ts.map +1 -1
  149. package/lib/typescript/module/src/NativeDaroModule.d.ts +61 -0
  150. package/lib/typescript/module/src/NativeDaroModule.d.ts.map +1 -0
  151. package/lib/typescript/module/src/RewardedAd.d.ts.map +1 -1
  152. package/lib/typescript/module/src/hooks/useInterstitialAd.d.ts +19 -0
  153. package/lib/typescript/module/src/hooks/useInterstitialAd.d.ts.map +1 -0
  154. package/lib/typescript/module/src/hooks/useLightPopupAd.d.ts +23 -0
  155. package/lib/typescript/module/src/hooks/useLightPopupAd.d.ts.map +1 -0
  156. package/lib/typescript/module/src/hooks/useRewardedAd.d.ts +20 -0
  157. package/lib/typescript/module/src/hooks/useRewardedAd.d.ts.map +1 -0
  158. package/lib/typescript/module/src/index.d.ts +7 -1
  159. package/lib/typescript/module/src/index.d.ts.map +1 -1
  160. package/lib/typescript/module/src/nativeAd/NativeAdView.d.ts +1 -0
  161. package/lib/typescript/module/src/nativeAd/NativeAdView.d.ts.map +1 -1
  162. package/lib/typescript/module/src/nativeAd/NativeAdViewComponents.d.ts.map +1 -1
  163. package/lib/typescript/module/src/nativeAd/NativeAdViewProvider.d.ts +11 -5
  164. package/lib/typescript/module/src/nativeAd/NativeAdViewProvider.d.ts.map +1 -1
  165. package/lib/typescript/module/src/types/NativeAdViewProps.d.ts +2 -0
  166. package/lib/typescript/module/src/types/NativeAdViewProps.d.ts.map +1 -1
  167. package/package.json +12 -5
  168. package/plugin/build/index.d.ts +1 -1
  169. package/plugin/build/withAndroid.js +99 -17
  170. package/plugin/build/withIos.js +4 -5
  171. package/react-native-daro.podspec +3 -0
  172. package/src/AdBannerView.tsx +24 -61
  173. package/src/AppOpenAd.ts +46 -9
  174. package/src/DaroMAdBannerViewNativeComponent.ts +47 -0
  175. package/src/DaroMNativeAdViewNativeComponent.ts +62 -0
  176. package/src/DaroMediaViewNativeComponent.ts +9 -0
  177. package/src/EventEmitter.ts +4 -5
  178. package/src/InterstitialAd.ts +8 -10
  179. package/src/LightPopupAd.ts +11 -12
  180. package/src/NativeDaroModule.ts +91 -0
  181. package/src/RewardedAd.ts +8 -10
  182. package/src/codegen-env.d.ts +45 -0
  183. package/src/hooks/useInterstitialAd.ts +117 -0
  184. package/src/hooks/useLightPopupAd.ts +132 -0
  185. package/src/hooks/useRewardedAd.ts +122 -0
  186. package/src/index.tsx +21 -22
  187. package/src/nativeAd/NativeAdView.tsx +41 -54
  188. package/src/nativeAd/NativeAdViewComponents.tsx +80 -42
  189. package/src/nativeAd/NativeAdViewProvider.tsx +75 -17
  190. package/src/types/NativeAdViewProps.ts +7 -0
  191. package/ios/DaroMAdBannerViewManager.swift +0 -91
  192. package/ios/DaroMNativeAdViewManager.swift +0 -291
@@ -0,0 +1,575 @@
1
+ import React
2
+ import UIKit
3
+
4
+ @objc(RNDaroMNativeAdView)
5
+ final class RNDaroMNativeAdView: UIView, DaroMNativeAdRenderDelegate {
6
+
7
+ private static let assetLoadRetryDelay: TimeInterval = 0.05
8
+ private static let maxAssetLoadRetryCount = 40
9
+
10
+ var _internalAdView: DaroMBridgeNativeAdView?
11
+ private var pinnedConstraints: [ObjectIdentifier: [NSLayoutConstraint]] = [:]
12
+ private var pendingLoadWorkItem: DispatchWorkItem?
13
+ private let loadCoordinator = DaroMNativeAdLoadCoordinator()
14
+ private var defaultAssetResolver: DaroMNativeAssetResolving?
15
+ private var assetResolver: DaroMNativeAssetResolving?
16
+ private let eventSink = DaroMAdEventSink()
17
+ var adUnit: DaroAdUnit
18
+
19
+ @objc var adUnitId: String?
20
+ @objc var loadOnMount: Bool = true
21
+ @objc var titleViewStyleProps: NSDictionary? {
22
+ didSet {
23
+ _internalTitleLabel.applyStyle(titleViewStyleProps)
24
+ }
25
+ }
26
+ private var _titleView: NSNumber?
27
+ @objc var titleView: NSNumber? {
28
+ get { _titleView }
29
+ set { _titleView = Self.normalizedAssetTag(newValue) }
30
+ }
31
+ @objc var bodyViewStyleProps: NSDictionary? {
32
+ didSet {
33
+ _internalBodyLabel.applyStyle(bodyViewStyleProps)
34
+ }
35
+ }
36
+ private var _bodyView: NSNumber?
37
+ @objc var bodyView: NSNumber? {
38
+ get { _bodyView }
39
+ set { _bodyView = Self.normalizedAssetTag(newValue) }
40
+ }
41
+ @objc var callToActionViewStyleProps: NSDictionary? {
42
+ didSet {
43
+ _internalFakeCTAButton.applyStyle(callToActionViewStyleProps)
44
+ }
45
+ }
46
+ private var _callToActionView: NSNumber?
47
+ @objc var callToActionView: NSNumber? {
48
+ get { _callToActionView }
49
+ set { _callToActionView = Self.normalizedAssetTag(newValue) }
50
+ }
51
+ @objc var iconViewStyleProps: NSDictionary? {
52
+ didSet {
53
+ _internalIconImageView.applyStyle(iconViewStyleProps)
54
+ }
55
+ }
56
+ private var _iconView: NSNumber?
57
+ @objc var iconView: NSNumber? {
58
+ get { _iconView }
59
+ set { _iconView = Self.normalizedAssetTag(newValue) }
60
+ }
61
+ @objc var mediaViewStyleProps: NSDictionary? {
62
+ didSet {
63
+ _internalMediaView.baseApplyStyle(mediaViewStyleProps)
64
+ }
65
+ }
66
+ private var _mediaView: NSNumber?
67
+ @objc var mediaView: NSNumber? {
68
+ get { _mediaView }
69
+ set { _mediaView = Self.normalizedAssetTag(newValue) }
70
+ }
71
+ @objc var adChoicesPosition: String?
72
+
73
+ var _internalTitleLabel: UILabel = RNDaroMNativeAdView.makeTitleLabel()
74
+ var _internalBodyLabel: UILabel = RNDaroMNativeAdView.makeBodyLabel()
75
+ var _internalMediaView: UIView = RNDaroMNativeAdView.makeMediaView()
76
+ var _internalIconImageView: UIImageView = RNDaroMNativeAdView.makeIconImageView()
77
+ var _internalFakeCTAButton: UIButton = RNDaroMNativeAdView.makeFakeCTAButton()
78
+
79
+ private static func makeTitleLabel() -> UILabel {
80
+ let label = UILabel()
81
+ label.isUserInteractionEnabled = false
82
+ label.clipsToBounds = true
83
+ label.font = .systemFont(ofSize: 15, weight: .bold)
84
+ label.textColor = UIColor.black
85
+ return label
86
+ }
87
+
88
+ private static func makeBodyLabel() -> UILabel {
89
+ let label = UILabel()
90
+ label.isUserInteractionEnabled = false
91
+ label.clipsToBounds = true
92
+ label.font = .systemFont(ofSize: 15, weight: .regular)
93
+ label.textColor = UIColor.black
94
+ return label
95
+ }
96
+
97
+ private static func makeMediaView() -> UIView {
98
+ UIView()
99
+ }
100
+
101
+ private static func makeIconImageView() -> UIImageView {
102
+ let view = UIImageView()
103
+ view.contentMode = .scaleAspectFill
104
+ return view
105
+ }
106
+
107
+ private static func makeFakeCTAButton() -> UIButton {
108
+ let button = UIButton()
109
+ button.translatesAutoresizingMaskIntoConstraints = false
110
+ button.isUserInteractionEnabled = false
111
+ button.setTitleColor(.white, for: .normal)
112
+ return button
113
+ }
114
+
115
+ @objc var onAdLoadedEvent: RCTDirectEventBlock? {
116
+ get { eventSink.onAdLoadedEvent }
117
+ set { eventSink.onAdLoadedEvent = newValue }
118
+ }
119
+ @objc var onAdLoadFailedEvent: RCTDirectEventBlock? {
120
+ get { eventSink.onAdLoadFailedEvent }
121
+ set { eventSink.onAdLoadFailedEvent = newValue }
122
+ }
123
+ @objc var onAdClickedEvent: RCTDirectEventBlock? {
124
+ get { eventSink.onAdClickedEvent }
125
+ set { eventSink.onAdClickedEvent = newValue }
126
+ }
127
+ @objc var onAdImpressionRecordedEvent: RCTDirectEventBlock? {
128
+ get { eventSink.onAdImpressionRecordedEvent }
129
+ set { eventSink.onAdImpressionRecordedEvent = newValue }
130
+ }
131
+
132
+ @Atomic private var isAdUnitIdSet: Bool = false
133
+ @Atomic private var isBindBridge: Bool = false
134
+ @Atomic private var isDisposed: Bool = false
135
+
136
+ private static func normalizedAssetTag(_ tag: NSNumber?) -> NSNumber? {
137
+ guard let tag, tag.intValue != 0 else { return nil }
138
+ return tag
139
+ }
140
+
141
+ convenience init(assetResolver: DaroMNativeAssetResolving?) {
142
+ self.init()
143
+ self.defaultAssetResolver = assetResolver
144
+ self.assetResolver = assetResolver
145
+ }
146
+
147
+ required init() {
148
+ self.adUnit = DaroAdUnit(unitId: "")
149
+ super.init(frame: .zero)
150
+ }
151
+
152
+ required init?(coder: NSCoder) {
153
+ fatalError("init(coder:) has not been implemented")
154
+ }
155
+
156
+ deinit {
157
+ disposeNativeAdView()
158
+ }
159
+
160
+ override func didSetProps(_ changedProps: [String]) {
161
+ applyReactProps(changedProps)
162
+ }
163
+
164
+ @objc(applyReactProps:)
165
+ func applyReactProps(_ changedProps: [String]) {
166
+ guard !isDisposed else { return }
167
+ guard changedProps.isEmpty == false else { return }
168
+ guard let adUnitId, adUnitId.isEmpty == false else {
169
+ if changedProps.contains("adUnitId") {
170
+ DaroMModule.logger.warning("[native-ad] AdUnitId is required")
171
+ }
172
+ return
173
+ }
174
+ loadCoordinator.cancelAutomaticLoadIfNeeded(loadOnMount: loadOnMount)
175
+ DaroMModule.logger.info("[native-ad] \(#function): \(changedProps)")
176
+ if !isAdUnitIdSet {
177
+ isAdUnitIdSet = true
178
+ makeAdViews(unitId: adUnitId)
179
+ requestInitialLoadIfNeeded()
180
+ }
181
+
182
+ if hasBridgeViewChange(changedProps) {
183
+ DispatchQueue.main.async { [weak self] in
184
+ self?.refreshAssetBindingsAndProcessPendingLoad()
185
+ }
186
+ }
187
+
188
+ requestInitialLoadIfNeeded()
189
+ }
190
+
191
+ override func didMoveToWindow() {
192
+ super.didMoveToWindow()
193
+
194
+ guard !isDisposed, window != nil else { return }
195
+
196
+ DispatchQueue.main.async { [weak self] in
197
+ self?.refreshAssetBindingsAndProcessPendingLoad()
198
+ }
199
+ }
200
+
201
+ override func removeFromSuperview() {
202
+ disposeNativeAdView()
203
+ super.removeFromSuperview()
204
+ }
205
+
206
+ @objc(prepareForReuse)
207
+ func prepareForReuse() {
208
+ resetNativeAdState(clearAssetResolver: false, recreateAssetViews: true)
209
+ resetReactProps()
210
+ isDisposed = false
211
+ }
212
+
213
+ func loadAd() {
214
+ guard !isDisposed else { return }
215
+ loadCoordinator.requestExplicitLoad()
216
+ processPendingLoad(retryCount: 0)
217
+ }
218
+
219
+ private func requestInitialLoadIfNeeded() {
220
+ guard loadOnMount, !isDisposed, _internalAdView != nil, hasRegisteredAssetView else { return }
221
+ loadCoordinator.requestAutomaticLoad(loadOnMount: loadOnMount)
222
+ processPendingLoad(retryCount: 0)
223
+ }
224
+
225
+ private func processPendingLoad(retryCount: Int) {
226
+ guard !isDisposed, loadCoordinator.hasPendingLoad else { return }
227
+
228
+ guard isReadyToLoadAd else {
229
+ retryPendingLoad(retryCount: retryCount)
230
+ return
231
+ }
232
+
233
+ guard let internalAdView = _internalAdView,
234
+ loadCoordinator.consumePendingLoad(loadOnMount: loadOnMount) != nil else {
235
+ return
236
+ }
237
+
238
+ pendingLoadWorkItem?.cancel()
239
+ pendingLoadWorkItem = nil
240
+ isBindBridge = false
241
+ bindAssetViews()
242
+ internalAdView.loadAd()
243
+ }
244
+
245
+ private func retryPendingLoad(retryCount: Int) {
246
+ guard retryCount < Self.maxAssetLoadRetryCount else {
247
+ DaroMModule.logger.warning("[native-ad] Asset views were not ready before load request")
248
+ pendingLoadWorkItem = nil
249
+ return
250
+ }
251
+
252
+ pendingLoadWorkItem?.cancel()
253
+ let workItem = DispatchWorkItem { [weak self] in
254
+ self?.processPendingLoad(retryCount: retryCount + 1)
255
+ }
256
+
257
+ pendingLoadWorkItem = workItem
258
+ DispatchQueue.main.asyncAfter(deadline: .now() + Self.assetLoadRetryDelay, execute: workItem)
259
+ }
260
+
261
+ private var isReadyToLoadAd: Bool {
262
+ _internalAdView != nil && hasRegisteredAssetView && registeredAssetViewsReady
263
+ }
264
+
265
+ private var hasRegisteredAssetView: Bool {
266
+ registeredAssetTags.isEmpty == false
267
+ }
268
+
269
+ private var registeredAssetViewsReady: Bool {
270
+ let tags = registeredAssetTags
271
+ guard tags.isEmpty == false else { return false }
272
+ return tags.allSatisfy { resolveAssetView(tag: $0) != nil }
273
+ }
274
+
275
+ private var registeredAssetTags: [NSNumber] {
276
+ [
277
+ titleView,
278
+ bodyView,
279
+ callToActionView,
280
+ iconView,
281
+ mediaView,
282
+ ].compactMap { $0 }
283
+ }
284
+
285
+ private func makeAdViews(unitId: String) {
286
+ self.adUnit = DaroAdUnit(unitId: unitId)
287
+ let native = DaroMBridgeNativeAdView(
288
+ unit: adUnit,
289
+ preferredAdChoicesPosition: preferredAdChoicesPosition
290
+ )
291
+ native.delegate = self
292
+ self._internalAdView = native
293
+ super.addSubview(native)
294
+ subviews
295
+ .filter { $0 !== native }
296
+ .enumerated()
297
+ .forEach { index, subview in
298
+ insertAdSubview(subview, at: index)
299
+ }
300
+ pin(native, to: self)
301
+
302
+ self._internalAdView?.listener.onAdLoadSuccess = { [weak self] _, adInfo in
303
+ self?.eventSink.emitAdLoaded(adInfo?.toBody)
304
+ }
305
+ self._internalAdView?.listener.onAdLoadFail = { [weak self] error in
306
+ self?.eventSink.emitAdLoadFailed(error.toBody)
307
+ }
308
+ self._internalAdView?.listener.onAdImpression = { [weak self] adInfo in
309
+ self?.eventSink.emitAdImpressionRecorded(adInfo?.toBody)
310
+ }
311
+ self._internalAdView?.listener.onAdClicked = { [weak self] adInfo in
312
+ self?.eventSink.emitAdClicked(adInfo?.toBody)
313
+ }
314
+ }
315
+
316
+ private func resolveAssetView(tag: NSNumber?) -> UIView? {
317
+ guard let tag else { return nil }
318
+ return assetResolver?.view(forReactTag: tag)
319
+ }
320
+
321
+ private func bindAssetViews() {
322
+ guard !isDisposed else { return }
323
+ guard _internalAdView != nil else { return }
324
+ guard !isBindBridge else { return }
325
+ isBindBridge = true
326
+ var bindTitleLabel: UILabel?
327
+ if let bridgeTitleView = resolveAssetView(tag: titleView) {
328
+ pin(_internalTitleLabel, to: bridgeTitleView)
329
+ bindTitleLabel = _internalTitleLabel
330
+ }
331
+
332
+ var bindBodyLabel: UILabel?
333
+ if let bridgeBodyView = resolveAssetView(tag: bodyView) {
334
+ pin(_internalBodyLabel, to: bridgeBodyView)
335
+ bindBodyLabel = _internalBodyLabel
336
+ }
337
+
338
+ var bindCallToActionButton: UIButton?
339
+ if let bridgeCallToActionView = resolveAssetView(tag: callToActionView) {
340
+ pin(_internalFakeCTAButton, to: bridgeCallToActionView)
341
+ bindCallToActionButton = _internalFakeCTAButton
342
+ }
343
+
344
+ var bindIconImageView: UIImageView?
345
+ if let bridgeIconView = resolveAssetView(tag: iconView) {
346
+ pin(_internalIconImageView, to: bridgeIconView)
347
+ bindIconImageView = _internalIconImageView
348
+ }
349
+
350
+ var bindMediaView: UIView?
351
+ if let bridgeMediaView = resolveAssetView(tag: mediaView) {
352
+ pin(_internalMediaView, to: bridgeMediaView)
353
+ bindMediaView = _internalMediaView
354
+ }
355
+
356
+ _internalAdView?.bindViews(
357
+ titleLabel: bindTitleLabel,
358
+ bodyLabel: bindBodyLabel,
359
+ iconImageView: bindIconImageView,
360
+ mediaContentView: bindMediaView,
361
+ callToActionButton: bindCallToActionButton
362
+ )
363
+ }
364
+
365
+ func renderAd(for nativeAd: DaroAdNative) {
366
+ guard !isDisposed else { return }
367
+ _internalFakeCTAButton.isUserInteractionEnabled = false
368
+ }
369
+
370
+ override func addSubview(_ view: UIView) {
371
+ guard !isDisposed else { return }
372
+ insertAdSubview(view, at: nil)
373
+ }
374
+
375
+ @objc(setAssetSearchRootView:)
376
+ func setAssetSearchRootView(_ rootView: UIView?) {
377
+ if let rootView {
378
+ assetResolver = DaroMDescendantNativeAssetResolver(rootView: rootView)
379
+ } else {
380
+ assetResolver = defaultAssetResolver
381
+ }
382
+ refreshAssetBindingsAndProcessPendingLoad()
383
+ }
384
+
385
+ @objc(insertMountedSubview:atIndex:)
386
+ func insertMountedSubview(_ view: UIView, atIndex index: NSNumber) {
387
+ guard !isDisposed else { return }
388
+ insertAdSubview(view, at: index.intValue)
389
+ refreshAssetBindingsAndProcessPendingLoad()
390
+ }
391
+
392
+ @objc(refreshMountedAssetViews)
393
+ func refreshMountedAssetViews() {
394
+ guard !isDisposed else { return }
395
+ refreshAssetBindingsAndProcessPendingLoad()
396
+ }
397
+
398
+ private func insertAdSubview(_ view: UIView, at index: Int?) {
399
+ guard let targetView = _internalAdView else {
400
+ let insertionIndex = min(max(index ?? subviews.count, 0), subviews.count)
401
+ super.insertSubview(view, at: insertionIndex)
402
+ return
403
+ }
404
+
405
+ targetView.addSubview(view)
406
+
407
+ guard let actualSuperview = view.superview else { return }
408
+ let maxInsertionIndex = max(actualSuperview.subviews.count - 1, 0)
409
+ let insertionIndex = min(max(index ?? maxInsertionIndex, 0), maxInsertionIndex)
410
+ if actualSuperview.subviews.firstIndex(where: { $0 === view }) != insertionIndex {
411
+ actualSuperview.insertSubview(view, at: insertionIndex)
412
+ }
413
+ }
414
+
415
+ private func refreshAssetBindingsAndProcessPendingLoad() {
416
+ guard !isDisposed else { return }
417
+ isBindBridge = false
418
+ bindAssetViews()
419
+ requestInitialLoadIfNeeded()
420
+ processPendingLoad(retryCount: 0)
421
+ }
422
+
423
+ private func hasBridgeViewChange(_ changedProps: [String]) -> Bool {
424
+ let bridgeProps: Set<String> = [
425
+ "titleView",
426
+ "bodyView",
427
+ "callToActionView",
428
+ "iconView",
429
+ "mediaView",
430
+ ]
431
+ return changedProps.contains { bridgeProps.contains($0) }
432
+ }
433
+
434
+ private var preferredAdChoicesPosition: DaroAdChoicesPosition {
435
+ switch adChoicesPosition {
436
+ case "topLeft":
437
+ return .topLeft
438
+ case "topRight":
439
+ return .topRight
440
+ case "bottomLeft":
441
+ return .bottomLeft
442
+ default:
443
+ return .bottomRight
444
+ }
445
+ }
446
+
447
+ private func pin(_ view: UIView, to targetView: UIView) {
448
+ let identifier = ObjectIdentifier(view)
449
+ if let constraints = pinnedConstraints[identifier] {
450
+ NSLayoutConstraint.deactivate(constraints)
451
+ pinnedConstraints[identifier] = nil
452
+ }
453
+
454
+ view.translatesAutoresizingMaskIntoConstraints = false
455
+ if view.superview != targetView {
456
+ view.removeFromSuperview()
457
+ targetView.addSubview(view)
458
+ }
459
+
460
+ let constraints = [
461
+ view.topAnchor.constraint(equalTo: targetView.topAnchor),
462
+ view.bottomAnchor.constraint(equalTo: targetView.bottomAnchor),
463
+ view.leftAnchor.constraint(equalTo: targetView.leftAnchor),
464
+ view.rightAnchor.constraint(equalTo: targetView.rightAnchor),
465
+ ]
466
+ NSLayoutConstraint.activate(constraints)
467
+ pinnedConstraints[identifier] = constraints
468
+ }
469
+
470
+ private func disposeNativeAdView() {
471
+ guard !isDisposed else { return }
472
+ isDisposed = true
473
+ resetNativeAdState(clearAssetResolver: true, recreateAssetViews: false)
474
+ }
475
+
476
+ private func resetReactProps() {
477
+ adUnitId = nil
478
+ loadOnMount = true
479
+ titleViewStyleProps = nil
480
+ titleView = nil
481
+ bodyViewStyleProps = nil
482
+ bodyView = nil
483
+ callToActionViewStyleProps = nil
484
+ callToActionView = nil
485
+ iconViewStyleProps = nil
486
+ iconView = nil
487
+ mediaViewStyleProps = nil
488
+ mediaView = nil
489
+ adChoicesPosition = nil
490
+ adUnit = DaroAdUnit(unitId: "")
491
+ }
492
+
493
+ private func resetNativeAdState(clearAssetResolver: Bool, recreateAssetViews: Bool) {
494
+ isBindBridge = false
495
+ isAdUnitIdSet = false
496
+ loadCoordinator.reset()
497
+ pendingLoadWorkItem?.cancel()
498
+ pendingLoadWorkItem = nil
499
+ if clearAssetResolver {
500
+ defaultAssetResolver = nil
501
+ assetResolver = nil
502
+ }
503
+
504
+ pinnedConstraints.values.forEach { NSLayoutConstraint.deactivate($0) }
505
+ pinnedConstraints.removeAll()
506
+
507
+ _internalAdView?.listener.onAdLoadSuccess = nil
508
+ _internalAdView?.listener.onAdLoadFail = nil
509
+ _internalAdView?.listener.onAdImpression = nil
510
+ _internalAdView?.listener.onAdClicked = nil
511
+ _internalAdView?.delegate = nil
512
+ _internalAdView?.clearNativeAd()
513
+ _internalAdView?.removeFromSuperview()
514
+ _internalAdView = nil
515
+
516
+ _internalTitleLabel.removeFromSuperview()
517
+ _internalBodyLabel.removeFromSuperview()
518
+ _internalFakeCTAButton.removeFromSuperview()
519
+ _internalIconImageView.removeFromSuperview()
520
+ _internalMediaView.removeFromSuperview()
521
+
522
+ _internalTitleLabel.text = nil
523
+ _internalTitleLabel.attributedText = nil
524
+ _internalBodyLabel.text = nil
525
+ _internalBodyLabel.attributedText = nil
526
+ _internalFakeCTAButton.setTitle(nil, for: .normal)
527
+ _internalFakeCTAButton.setAttributedTitle(nil, for: .normal)
528
+ _internalIconImageView.image = nil
529
+ _internalMediaView.subviews.forEach { $0.removeFromSuperview() }
530
+
531
+ guard recreateAssetViews else { return }
532
+
533
+ _internalTitleLabel = Self.makeTitleLabel()
534
+ _internalBodyLabel = Self.makeBodyLabel()
535
+ _internalMediaView = Self.makeMediaView()
536
+ _internalIconImageView = Self.makeIconImageView()
537
+ _internalFakeCTAButton = Self.makeFakeCTAButton()
538
+ }
539
+ }
540
+
541
+ protocol DaroMNativeAdRenderDelegate: AnyObject {
542
+ func renderAd(for nativeAd: DaroAdNative)
543
+ }
544
+
545
+ class DaroMBridgeNativeAdView: DaroAdNativeView {
546
+ weak var delegate: DaroMNativeAdRenderDelegate?
547
+
548
+ override func renderAd(for nativeAd: DaroAdNative, loader: any AdNativeLoadable) {
549
+ super.renderAd(for: nativeAd, loader: loader)
550
+ self.delegate?.renderAd(for: nativeAd)
551
+ }
552
+ }
553
+
554
+ @propertyWrapper
555
+ private struct Atomic<Value> {
556
+ private var value: Value
557
+ private let lock = NSLock()
558
+
559
+ init(wrappedValue value: Value) {
560
+ self.value = value
561
+ }
562
+
563
+ var wrappedValue: Value {
564
+ get {
565
+ lock.lock()
566
+ defer { lock.unlock() }
567
+ return value
568
+ }
569
+ set {
570
+ lock.lock()
571
+ value = newValue
572
+ lock.unlock()
573
+ }
574
+ }
575
+ }
@@ -0,0 +1,53 @@
1
+ import React
2
+ import UIKit
3
+
4
+ protocol DaroMNativeAssetResolving: AnyObject {
5
+ func view(forReactTag tag: NSNumber) -> UIView?
6
+ }
7
+
8
+ final class DaroMBridgeNativeAssetResolver: DaroMNativeAssetResolving {
9
+ private weak var bridge: RCTBridge?
10
+
11
+ init(bridge: RCTBridge?) {
12
+ self.bridge = bridge
13
+ }
14
+
15
+ func view(forReactTag tag: NSNumber) -> UIView? {
16
+ bridge?.uiManager.view(forReactTag: tag)
17
+ }
18
+ }
19
+
20
+ final class DaroMDescendantNativeAssetResolver: DaroMNativeAssetResolving {
21
+ private weak var rootView: UIView?
22
+
23
+ init(rootView: UIView) {
24
+ self.rootView = rootView
25
+ }
26
+
27
+ func view(forReactTag tag: NSNumber) -> UIView? {
28
+ rootView?.daroMFindReactSubview(tag: tag)
29
+ }
30
+ }
31
+
32
+ private extension UIView {
33
+ func daroMFindReactSubview(tag: NSNumber) -> UIView? {
34
+ if self.tag == tag.intValue {
35
+ return self
36
+ }
37
+
38
+ let reactTagSelector = Selector(("reactTag"))
39
+ if responds(to: reactTagSelector),
40
+ let reactTag = value(forKey: "reactTag") as? NSNumber,
41
+ reactTag == tag {
42
+ return self
43
+ }
44
+
45
+ for subview in subviews {
46
+ if let found = subview.daroMFindReactSubview(tag: tag) {
47
+ return found
48
+ }
49
+ }
50
+
51
+ return nil
52
+ }
53
+ }
@@ -6,6 +6,7 @@ internal typealias DaroAdFormat = Daro.DaroAdFormat
6
6
  internal typealias DaroAdInfo = Daro.DaroAdInfo
7
7
  internal typealias DaroAdNative = Daro.DaroAdNative
8
8
  internal typealias DaroAdNativeView = Daro.DaroAdNativeView
9
+ internal typealias DaroAdChoicesPosition = Daro.DaroAdChoicesPosition
9
10
  internal typealias DaroAdUnit = Daro.DaroAdUnit
10
11
  internal typealias DaroAds = Daro.DaroAds
11
12
  internal typealias DaroAppOpenAd = Daro.DaroAppOpenAd