Skip to content

Commit 6a91375

Browse files
feat: Add URL validation and delay for internal ad loading to prevent crashes on iOS
1 parent f30506c commit 6a91375

1 file changed

Lines changed: 46 additions & 9 deletions

File tree

lib/modules/main/presentation/widgets/ads/strategy/internal_ad_strategy.dart

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,24 @@ class InternalAdStrategy implements AdLoadingStrategy {
8282
);
8383
}
8484

85+
// Validate URL format before using it (safety check for iOS network issue)
86+
if (!imageUrl.startsWith('http://') && !imageUrl.startsWith('https://')) {
87+
debugPrint('❌ Invalid internal ad URL format: $imageUrl');
88+
await _analytics.logEvent(
89+
name: 'ads_internal_ad_load_failure',
90+
parameters: {'error_code': 'INVALID_URL', 'url': imageUrl},
91+
);
92+
93+
ref.read(adsProvider.notifier).setAdLoadFailed(
94+
errorCode: 'INVALID_URL',
95+
errorMessage: 'Invalid ad URL format',
96+
);
97+
return AdLoadResult.failure(
98+
errorCode: 'INVALID_URL',
99+
errorMessage: 'Invalid ad URL format: $imageUrl',
100+
);
101+
}
102+
85103
debugPrint('✅ Internal ad loaded:');
86104
debugPrint(' Image: $imageUrl');
87105
debugPrint(' Click: $clickUrl');
@@ -125,6 +143,16 @@ class InternalAdStrategy implements AdLoadingStrategy {
125143
final imageUrl = state.customImageUrl ?? '';
126144
final clickUrl = state.customClickUrl ?? '';
127145

146+
// Validate URL format before rendering (defensive check to prevent iOS file:/// error)
147+
// CRITICAL: Never pass empty or malformed URLs to Image.network on iOS
148+
// iOS incorrectly resolves empty strings as file:/// URIs causing crashes
149+
if (imageUrl.isEmpty || (!imageUrl.startsWith('http://') && !imageUrl.startsWith('https://'))) {
150+
if (imageUrl.isNotEmpty) {
151+
debugPrint('❌ Invalid URL in buildAdWidget, refusing to render: $imageUrl');
152+
}
153+
return const SizedBox.shrink();
154+
}
155+
128156
return GestureDetector(
129157
onTap: () async {
130158
if (clickUrl.isEmpty) {
@@ -200,8 +228,9 @@ class InternalAdStrategy implements AdLoadingStrategy {
200228
},
201229
);
202230

203-
// Trigger state update to hide the entire widget
204-
container.read(adsProvider.notifier).setCustomImageLoadFailed();
231+
// Clear all ad data so the container disappears completely
232+
debugPrint('🗑️ Clearing ad data due to image load failure');
233+
container.read(adsProvider.notifier).clearCustomAdData();
205234
});
206235
}
207236

@@ -225,17 +254,25 @@ class InternalAdStrategy implements AdLoadingStrategy {
225254
// INTERNAL ADS: Show when connected (all users including Iranian)
226255
// When connected, load fresh internal ad and show it
227256
if (current == ConnectionStatus.connected && previous != ConnectionStatus.connected) {
228-
debugPrint('▶️ Connected - loading fresh internal ad to show during VPN session');
257+
debugPrint('▶️ Connected - will load internal ad after network routing stabilizes');
229258

230259
// Mark first connection complete
231260
ref.read(adsProvider.notifier).markFirstConnectionComplete();
232261

233-
// Load fresh internal ad
234-
loadAd(ref: ref).then((result) {
235-
if (result.success) {
236-
debugPrint('⏰ Fresh internal ad loaded - starting countdown');
237-
ref.read(adsProvider.notifier).startCountdownTimer();
238-
}
262+
// iOS FIX: Add delay to allow VPN network routing to fully establish
263+
// Without this delay, Image.network may incorrectly resolve HTTPS URLs as file:// URIs
264+
// causing "No host specified in URI file:///..." errors on first connection
265+
// Also allows time for VPN tunnel to stabilize (tun2socks, ping refresh, SSL/TLS)
266+
Future.delayed(const Duration(milliseconds: 2500), () {
267+
debugPrint('⏱️ Network routing delay complete (2.5s) - loading fresh internal ad');
268+
269+
// Load fresh internal ad
270+
loadAd(ref: ref).then((result) {
271+
if (result.success) {
272+
debugPrint('⏰ Fresh internal ad loaded - starting countdown');
273+
ref.read(adsProvider.notifier).startCountdownTimer();
274+
}
275+
});
239276
});
240277
return;
241278
}

0 commit comments

Comments
 (0)