mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
12543d557f
Summary: Changelog: Some callsites may rely on the assumption that address return by this method does not contain port information. JSLocation saved in NSUserDefault might contain port information, removing it here can prevent the issue for callsites. Differential Revision: D23240495 fbshipit-source-id: a2edf4abb086fd951dd089331407bd659aad1729
324 lines
10 KiB
Plaintext
324 lines
10 KiB
Plaintext
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#import "RCTBundleURLProvider.h"
|
|
|
|
#import "RCTConvert.h"
|
|
#import "RCTDefines.h"
|
|
|
|
#if __has_include("hermes.h") || __has_include(<hermes/hermes.h>)
|
|
#import <hermes/hermes.h>
|
|
#define HAS_BYTECODE_VERSION
|
|
#endif
|
|
|
|
NSString *const RCTBundleURLProviderUpdatedNotification = @"RCTBundleURLProviderUpdatedNotification";
|
|
|
|
const NSUInteger kRCTBundleURLProviderDefaultPort = RCT_METRO_PORT;
|
|
|
|
static NSString *const kRCTJsLocationKey = @"RCT_jsLocation";
|
|
// This option is no longer exposed in the dev menu UI.
|
|
// It was renamed in D15958697 so it doesn't get stuck with no way to turn it off:
|
|
static NSString *const kRCTEnableLiveReloadKey = @"RCT_enableLiveReload_LEGACY";
|
|
static NSString *const kRCTEnableDevKey = @"RCT_enableDev";
|
|
static NSString *const kRCTEnableMinificationKey = @"RCT_enableMinification";
|
|
|
|
@implementation RCTBundleURLProvider
|
|
|
|
- (instancetype)init
|
|
{
|
|
self = [super init];
|
|
if (self) {
|
|
[self setDefaults];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (NSDictionary *)defaults
|
|
{
|
|
return @{
|
|
kRCTEnableLiveReloadKey : @NO,
|
|
kRCTEnableDevKey : @YES,
|
|
kRCTEnableMinificationKey : @NO,
|
|
};
|
|
}
|
|
|
|
- (void)settingsUpdated
|
|
{
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTBundleURLProviderUpdatedNotification object:self];
|
|
}
|
|
|
|
- (void)setDefaults
|
|
{
|
|
[[NSUserDefaults standardUserDefaults] registerDefaults:[self defaults]];
|
|
}
|
|
|
|
- (void)resetToDefaults
|
|
{
|
|
for (NSString *key in [[self defaults] allKeys]) {
|
|
[[NSUserDefaults standardUserDefaults] removeObjectForKey:key];
|
|
}
|
|
[self setDefaults];
|
|
[self settingsUpdated];
|
|
}
|
|
|
|
static NSURL *serverRootWithHostPort(NSString *hostPort)
|
|
{
|
|
if ([hostPort rangeOfString:@":"].location != NSNotFound) {
|
|
return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/", hostPort]];
|
|
}
|
|
return [NSURL
|
|
URLWithString:[NSString
|
|
stringWithFormat:@"http://%@:%lu/", hostPort, (unsigned long)kRCTBundleURLProviderDefaultPort]];
|
|
}
|
|
|
|
#if RCT_DEV_MENU
|
|
+ (BOOL)isPackagerRunning:(NSString *)host
|
|
{
|
|
NSURL *url = [serverRootWithHostPort(host) URLByAppendingPathComponent:@"status"];
|
|
|
|
NSURLSession *session = [NSURLSession sharedSession];
|
|
NSURLRequest *request = [NSURLRequest requestWithURL:url];
|
|
__block NSURLResponse *response;
|
|
__block NSData *data;
|
|
|
|
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
|
[[session dataTaskWithRequest:request
|
|
completionHandler:^(NSData *d, NSURLResponse *res, __unused NSError *err) {
|
|
data = d;
|
|
response = res;
|
|
dispatch_semaphore_signal(semaphore);
|
|
}] resume];
|
|
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
|
|
|
NSString *status = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
return [status isEqualToString:@"packager-status:running"];
|
|
}
|
|
|
|
- (NSString *)guessPackagerHost
|
|
{
|
|
static NSString *ipGuess;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
NSString *ipPath = [[NSBundle mainBundle] pathForResource:@"ip" ofType:@"txt"];
|
|
ipGuess =
|
|
[[NSString stringWithContentsOfFile:ipPath encoding:NSUTF8StringEncoding
|
|
error:nil] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
|
|
});
|
|
|
|
NSString *host = ipGuess ?: @"localhost";
|
|
if ([RCTBundleURLProvider isPackagerRunning:host]) {
|
|
return host;
|
|
}
|
|
return nil;
|
|
}
|
|
#else
|
|
+ (BOOL)isPackagerRunning:(NSString *)host
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
- (NSString *)packagerServerHost
|
|
{
|
|
NSString *location = [self jsLocation];
|
|
if (location) {
|
|
NSInteger index = [location rangeOfString:@":"].location;
|
|
if (index != NSNotFound) {
|
|
location = [location substringToIndex:index];
|
|
}
|
|
}
|
|
#if RCT_DEV_MENU
|
|
if ([location length] && ![RCTBundleURLProvider isPackagerRunning:location]) {
|
|
location = nil;
|
|
}
|
|
#endif
|
|
if (location != nil) {
|
|
return location;
|
|
}
|
|
#if RCT_DEV
|
|
NSString *host = [self guessPackagerHost];
|
|
if (host) {
|
|
return host;
|
|
}
|
|
#endif
|
|
return nil;
|
|
}
|
|
|
|
- (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot fallbackURLProvider:(NSURL * (^)(void))fallbackURLProvider
|
|
{
|
|
NSString *packagerServerHost = [self packagerServerHost];
|
|
if (!packagerServerHost) {
|
|
return fallbackURLProvider();
|
|
} else {
|
|
return [RCTBundleURLProvider jsBundleURLForBundleRoot:bundleRoot
|
|
packagerHost:packagerServerHost
|
|
enableDev:[self enableDev]
|
|
enableMinification:[self enableMinification]];
|
|
}
|
|
}
|
|
|
|
- (NSURL *)jsBundleURLForSplitBundleRoot:(NSString *)bundleRoot
|
|
{
|
|
return [RCTBundleURLProvider jsBundleURLForBundleRoot:bundleRoot
|
|
packagerHost:[self packagerServerHost]
|
|
enableDev:[self enableDev]
|
|
enableMinification:[self enableMinification]
|
|
modulesOnly:YES
|
|
runModule:NO];
|
|
}
|
|
|
|
- (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot
|
|
fallbackResource:(NSString *)resourceName
|
|
fallbackExtension:(NSString *)extension
|
|
{
|
|
return [self jsBundleURLForBundleRoot:bundleRoot
|
|
fallbackURLProvider:^NSURL * {
|
|
return [self jsBundleURLForFallbackResource:resourceName fallbackExtension:extension];
|
|
}];
|
|
}
|
|
|
|
- (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot fallbackResource:(NSString *)resourceName
|
|
{
|
|
return [self jsBundleURLForBundleRoot:bundleRoot fallbackResource:resourceName fallbackExtension:nil];
|
|
}
|
|
|
|
- (NSURL *)jsBundleURLForFallbackResource:(NSString *)resourceName fallbackExtension:(NSString *)extension
|
|
{
|
|
resourceName = resourceName ?: @"main";
|
|
extension = extension ?: @"jsbundle";
|
|
return [[NSBundle mainBundle] URLForResource:resourceName withExtension:extension];
|
|
}
|
|
|
|
- (NSURL *)resourceURLForResourceRoot:(NSString *)root
|
|
resourceName:(NSString *)name
|
|
resourceExtension:(NSString *)extension
|
|
offlineBundle:(NSBundle *)offlineBundle
|
|
{
|
|
NSString *packagerServerHost = [self packagerServerHost];
|
|
if (!packagerServerHost) {
|
|
// Serve offline bundle (local file)
|
|
NSBundle *bundle = offlineBundle ?: [NSBundle mainBundle];
|
|
return [bundle URLForResource:name withExtension:extension];
|
|
}
|
|
NSString *path = [NSString stringWithFormat:@"/%@/%@.%@", root, name, extension];
|
|
return [[self class] resourceURLForResourcePath:path packagerHost:packagerServerHost query:nil];
|
|
}
|
|
|
|
+ (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot
|
|
packagerHost:(NSString *)packagerHost
|
|
enableDev:(BOOL)enableDev
|
|
enableMinification:(BOOL)enableMinification
|
|
|
|
{
|
|
return [self jsBundleURLForBundleRoot:bundleRoot
|
|
packagerHost:packagerHost
|
|
enableDev:enableDev
|
|
enableMinification:enableMinification
|
|
modulesOnly:NO
|
|
runModule:YES];
|
|
}
|
|
|
|
+ (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot
|
|
packagerHost:(NSString *)packagerHost
|
|
enableDev:(BOOL)enableDev
|
|
enableMinification:(BOOL)enableMinification
|
|
modulesOnly:(BOOL)modulesOnly
|
|
runModule:(BOOL)runModule
|
|
{
|
|
NSString *path = [NSString stringWithFormat:@"/%@.bundle", bundleRoot];
|
|
#ifdef HAS_BYTECODE_VERSION
|
|
NSString *runtimeBytecodeVersion =
|
|
[NSString stringWithFormat:@"&runtimeBytecodeVersion=%u", facebook::hermes::HermesRuntime::getBytecodeVersion()];
|
|
#else
|
|
NSString *runtimeBytecodeVersion = @"";
|
|
#endif
|
|
|
|
// When we support only iOS 8 and above, use queryItems for a better API.
|
|
NSString *query = [NSString stringWithFormat:@"platform=ios&dev=%@&minify=%@&modulesOnly=%@&runModule=%@%@",
|
|
enableDev ? @"true" : @"false",
|
|
enableMinification ? @"true" : @"false",
|
|
modulesOnly ? @"true" : @"false",
|
|
runModule ? @"true" : @"false",
|
|
runtimeBytecodeVersion];
|
|
|
|
NSString *bundleID = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleIdentifierKey];
|
|
if (bundleID) {
|
|
query = [NSString stringWithFormat:@"%@&app=%@", query, bundleID];
|
|
}
|
|
return [[self class] resourceURLForResourcePath:path packagerHost:packagerHost query:query];
|
|
}
|
|
|
|
+ (NSURL *)resourceURLForResourcePath:(NSString *)path packagerHost:(NSString *)packagerHost query:(NSString *)query
|
|
{
|
|
NSURLComponents *components = [NSURLComponents componentsWithURL:serverRootWithHostPort(packagerHost)
|
|
resolvingAgainstBaseURL:NO];
|
|
components.path = path;
|
|
if (query != nil) {
|
|
components.query = query;
|
|
}
|
|
return components.URL;
|
|
}
|
|
|
|
- (void)updateValue:(id)object forKey:(NSString *)key
|
|
{
|
|
[[NSUserDefaults standardUserDefaults] setObject:object forKey:key];
|
|
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
[self settingsUpdated];
|
|
}
|
|
|
|
- (BOOL)enableDev
|
|
{
|
|
return [[NSUserDefaults standardUserDefaults] boolForKey:kRCTEnableDevKey];
|
|
}
|
|
|
|
- (BOOL)enableLiveReload
|
|
{
|
|
return [[NSUserDefaults standardUserDefaults] boolForKey:kRCTEnableLiveReloadKey];
|
|
}
|
|
|
|
- (BOOL)enableMinification
|
|
{
|
|
return [[NSUserDefaults standardUserDefaults] boolForKey:kRCTEnableMinificationKey];
|
|
}
|
|
|
|
- (NSString *)jsLocation
|
|
{
|
|
return [[NSUserDefaults standardUserDefaults] stringForKey:kRCTJsLocationKey];
|
|
}
|
|
|
|
- (void)setEnableDev:(BOOL)enableDev
|
|
{
|
|
[self updateValue:@(enableDev) forKey:kRCTEnableDevKey];
|
|
}
|
|
|
|
- (void)setEnableLiveReload:(BOOL)enableLiveReload
|
|
{
|
|
[self updateValue:@(enableLiveReload) forKey:kRCTEnableLiveReloadKey];
|
|
}
|
|
|
|
- (void)setJsLocation:(NSString *)jsLocation
|
|
{
|
|
[self updateValue:jsLocation forKey:kRCTJsLocationKey];
|
|
}
|
|
|
|
- (void)setEnableMinification:(BOOL)enableMinification
|
|
{
|
|
[self updateValue:@(enableMinification) forKey:kRCTEnableMinificationKey];
|
|
}
|
|
|
|
+ (instancetype)sharedSettings
|
|
{
|
|
static RCTBundleURLProvider *sharedInstance;
|
|
static dispatch_once_t once_token;
|
|
dispatch_once(&once_token, ^{
|
|
sharedInstance = [RCTBundleURLProvider new];
|
|
});
|
|
return sharedInstance;
|
|
}
|
|
|
|
@end
|