diff --git a/patches/expo-modules-core+2.3.12.patch b/patches/expo-modules-core+2.3.12.patch new file mode 100644 index 0000000000..4d88077772 --- /dev/null +++ b/patches/expo-modules-core+2.3.12.patch @@ -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(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.