diff --git a/Examples/2048/2048/AppDelegate.m b/Examples/2048/2048/AppDelegate.m index 17eccb6b1..67c0f2d22 100644 --- a/Examples/2048/2048/AppDelegate.m +++ b/Examples/2048/2048/AppDelegate.m @@ -16,7 +16,6 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSURL *jsCodeLocation; - RCTRootView *rootView = [[RCTRootView alloc] init]; // Loading JavaScript code - uncomment the one you want. @@ -37,8 +36,9 @@ // and uncomment the next following line // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; - rootView.scriptURL = jsCodeLocation; - rootView.moduleName = @"Game2048"; + RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation + moduleName:@"Game2048" + launchOptions:launchOptions]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIViewController *rootViewController = [[UIViewController alloc] init]; diff --git a/Examples/Movies/Movies.xcodeproj/project.pbxproj b/Examples/Movies/Movies.xcodeproj/project.pbxproj index 3d22844d8..8dfb1be4e 100644 --- a/Examples/Movies/Movies.xcodeproj/project.pbxproj +++ b/Examples/Movies/Movies.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 140D9B661AC36C42004F25EE /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14312D241AC3654D00CDC950 /* libRCTLinking.a */; }; 58C5726B1AA6239E00CDF9C8 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58C5725B1AA6236500CDF9C8 /* libRCTText.a */; }; 58C5726C1AA623A200CDF9C8 /* libReactKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58C572681AA6236600CDF9C8 /* libReactKit.a */; }; /* End PBXBuildFile section */ @@ -32,6 +33,13 @@ remoteGlobalIDString = 58B5115D1A9E6B3D00147676; remoteInfo = RCTImage; }; + 14312D231AC3654D00CDC950 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 14312D1E1AC3654D00CDC950 /* RCTLinking.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTLinking; + }; 58C5725A1AA6236500CDF9C8 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 587650F61A9EB120008B8F17 /* RCTText.xcodeproj */; @@ -58,6 +66,7 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Movies/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Movies/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Movies/main.m; sourceTree = ""; }; + 14312D1E1AC3654D00CDC950 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../../Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = ""; }; 587650F61A9EB120008B8F17 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../Libraries/Text/RCTText.xcodeproj; sourceTree = SOURCE_ROOT; }; 587650F91A9EB120008B8F17 /* ReactKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactKit.xcodeproj; path = ../../ReactKit/ReactKit.xcodeproj; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ @@ -67,6 +76,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 140D9B661AC36C42004F25EE /* libRCTLinking.a in Frameworks */, 1341801E1AA91750003F314A /* libRCTNetwork.a in Frameworks */, 13442C061AA90EA00037E5B0 /* libRCTImage.a in Frameworks */, 58C5726B1AA6239E00CDF9C8 /* libRCTText.a in Frameworks */, @@ -106,9 +116,18 @@ name = Movies; sourceTree = ""; }; + 14312D1F1AC3654D00CDC950 /* Products */ = { + isa = PBXGroup; + children = ( + 14312D241AC3654D00CDC950 /* libRCTLinking.a */, + ); + name = Products; + sourceTree = ""; + }; 58C571FC1AA6124500CDF9C8 /* Libraries */ = { isa = PBXGroup; children = ( + 14312D1E1AC3654D00CDC950 /* RCTLinking.xcodeproj */, 134180151AA91740003F314A /* RCTNetwork.xcodeproj */, 13442C001AA90E7D0037E5B0 /* RCTImage.xcodeproj */, 587650F61A9EB120008B8F17 /* RCTText.xcodeproj */, @@ -195,6 +214,10 @@ ProductGroup = 13442C011AA90E7D0037E5B0 /* Products */; ProjectRef = 13442C001AA90E7D0037E5B0 /* RCTImage.xcodeproj */; }, + { + ProductGroup = 14312D1F1AC3654D00CDC950 /* Products */; + ProjectRef = 14312D1E1AC3654D00CDC950 /* RCTLinking.xcodeproj */; + }, { ProductGroup = 134180161AA91740003F314A /* Products */; ProjectRef = 134180151AA91740003F314A /* RCTNetwork.xcodeproj */; @@ -230,6 +253,13 @@ remoteRef = 13442C041AA90E7D0037E5B0 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 14312D241AC3654D00CDC950 /* libRCTLinking.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTLinking.a; + remoteRef = 14312D231AC3654D00CDC950 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 58C5725B1AA6236500CDF9C8 /* libRCTText.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -294,12 +324,10 @@ ); INFOPLIST_FILE = "$(SRCROOT)/Movies/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "/Users/nicklockwood/fbobjc-hg/Libraries/FBReactKit/js/react-native-github/Libraries/Network/build/Debug-iphoneos", - ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = Movies; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../Libraries/**"; }; name = Debug; }; @@ -314,12 +342,10 @@ ); INFOPLIST_FILE = "$(SRCROOT)/Movies/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "/Users/nicklockwood/fbobjc-hg/Libraries/FBReactKit/js/react-native-github/Libraries/Network/build/Debug-iphoneos", - ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = Movies; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../Libraries/**"; }; name = Release; }; diff --git a/Examples/Movies/Movies/AppDelegate.m b/Examples/Movies/Movies/AppDelegate.m index 51efb6ddc..f4c881ad8 100644 --- a/Examples/Movies/Movies/AppDelegate.m +++ b/Examples/Movies/Movies/AppDelegate.m @@ -9,6 +9,7 @@ #import "AppDelegate.h" +#import "RCTLinkingManager.h" #import "RCTRootView.h" @implementation AppDelegate @@ -16,7 +17,6 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSURL *jsCodeLocation; - RCTRootView *rootView = [[RCTRootView alloc] init]; // Loading JavaScript code - uncomment the one you want. @@ -37,8 +37,9 @@ // and uncomment the next following line // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; - rootView.scriptURL = jsCodeLocation; - rootView.moduleName = @"MoviesApp"; + RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation + moduleName:@"MoviesApp" + launchOptions:launchOptions]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIViewController *rootViewController = [[UIViewController alloc] init]; @@ -48,4 +49,15 @@ return YES; } +- (BOOL)application:(UIApplication *)application + openURL:(NSURL *)url + sourceApplication:(NSString *)sourceApplication + annotation:(id)annotation +{ + return [RCTLinkingManager application:application + openURL:url + sourceApplication:sourceApplication + annotation:annotation]; +} + @end diff --git a/Examples/Movies/Movies/Info.plist b/Examples/Movies/Movies/Info.plist index 3c7e8c72e..f76d0502a 100644 --- a/Examples/Movies/Movies/Info.plist +++ b/Examples/Movies/Movies/Info.plist @@ -18,6 +18,17 @@ 1.0 CFBundleSignature ???? + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + movies + + + CFBundleVersion 1 LSRequiresIPhoneOS @@ -28,13 +39,13 @@ armv7 - UIViewControllerBasedStatusBarAppearance - UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIViewControllerBasedStatusBarAppearance + diff --git a/Examples/Movies/SearchScreen.js b/Examples/Movies/SearchScreen.js index f9ef67b7c..b845c03a8 100644 --- a/Examples/Movies/SearchScreen.js +++ b/Examples/Movies/SearchScreen.js @@ -25,6 +25,8 @@ var TimerMixin = require('react-timer-mixin'); var MovieCell = require('./MovieCell'); var MovieScreen = require('./MovieScreen'); +var fetch = require('fetch'); + var API_URL = 'http://api.rottentomatoes.com/api/public/v1.0/'; var API_KEYS = ['7waqfqbprs7pajbz28mqf6vz', 'y4vwv8m33hed9ety83jmv52f']; diff --git a/Examples/SampleApp/.gitignore b/Examples/SampleApp/.gitignore new file mode 100644 index 000000000..c39012e9e --- /dev/null +++ b/Examples/SampleApp/.gitignore @@ -0,0 +1,27 @@ +# OSX +# +.DS_Store + +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate + +# node.js +# +node_modules/ +npm-debug.log diff --git a/Examples/SampleApp/iOS/AppDelegate.m b/Examples/SampleApp/iOS/AppDelegate.m index 9a2016c93..1d8049ac6 100644 --- a/Examples/SampleApp/iOS/AppDelegate.m +++ b/Examples/SampleApp/iOS/AppDelegate.m @@ -16,7 +16,6 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSURL *jsCodeLocation; - RCTRootView *rootView = [[RCTRootView alloc] init]; // Loading JavaScript code - uncomment the one you want. @@ -37,8 +36,9 @@ // and uncomment the next following line // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; - rootView.scriptURL = jsCodeLocation; - rootView.moduleName = @"SampleApp"; + RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation + moduleName:@"SampleApp" + launchOptions:launchOptions]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIViewController *rootViewController = [[UIViewController alloc] init]; diff --git a/Examples/TicTacToe/TicTacToe/AppDelegate.m b/Examples/TicTacToe/TicTacToe/AppDelegate.m index b89ad0225..08dfc6e6a 100644 --- a/Examples/TicTacToe/TicTacToe/AppDelegate.m +++ b/Examples/TicTacToe/TicTacToe/AppDelegate.m @@ -16,7 +16,6 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSURL *jsCodeLocation; - RCTRootView *rootView = [[RCTRootView alloc] init]; // Loading JavaScript code - uncomment the one you want. @@ -37,8 +36,9 @@ // and uncomment the next following line // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; - rootView.scriptURL = jsCodeLocation; - rootView.moduleName = @"TicTacToeApp"; + RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation + moduleName:@"TicTacToeApp" + launchOptions:launchOptions]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIViewController *rootViewController = [[UIViewController alloc] init]; diff --git a/Examples/UIExplorer/UIExplorer/AppDelegate.m b/Examples/UIExplorer/UIExplorer/AppDelegate.m index c25414370..f0cf25d21 100644 --- a/Examples/UIExplorer/UIExplorer/AppDelegate.m +++ b/Examples/UIExplorer/UIExplorer/AppDelegate.m @@ -16,7 +16,6 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSURL *jsCodeLocation; - RCTRootView *rootView = [[RCTRootView alloc] init]; // Loading JavaScript code - uncomment the one you want. @@ -37,8 +36,9 @@ // and uncomment the next following line // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; - rootView.scriptURL = jsCodeLocation; - rootView.moduleName = @"UIExplorerApp"; + RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation + moduleName:@"UIExplorerApp" + launchOptions:launchOptions]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIViewController *rootViewController = [[UIViewController alloc] init]; diff --git a/IntegrationTests/AppDelegate.m b/IntegrationTests/AppDelegate.m index 3d4087ba6..e5466b235 100644 --- a/IntegrationTests/AppDelegate.m +++ b/IntegrationTests/AppDelegate.m @@ -16,7 +16,6 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSURL *jsCodeLocation; - RCTRootView *rootView = [[RCTRootView alloc] init]; // Loading JavaScript code - uncomment the one you want. @@ -37,8 +36,9 @@ // and uncomment the next following line // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; - rootView.scriptURL = jsCodeLocation; - rootView.moduleName = @"IntegrationTestsApp"; + RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation + moduleName:@"IntegrationTestsApp" + launchOptions:launchOptions]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIViewController *rootViewController = [[UIViewController alloc] init]; diff --git a/Libraries/LinkingIOS/LinkingIOS.js b/Libraries/LinkingIOS/LinkingIOS.js new file mode 100644 index 000000000..473bd0fa3 --- /dev/null +++ b/Libraries/LinkingIOS/LinkingIOS.js @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule LinkingIOS + * @flow + */ +'use strict'; + +var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); +var RCTLinkingManager = require('NativeModules').LinkingManager; +var invariant = require('invariant'); + +var _notifHandlers = {}; +var _initialURL = RCTLinkingManager && + RCTLinkingManager.initialURL; + +var DEVICE_NOTIF_EVENT = 'openURL'; + +class LinkingIOS { + static addEventListener(type, handler) { + invariant( + type === 'url', + 'LinkingIOS only supports `url` events' + ); + _notifHandlers[handler] = RCTDeviceEventEmitter.addListener( + DEVICE_NOTIF_EVENT, + (notifData) => { + handler(new LinkingIOS(notifData)); + } + ); + } + + static removeEventListener(type, handler) { + invariant( + type === 'url', + 'LinkingIOS only supports `url` events' + ); + if (!_notifHandlers[handler]) { + return; + } + _notifHandlers[handler].remove(); + _notifHandlers[handler] = null; + } + + static openURL(url) { + invariant( + typeof url === 'string', + 'Invalid url: should be a string' + ); + RCTLinkingManager.openURL(url); + } + + static canOpenURL(url, callback) { + invariant( + typeof url === 'string', + 'Invalid url: should be a string' + ); + invariant( + typeof callback === 'function', + 'A valid callback function is required' + ); + RCTLinkingManager.canOpenURL(url, callback); + } + + static popInitialURL() { + var initialURL = _initialURL; + _initialURL = null; + return initialURL; + } +} + +module.exports = LinkingIOS; diff --git a/Libraries/LinkingIOS/RCTLinking.xcodeproj/project.pbxproj b/Libraries/LinkingIOS/RCTLinking.xcodeproj/project.pbxproj new file mode 100644 index 000000000..0d9e593d2 --- /dev/null +++ b/Libraries/LinkingIOS/RCTLinking.xcodeproj/project.pbxproj @@ -0,0 +1,256 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 148699CF1ABD045300480536 /* RCTLinkingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 148699CE1ABD045300480536 /* RCTLinkingManager.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 58B511D91A9E6C8500147676 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 134814201AA4EA6300B7C361 /* libRCTLinking.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTLinking.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 148699CD1ABD045300480536 /* RCTLinkingManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTLinkingManager.h; sourceTree = ""; }; + 148699CE1ABD045300480536 /* RCTLinkingManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTLinkingManager.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 58B511D81A9E6C8500147676 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 134814211AA4EA7D00B7C361 /* Products */ = { + isa = PBXGroup; + children = ( + 134814201AA4EA6300B7C361 /* libRCTLinking.a */, + ); + name = Products; + sourceTree = ""; + }; + 58B511D21A9E6C8500147676 = { + isa = PBXGroup; + children = ( + 148699CD1ABD045300480536 /* RCTLinkingManager.h */, + 148699CE1ABD045300480536 /* RCTLinkingManager.m */, + 134814211AA4EA7D00B7C361 /* Products */, + ); + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 58B511DA1A9E6C8500147676 /* RCTLinking */ = { + isa = PBXNativeTarget; + buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTLinking" */; + buildPhases = ( + 58B511D71A9E6C8500147676 /* Sources */, + 58B511D81A9E6C8500147676 /* Frameworks */, + 58B511D91A9E6C8500147676 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = RCTLinking; + productName = RCTDataManager; + productReference = 134814201AA4EA6300B7C361 /* libRCTLinking.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 58B511D31A9E6C8500147676 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0610; + ORGANIZATIONNAME = Facebook; + TargetAttributes = { + 58B511DA1A9E6C8500147676 = { + CreatedOnToolsVersion = 6.1.1; + }; + }; + }; + buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTLinking" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 58B511D21A9E6C8500147676; + productRefGroup = 58B511D21A9E6C8500147676; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 58B511DA1A9E6C8500147676 /* RCTLinking */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 58B511D71A9E6C8500147676 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 148699CF1ABD045300480536 /* RCTLinkingManager.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 58B511ED1A9E6C8500147676 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.1; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 58B511EE1A9E6C8500147676 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.1; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 58B511F01A9E6C8500147676 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../../ReactKit/**", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "/Users/nicklockwood/fbobjc-hg/Libraries/FBReactKit/js/react-native-github/ReactKit/build/Debug-iphoneos", + ); + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = RCTLinking; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 58B511F11A9E6C8500147676 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../../ReactKit/**", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "/Users/nicklockwood/fbobjc-hg/Libraries/FBReactKit/js/react-native-github/ReactKit/build/Debug-iphoneos", + ); + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = RCTLinking; + SKIP_INSTALL = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTLinking" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 58B511ED1A9E6C8500147676 /* Debug */, + 58B511EE1A9E6C8500147676 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTLinking" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 58B511F01A9E6C8500147676 /* Debug */, + 58B511F11A9E6C8500147676 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 58B511D31A9E6C8500147676 /* Project object */; +} diff --git a/Libraries/LinkingIOS/RCTLinkingManager.h b/Libraries/LinkingIOS/RCTLinkingManager.h new file mode 100644 index 000000000..f2ec63215 --- /dev/null +++ b/Libraries/LinkingIOS/RCTLinkingManager.h @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "RCTBridgeModule.h" + +@interface RCTLinkingManager : NSObject + ++ (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation; + +@end diff --git a/Libraries/LinkingIOS/RCTLinkingManager.m b/Libraries/LinkingIOS/RCTLinkingManager.m new file mode 100644 index 000000000..565469c5d --- /dev/null +++ b/Libraries/LinkingIOS/RCTLinkingManager.m @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTLinkingManager.h" + +#import "RCTBridge.h" +#import "RCTEventDispatcher.h" + +NSString *const RCTOpenURLNotification = @"RCTOpenURLNotification"; + +@implementation RCTLinkingManager + +@synthesize bridge = _bridge; + +- (instancetype)init +{ + if ((self = [super init])) { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleOpenURLNotification:) + name:RCTOpenURLNotification + object:nil]; + } + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + ++ (BOOL)application:(UIApplication *)application + openURL:(NSURL *)url + sourceApplication:(NSString *)sourceApplication + annotation:(id)annotation +{ + NSDictionary *payload = @{@"url": [url absoluteString]}; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTOpenURLNotification + object:self + userInfo:payload]; + return YES; +} + +- (void)handleOpenURLNotification:(NSNotification *)notification +{ + [_bridge.eventDispatcher sendDeviceEventWithName:@"openURL" + body:[notification userInfo]]; +} + +- (void)openURL:(NSString *)url +{ + RCT_EXPORT(); + + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]]; +} + +- (void)canOpenURL:(NSString *)url + callback:(RCTResponseSenderBlock)callback +{ + RCT_EXPORT(); + + BOOL supported = [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:url]]; + callback(@[@(supported)]); +} + +- (NSDictionary *)constantsToExport +{ + return @{ + @"initialURL": [[_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsURLKey] absoluteString] ?: [NSNull null] + }; +} + +@end diff --git a/Libraries/PushNotificationIOS/RCTPushNotificationManager.h b/Libraries/PushNotificationIOS/RCTPushNotificationManager.h index b8ddd19d9..b60a646e4 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotificationManager.h +++ b/Libraries/PushNotificationIOS/RCTPushNotificationManager.h @@ -17,6 +17,5 @@ + (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings; + (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification; -+ (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation; @end diff --git a/Libraries/PushNotificationIOS/RCTPushNotificationManager.m b/Libraries/PushNotificationIOS/RCTPushNotificationManager.m index 396939ea5..6a54e457c 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotificationManager.m +++ b/Libraries/PushNotificationIOS/RCTPushNotificationManager.m @@ -13,7 +13,6 @@ #import "RCTEventDispatcher.h" NSString *const RCTRemoteNotificationReceived = @"RemoteNotificationReceived"; -NSString *const RCTOpenURLNotification = @"RCTOpenURLNotification"; @implementation RCTPushNotificationManager { @@ -35,10 +34,6 @@ NSString *const RCTOpenURLNotification = @"RCTOpenURLNotification"; selector:@selector(handleRemoteNotificationReceived:) name:RCTRemoteNotificationReceived object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleOpenURLNotification:) - name:RCTOpenURLNotification - object:nil]; } return self; } @@ -62,30 +57,12 @@ NSString *const RCTOpenURLNotification = @"RCTOpenURLNotification"; userInfo:notification]; } -+ (BOOL)application:(UIApplication *)application - openURL:(NSURL *)url - sourceApplication:(NSString *)sourceApplication - annotation:(id)annotation -{ - NSDictionary *payload = @{@"url": [url absoluteString]}; - [[NSNotificationCenter defaultCenter] postNotificationName:RCTOpenURLNotification - object:self - userInfo:payload]; - return YES; -} - - (void)handleRemoteNotificationReceived:(NSNotification *)notification { [_bridge.eventDispatcher sendDeviceEventWithName:@"remoteNotificationReceived" body:[notification userInfo]]; } -- (void)handleOpenURLNotification:(NSNotification *)notification -{ - [_bridge.eventDispatcher sendDeviceEventWithName:@"openURL" - body:[notification userInfo]]; -} - /** * Update the application icon badge number on the home screen */ diff --git a/Libraries/RCTTest/RCTTestRunner.m b/Libraries/RCTTest/RCTTestRunner.m index a6b710e3b..27aac48d9 100644 --- a/Libraries/RCTTest/RCTTestRunner.m +++ b/Libraries/RCTTest/RCTTestRunner.m @@ -17,6 +17,15 @@ #define TIMEOUT_SECONDS 240 +@interface RCTRootView (Testing) + +- (instancetype)_initWithBundleURL:(NSURL *)bundleURL + moduleName:(NSString *)moduleName + launchOptions:(NSDictionary *)launchOptions + moduleProvider:(RCTBridgeModuleProviderBlock)moduleProvider; + +@end + @implementation RCTTestRunner { FBSnapshotTestController *_snapshotController; @@ -63,16 +72,20 @@ [(RCTRootView *)vc.view invalidate]; // Make sure the normal app view doesn't interfere } vc.view = [[UIView alloc] init]; - RCTRootView *rootView = [[RCTRootView alloc] initWithFrame:CGRectMake(0, 0, 320, 2000)]; // Constant size for testing on multiple devices - RCTTestModule *testModule = [[RCTTestModule alloc] initWithSnapshotController:_snapshotController view:rootView]; + + RCTTestModule *testModule = [[RCTTestModule alloc] initWithSnapshotController:_snapshotController view:nil]; testModule.testSelector = test; + + RCTRootView *rootView = [[RCTRootView alloc] _initWithBundleURL:[NSURL URLWithString:_script] + moduleName:moduleName + launchOptions:nil + moduleProvider:^{ + return @[testModule]; + }]; + [testModule setValue:rootView forKey:@"_view"]; + rootView.frame = CGRectMake(0, 0, 320, 2000); [vc.view addSubview:rootView]; // Add as subview so it doesn't get resized - rootView.moduleProvider = ^(void){ - return @[testModule]; - }; - rootView.moduleName = moduleName; rootView.initialProperties = initialProps; - rootView.scriptURL = [NSURL URLWithString:_script]; NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; NSString *error = [[RCTRedBox sharedInstance] currentErrorMessage]; diff --git a/Libraries/react-native/react-native.js b/Libraries/react-native/react-native.js index c846d0a82..557f4f3f2 100644 --- a/Libraries/react-native/react-native.js +++ b/Libraries/react-native/react-native.js @@ -47,6 +47,7 @@ var ReactNative = Object.assign(Object.create(require('React')), { AsyncStorage: require('AsyncStorage'), CameraRoll: require('CameraRoll'), InteractionManager: require('InteractionManager'), + LinkingIOS: require('LinkingIOS'), LayoutAnimation: require('LayoutAnimation'), NetInfo: require('NetInfo'), PixelRatio: require('PixelRatio'), diff --git a/ReactKit/Base/RCTBridge.h b/ReactKit/Base/RCTBridge.h index 2506b91c2..7aa28e49b 100644 --- a/ReactKit/Base/RCTBridge.h +++ b/ReactKit/Base/RCTBridge.h @@ -37,8 +37,9 @@ typedef NSArray *(^RCTBridgeModuleProviderBlock)(void); * array of pre-initialized module instances if they require additional init * parameters or configuration. */ -- (instancetype)initWithExecutor:(id)executor - moduleProvider:(RCTBridgeModuleProviderBlock)block NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithBundlePath:(NSString *)bundlepath + moduleProvider:(RCTBridgeModuleProviderBlock)block + launchOptions:(NSDictionary *)launchOptions NS_DESIGNATED_INITIALIZER; /** * This method is used to call functions in the JavaScript application context. @@ -80,6 +81,9 @@ typedef NSArray *(^RCTBridgeModuleProviderBlock)(void); */ + (void)log:(NSArray *)objects level:(NSString *)level; +@property (nonatomic, copy, readonly) NSDictionary *launchOptions; + + /** * Method to check that a valid executor exists with which to log */ diff --git a/ReactKit/Base/RCTBridge.m b/ReactKit/Base/RCTBridge.m index c95a15952..fe13a1bfd 100644 --- a/ReactKit/Base/RCTBridge.m +++ b/ReactKit/Base/RCTBridge.m @@ -497,77 +497,89 @@ static NSDictionary *RCTLocalModulesConfig() RCTSparseArray *_modulesByID; NSDictionary *_modulesByName; id _javaScriptExecutor; + RCTBridgeModuleProviderBlock _moduleProvider; } static id _latestJSExecutor; -- (instancetype)initWithExecutor:(id)executor - moduleProvider:(RCTBridgeModuleProviderBlock)block +- (instancetype)initWithBundlePath:(NSString *)bundlepath + moduleProvider:(RCTBridgeModuleProviderBlock)block + launchOptions:(NSDictionary *)launchOptions { if ((self = [super init])) { - _javaScriptExecutor = executor; - _latestJSExecutor = _javaScriptExecutor; _eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self]; _shadowQueue = dispatch_queue_create("com.facebook.ReactKit.ShadowQueue", DISPATCH_QUEUE_SERIAL); + _moduleProvider = block; + _launchOptions = launchOptions; + } + return self; +} - // Register passed-in module instances - NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc] init]; - for (id module in block ? block() : nil) { - preregisteredModules[RCTModuleNameForClass([module class])] = module; - } +- (void)setJavaScriptExecutor:(id)executor +{ + _javaScriptExecutor = executor; + _latestJSExecutor = _javaScriptExecutor; + [self setUp]; +} - // Instantiate modules - _modulesByID = [[RCTSparseArray alloc] init]; - NSMutableDictionary *modulesByName = [preregisteredModules mutableCopy]; - [RCTBridgeModuleClassesByModuleID() enumerateObjectsUsingBlock:^(Class moduleClass, NSUInteger moduleID, BOOL *stop) { - NSString *moduleName = RCTModuleNamesByID[moduleID]; - // Check if module instance has already been registered for this name - if ((_modulesByID[moduleID] = modulesByName[moduleName])) { - // Preregistered instances takes precedence, no questions asked - if (!preregisteredModules[moduleName]) { - // It's OK to have a name collision as long as the second instance is nil - RCTAssert([[moduleClass alloc] init] == nil, - @"Attempted to register RCTBridgeModule class %@ for the name '%@', \ - but name was already registered by class %@", moduleClass, - moduleName, [modulesByName[moduleName] class]); - } - } else { - // Module name hasn't been used before, so go ahead and instantiate - id module = [[moduleClass alloc] init]; - if (module) { - _modulesByID[moduleID] = modulesByName[moduleName] = module; - } +- (void)setUp +{ + // Register passed-in module instances + NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc] init]; + for (id module in _moduleProvider ? _moduleProvider() : nil) { + preregisteredModules[RCTModuleNameForClass([module class])] = module; + } + + // Instantiate modules + _modulesByID = [[RCTSparseArray alloc] init]; + NSMutableDictionary *modulesByName = [preregisteredModules mutableCopy]; + [RCTBridgeModuleClassesByModuleID() enumerateObjectsUsingBlock:^(Class moduleClass, NSUInteger moduleID, BOOL *stop) { + NSString *moduleName = RCTModuleNamesByID[moduleID]; + // Check if module instance has already been registered for this name + if ((_modulesByID[moduleID] = modulesByName[moduleName])) { + // Preregistered instances takes precedence, no questions asked + if (!preregisteredModules[moduleName]) { + // It's OK to have a name collision as long as the second instance is nil + RCTAssert([[moduleClass alloc] init] == nil, + @"Attempted to register RCTBridgeModule class %@ for the name '%@', \ + but name was already registered by class %@", moduleClass, + moduleName, [modulesByName[moduleName] class]); } - }]; - - // Store modules - _modulesByName = [modulesByName copy]; - - // Set bridge - for (id module in _modulesByName.allValues) { - if ([module respondsToSelector:@selector(setBridge:)]) { - module.bridge = self; + } else { + // Module name hasn't been used before, so go ahead and instantiate + id module = [[moduleClass alloc] init]; + if (module) { + _modulesByID[moduleID] = modulesByName[moduleName] = module; } } + }]; - // Inject module data into JS context - NSString *configJSON = RCTJSONStringify(@{ - @"remoteModuleConfig": RCTRemoteModulesConfig(_modulesByName), - @"localModulesConfig": RCTLocalModulesConfig() - }, NULL); - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - [_javaScriptExecutor injectJSONText:configJSON asGlobalObjectNamed:@"__fbBatchedBridgeConfig" callback:^(id err) { - dispatch_semaphore_signal(semaphore); - }]; + // Store modules + _modulesByName = [modulesByName copy]; - if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC)) != 0) { - RCTLogError(@"JavaScriptExecutor took too long to inject JSON object"); + // Set bridge + for (id module in _modulesByName.allValues) { + if ([module respondsToSelector:@selector(setBridge:)]) { + module.bridge = self; } } - return self; + // Inject module data into JS context + NSString *configJSON = RCTJSONStringify(@{ + @"remoteModuleConfig": RCTRemoteModulesConfig(_modulesByName), + @"localModulesConfig": RCTLocalModulesConfig() + }, NULL); + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + [_javaScriptExecutor injectJSONText:configJSON asGlobalObjectNamed:@"__fbBatchedBridgeConfig" callback:^(id err) { + dispatch_semaphore_signal(semaphore); + }]; + + if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC)) != 0) { + RCTLogError(@"JavaScriptExecutor took too long to inject JSON object"); + } } + - (NSDictionary *)modules { RCTAssert(_modulesByName != nil, @"Bridge modules have not yet been initialized. \ diff --git a/ReactKit/Base/RCTRootView.h b/ReactKit/Base/RCTRootView.h index ef72d374b..e5776cc6f 100644 --- a/ReactKit/Base/RCTRootView.h +++ b/ReactKit/Base/RCTRootView.h @@ -13,12 +13,16 @@ @interface RCTRootView : UIView +- (instancetype)initWithBundleURL:(NSURL *)bundleURL + moduleName:(NSString *)moduleName + launchOptions:(NSDictionary *)launchOptions /* NS_DESIGNATED_INITIALIZER */; + /** * The URL of the bundled application script (required). * Setting this will clear the view contents, and trigger * an asynchronous load/download and execution of the script. */ -@property (nonatomic, strong) NSURL *scriptURL; +@property (nonatomic, strong, readonly) NSURL *scriptURL; /** * The name of the JavaScript module to execute within the @@ -26,14 +30,14 @@ * any immediate effect, but it must be done prior to loading * the script. */ -@property (nonatomic, copy) NSString *moduleName; +@property (nonatomic, copy, readonly) NSString *moduleName; /** * A block that returns an array of pre-allocated modules. These * modules will take precedence over any automatically registered * modules of the same name. */ -@property (nonatomic, copy) RCTBridgeModuleProviderBlock moduleProvider; +@property (nonatomic, copy, readonly) RCTBridgeModuleProviderBlock moduleProvider; /** * The default properties to apply to the view when the script bundle diff --git a/ReactKit/Base/RCTRootView.m b/ReactKit/Base/RCTRootView.m index 7375b77ba..7f60598c3 100644 --- a/ReactKit/Base/RCTRootView.m +++ b/ReactKit/Base/RCTRootView.m @@ -25,6 +25,16 @@ NSString *const RCTReloadNotification = @"RCTReloadNotification"; +/** + * HACK(t6568049) This should be removed soon, hiding to prevent people from + * relying on it + */ +@interface RCTBridge (RCTRootView) + +- (void)setJavaScriptExecutor:(id)executor; + +@end + @implementation RCTRootView { RCTDevMenu *_devMenu; @@ -32,6 +42,7 @@ NSString *const RCTReloadNotification = @"RCTReloadNotification"; RCTTouchHandler *_touchHandler; id _executor; BOOL _registered; + NSDictionary *_launchOptions; } static Class _globalExecutorClass; @@ -63,19 +74,35 @@ static Class _globalExecutorClass; } -- (id)initWithCoder:(NSCoder *)aDecoder +- (instancetype)initWithBundleURL:(NSURL *)bundleURL + moduleName:(NSString *)moduleName + launchOptions:(NSDictionary *)launchOptions { - if ((self = [super initWithCoder:aDecoder])) { + if ((self = [super init])) { + RCTAssert(bundleURL, @"A bundleURL is required to create an RCTRootView"); + RCTAssert(moduleName, @"A bundleURL is required to create an RCTRootView"); + _moduleName = moduleName; + _launchOptions = launchOptions; [self setUp]; + [self setScriptURL:bundleURL]; } return self; } -- (instancetype)initWithFrame:(CGRect)frame + /** + * HACK(t6568049) Private constructor for testing purposes + */ +- (instancetype)_initWithBundleURL:(NSURL *)bundleURL + moduleName:(NSString *)moduleName + launchOptions:(NSDictionary *)launchOptions + moduleProvider:(RCTBridgeModuleProviderBlock)moduleProvider { - if ((self = [super initWithFrame:frame])) { - self.backgroundColor = [UIColor whiteColor]; + if ((self = [super init])) { + _moduleProvider = moduleProvider; + _moduleName = moduleName; + _launchOptions = launchOptions; [self setUp]; + [self setScriptURL:bundleURL]; } return self; } @@ -89,6 +116,7 @@ static Class _globalExecutorClass; #ifdef DEBUG self.enableDevMenu = YES; #endif + self.backgroundColor = [UIColor whiteColor]; rootViewTag += 10; // Add reload observer @@ -195,7 +223,16 @@ static Class _globalExecutorClass; // Choose local executor if specified, followed by global, followed by default _executor = [[_executorClass ?: _globalExecutorClass ?: [RCTContextExecutor class] alloc] init]; - _bridge = [[RCTBridge alloc] initWithExecutor:_executor moduleProvider:_moduleProvider]; + + /** + * HACK(t6568049) Most of the properties passed into the bridge are not used + * right now but it'll be changed soon so it's here for convenience. + */ + _bridge = [[RCTBridge alloc] initWithBundlePath:_scriptURL.absoluteString + moduleProvider:_moduleProvider + launchOptions:_launchOptions]; + [_bridge setJavaScriptExecutor:_executor]; + _touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge]; [self addGestureRecognizer:_touchHandler]; diff --git a/package.json b/package.json index 201942427..3cc2ae49c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native", - "version": "0.1.0", + "version": "0.0.2", "description": "Build native apps with React!", "repository": { "type": "git", @@ -21,12 +21,25 @@ ] }, "main": "Libraries/react-native/react-native.js", + "files": [ + "ReactKit", + "Examples/SampleApp", + "Libraries", + "packager", + "cli.js", + "init.sh", + "LICENSE", + "PATENTS" + ], "scripts": { "test": "jest", "lint": "node linter.js Examples/", "start": "./packager/packager.sh", "postinstall": "cd packager && npm install" }, + "bin": { + "react-native-start": "packager/packager.sh" + }, "dependencies": { "connect": "2.8.3", "jstransform": "10.0.1", @@ -37,7 +50,6 @@ "stacktrace-parser": "0.1.1" }, "devDependencies": { - "ws": "0.4.31", "jest-cli": "0.2.1", "eslint": "0.9.2" } diff --git a/packager/package.json b/packager/package.json index 0afcd3c3c..6bfb6a2f8 100644 --- a/packager/package.json +++ b/packager/package.json @@ -36,7 +36,8 @@ "uglify-js": "~2.4.16", "underscore": "1.7.0", "worker-farm": "1.1.0", - "yargs": "1.3.2" + "yargs": "1.3.2", + "ws": "0.4.31" }, "devDependencies": { "jest-cli": "0.2.1",