Skip to content
94 changes: 94 additions & 0 deletions patches/expo-modules-core+2.3.12.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
diff --git a/node_modules/expo-modules-core/ios/Core/Arguments/Convertibles.swift b/node_modules/expo-modules-core/ios/Core/Arguments/Convertibles.swift
index a126e11..35d620c 100644
--- a/node_modules/expo-modules-core/ios/Core/Arguments/Convertibles.swift
+++ b/node_modules/expo-modules-core/ios/Core/Arguments/Convertibles.swift
@@ -19,14 +19,88 @@ extension URL: Convertible {
throw Conversions.ConvertingException<URL>(value)
}

+ // iOS 18.6+ CRITICAL: Check for HTTP/HTTPS URLs FIRST to prevent API misuse
+ // iOS 18.6 will crash with "API MISUSE: URL(filePath:) called with an HTTP URL string"
+ // if we try to use fileURLWithPath on an HTTP URL
+ let lowercasedValue = value.lowercased()
+ let isHttpUrl = lowercasedValue.hasPrefix("http://") || lowercasedValue.hasPrefix("https://")
+
+ if isHttpUrl {
+ // Try to parse as HTTP URL first
+ if let url = URL(string: value) {
+ return url
+ }
+ // If URL(string:) fails, try with URLComponents for better RFC 3986 support
+ if #available(iOS 16, *) {
+ if let url = URLComponents(string: value)?.url {
+ return url
+ }
+ }
+ throw UrlContainsInvalidCharactersException()
+ }
+
// First we try to create a URL without extra encoding, as it came.
if let url = convertToUrl(string: value) {
return url
}

// File path doesn't need to be percent-encoded.
+ // iOS 18.6+ CRITICAL: Has stricter validation for fileURLWithPath that causes assertion failures
+ // in production builds. We must validate extensively before calling it.
+ // Only check for file paths if it's NOT an HTTP URL (already checked above)
if isFileUrlPath(value) {
- return URL(fileURLWithPath: value)
+ // iOS 18.6+ CRITICAL: Empty paths will crash with assertion failure in production
+ guard !value.isEmpty else {
+ throw UrlContainsInvalidCharactersException()
+ }
+
+ // iOS 18.6+ requires stricter validation - check for invalid characters
+ let trimmedPath = value.trimmingCharacters(in: .whitespacesAndNewlines)
+ guard !trimmedPath.isEmpty else {
+ throw UrlContainsInvalidCharactersException()
+ }
+
+ // Validate path format for iOS 18.6+ strict requirements
+ // Must be absolute path (starts with /) or already a file:// URL
+ // CRITICAL: Double-check it's not an HTTP URL that slipped through
+ let isAbsolutePath = trimmedPath.hasPrefix("/")
+ let isFileURL = trimmedPath.hasPrefix("file://")
+ let trimmedLowercased = trimmedPath.lowercased()
+ let isHttpUrl = trimmedLowercased.hasPrefix("http://") || trimmedLowercased.hasPrefix("https://")
+
+ // iOS 18.6+ will crash if we call fileURLWithPath on an HTTP URL
+ guard !isHttpUrl else {
+ // Try to parse as HTTP URL instead
+ if let url = URL(string: value) {
+ return url
+ }
+ throw UrlContainsInvalidCharactersException()
+ }
+
+ guard isAbsolutePath || isFileURL else {
+ throw UrlContainsInvalidCharactersException()
+ }
+
+ // Check for null characters and control characters that iOS 18.6 rejects
+ if trimmedPath.contains("\0") || trimmedPath.rangeOfCharacter(from: CharacterSet.controlCharacters) != nil {
+ throw UrlContainsInvalidCharactersException()
+ }
+
+ // Create file URL - iOS 18.6 will assert if path is still invalid
+ // Note: We can't catch assertion failures, so validation above is critical
+ let fileURL: URL
+ if isFileURL {
+ // Already a file:// URL, parse it safely
+ guard let parsedURL = URL(string: trimmedPath) else {
+ throw UrlContainsInvalidCharactersException()
+ }
+ fileURL = parsedURL
+ } else {
+ // Absolute path - iOS 18.6 will assert if malformed
+ fileURL = URL(fileURLWithPath: trimmedPath)
+ }
+
+ return fileURL
}

// If we get here, the string is not the file url and may require percent-encoding characters that are not URL-safe according to RFC 3986.
Loading