first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,101 @@
// Copyright 2025-present 650 Industries. All rights reserved.
internal final class ConstantsProvider: EXConstantsInterface {
nonisolated(unsafe) static let shared = ConstantsProvider()
func constants() -> [AnyHashable: Any] {
// Some constants can be safely read only on the main thread
let (statusBarHeight, deviceName, systemFonts) = performSynchronouslyOnMainActor {
return (
getStatusBarHeight(),
getDeviceName(),
getSystemFontNames()
)
}
#if DEBUG
let isDebugXcodeScheme = true
#else
let isDebugXcodeScheme = false
#endif
return [
"sessionId": UUID().uuidString,
"executionEnvironment": "bare",
"statusBarHeight": statusBarHeight,
"deviceName": deviceName,
"systemFonts": systemFonts,
"debugMode": isDebugXcodeScheme,
"isHeadless": false,
"manifest": getManifest(), // Deprecated, but still used internally.
"platform": [
"ios": [
"buildNumber": getBuildVersion()
]
]
]
}
}
private func getBuildVersion() -> String? {
return Bundle.main.infoDictionary?["CFBundleVersion"] as? String
}
@MainActor
private func getStatusBarHeight() -> Double {
#if os(iOS)
let statusBarSize = UIApplication.shared.statusBarFrame.size
return min(statusBarSize.width, statusBarSize.height)
#else
return 0
#endif
}
@MainActor
private func getSystemFontNames() -> [String] {
#if os(iOS) || os(tvOS)
let familyNames = UIFont.familyNames
var fontNames = Set<String>()
// "System Font" is added to `UIFont.familyNames` in iOS 15, and the font names that
// correspond with it are dot prefixed .SFUI-* fonts which log the following warning
// when passed in to `UIFont.fontNames(forFamilyName:)`:
// > CoreText note: Client requested name .SFUI-HeavyItalic, it will get TimesNewRomanPSMT rather than the intended font.
// All system UI font access should be through proper APIs such as `CTFontCreateUIFontForLanguage()` or `UIFont.systemFont(ofSize:)`.
for familyName in familyNames where familyName != "System Font" {
fontNames.insert(familyName)
UIFont.fontNames(forFamilyName: familyName).forEach { fontName in
fontNames.insert(fontName)
}
}
return fontNames.sorted()
#elseif os(macOS)
return NSFontManager.shared.availableFontFamilies
#endif
}
@MainActor
private func getDeviceName() -> String {
#if os(iOS) || os(tvOS)
return UIDevice.current.name
#elseif os(macOS)
return ProcessInfo.processInfo.hostName
#endif
}
private func getManifest() -> [String: Any]? {
let frameworkBundle = Bundle(for: ConstantsProvider.self)
guard let bundleUrl = frameworkBundle.resourceURL?.appendingPathComponent("EXConstants.bundle"),
let bundle = Bundle(url: bundleUrl),
let url = bundle.url(forResource: "app", withExtension: "config") else {
log.error("Unable to find the embedded app config")
return nil
}
do {
let configData = try Data(contentsOf: url)
return try JSONSerialization.jsonObject(with: configData, options: []) as? [String: Any]
} catch {
log.error("Error reading the embedded app config: \(error)")
return nil
}
}

View File

@@ -0,0 +1,14 @@
// Copyright 2025-present 650 Industries. All rights reserved.
#import <React/RCTBridge.h>
#import <React/RCTImageLoader.h>
#import <ExpoModulesCore/EXImageLoaderInterface.h>
NS_SWIFT_NAME(ImageLoader)
@interface EXImageLoader : NSObject <EXImageLoaderInterface>
- (nonnull instancetype)initWithBridge:(nonnull RCTBridge *)bridge;
- (nonnull instancetype)initWithRCTImageLoader:(nonnull RCTImageLoader *)loader;
@end

View File

@@ -0,0 +1,33 @@
// Copyright 2025-present 650 Industries. All rights reserved.
#import <ExpoModulesCore/EXImageLoader.h>
#import <React/RCTImageLoaderProtocol.h>
#import <React/RCTImageLoader.h>
@implementation EXImageLoader {
RCTImageLoader *_rctImageLoader;
}
- (nonnull instancetype)initWithBridge:(nonnull RCTBridge *)bridge
{
if (self = [super init]) {
_rctImageLoader = [bridge moduleForName:@"RCTImageLoader" lazilyLoadIfNecessary:YES];
}
return self;
}
- (nonnull instancetype)initWithRCTImageLoader:(nonnull RCTImageLoader *)loader
{
if (self = [super init]) {
_rctImageLoader = loader;
}
return self;
}
- (void)loadImageForURL:(NSURL *)imageURL
completionHandler:(EXImageLoaderCompletionBlock)completionHandler
{
[_rctImageLoader loadImageWithURLRequest:[NSURLRequest requestWithURL:imageURL] callback:completionHandler];
}
@end

View File

@@ -0,0 +1,131 @@
// Copyright 2022-present 650 Industries. All rights reserved.
/**
Executes the given Swift closure and if it throws, the `NSError` is set on the given `NSErrorPointer` and the original error is rethrown.
This is especially useful for ObjC<->Swift interop, specifically when the ObjC needs to catch errors thrown by Swift closure.
*/
internal func runWithErrorPointer<R>(_ errorPointer: NSErrorPointer, _ closure: () throws -> R) rethrows -> R? {
do {
return try closure()
} catch {
errorPointer?.pointee = toNSError(error)
throw error
}
}
/**
Converts Swift errors to `NSError` so that they can be handled from the ObjC code.
*/
internal func toNSError(_ error: Error) -> NSError {
if let error = error as? Exception {
return NSError(domain: "dev.expo.modules", code: 0, userInfo: [
"name": error.name,
"code": error.code,
"message": error.debugDescription
])
}
return error as NSError
}
// MARK: - URLs
/**
A string with non-alphanumeric url-safe characters according to RFC 3986.
These characters might have to be explicitly percent-encoded when used in URL components other than intended.
*/
internal let urlAllowedCharacters = "-._~:/?#[]@!$&'()*+,;="
/**
A `CharacterSet` instance containing all alphanumerics and characters allowed in at least one part of a URL.
*/
internal let urlAllowedCharactersSet = CharacterSet.alphanumerics.union(CharacterSet(charactersIn: urlAllowedCharacters))
/**
Returns the given string with percent-encoded characters that are not allowed in any of the URL components as defined by RFC 3986.
This is useful for auto-encoding unicode characters.
*/
internal func percentEncodeUrlString(_ url: String) -> String? {
let encodedString = url.addingPercentEncoding(withAllowedCharacters: urlAllowedCharactersSet)
return encodedString?.replacingOccurrences(of: "%25", with: "%")
}
/**
Checks whether the given string is a file URL path (URL string without the scheme).
*/
internal func isFileUrlPath(_ path: String) -> Bool {
guard let encodedPath = path.addingPercentEncoding(withAllowedCharacters: urlAllowedCharactersSet) else {
return false
}
return URL(string: encodedPath)?.scheme == nil
}
internal func convertToUrl(string value: String) -> URL? {
let url: URL?
if #available(iOS 17, *) {
// URL(string:) supports RFC 3986 as URLComponents from iOS 17
url = URL(string: value)
} else if #available(iOS 16, *) {
// URLComponents parses and constructs URLs according to RFC 3986.
// For some unusual urls URL(string:) will fail incorrectly
url = URLComponents(string: value)?.url ?? URL(string: value)
} else {
// URLComponents on iOS 15 and lower does not well support RFC 3986.
// We have to fallback URL(fileURLWithPath:) first.
url = value.hasPrefix("/")
? URL(fileURLWithPath: value)
: URLComponents(string: value)?.url ?? URL(string: value)
}
guard let url else {
return nil
}
// If it has no scheme, we assume it was the file path which needs to be recreated to be recognized as the file url.
return url.scheme != nil ? url : URL(fileURLWithPath: value)
}
/**
Helper class that lets us bypass the compiler's data-race protection checks by taking the value out of isolation.
- TODO: Remove it once the code is refactored to deal better with the concurrency model.
*/
internal class NonisolatedUnsafeVar<VarType>: @unchecked Sendable {
nonisolated(unsafe) var value: VarType
init(_ value: VarType) {
self.value = value
}
}
internal func performSynchronouslyOnMainActor<Result: Sendable>(_ closure: @MainActor () throws -> Result) rethrows -> Result {
if Thread.isMainThread {
return try MainActor.assumeIsolated(closure)
}
return try DispatchQueue.main.sync(execute: closure)
}
/**
A collection of utility functions for various Expo Modules common tasks.
*/
public struct Utilities {
/**
Converts a `String` to a `URL`.
*/
public static func urlFrom(string: String) -> URL? {
return convertToUrl(string: string)
}
nonisolated public func currentViewController() -> UIViewController? {
return MainActor.assumeIsolated {
#if os(iOS) || os(tvOS)
var controller = UIApplication.shared.keyWindow?.rootViewController
while let presentedController = controller?.presentedViewController, !presentedController.isBeingDismissed {
controller = presentedController
}
return controller
#elseif os(macOS)
// Even though the function's return type is `UIViewController`, react-native-macos will alias `NSViewController` to `UIViewController`.
return NSApplication.shared.keyWindow?.contentViewController
#endif
}
}
}