diff --git a/Examples/HybridMobileDeployCompanion/.npmignore b/.npmignore similarity index 87% rename from Examples/HybridMobileDeployCompanion/.npmignore rename to .npmignore index c39012e9e..2fd920548 100644 --- a/Examples/HybridMobileDeployCompanion/.npmignore +++ b/.npmignore @@ -25,3 +25,6 @@ DerivedData # node_modules/ npm-debug.log + +# Don't publish example apps +Examples/ diff --git a/Examples/HybridMobileDeployCompanion/.gitignore b/Examples/HybridMobileDeployCompanion/.gitignore index c39012e9e..231273bb1 100644 --- a/Examples/HybridMobileDeployCompanion/.gitignore +++ b/Examples/HybridMobileDeployCompanion/.gitignore @@ -25,3 +25,5 @@ DerivedData # node_modules/ npm-debug.log + +main.jsbundle diff --git a/Examples/HybridMobileDeployCompanion/HybridMobileDeployCompanion.xcodeproj/project.pbxproj b/Examples/HybridMobileDeployCompanion/HybridMobileDeployCompanion.xcodeproj/project.pbxproj index 98e9369fa..6acd3c6ea 100644 --- a/Examples/HybridMobileDeployCompanion/HybridMobileDeployCompanion.xcodeproj/project.pbxproj +++ b/Examples/HybridMobileDeployCompanion/HybridMobileDeployCompanion.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; + 81551E1B1B3B428000F5B9F1 /* libHybridMobileDeploy.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 81551E0F1B3B427200F5B9F1 /* libHybridMobileDeploy.a */; }; 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; /* End PBXBuildFile section */ @@ -104,6 +105,13 @@ remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteInfo = RCTLinking; }; + 81551E0E1B3B427200F5B9F1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 81551E0A1B3B427200F5B9F1 /* HybridMobileDeploy.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = HybridMobileDeploy; + }; 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; @@ -115,17 +123,17 @@ /* Begin PBXFileReference section */ 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = main.jsbundle; path = iOS/main.jsbundle; sourceTree = ""; }; - 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = ""; }; - 00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAdSupport.xcodeproj; path = node_modules/react-native/Libraries/AdSupport/RCTAdSupport.xcodeproj; sourceTree = ""; }; - 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = ""; }; - 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = node_modules/react-native/Libraries/Image/RCTImage.xcodeproj; sourceTree = ""; }; - 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj; sourceTree = ""; }; - 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj; sourceTree = ""; }; + 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = ""; }; + 00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAdSupport.xcodeproj; path = "node_modules/react-native/Libraries/AdSupport/RCTAdSupport.xcodeproj"; sourceTree = ""; }; + 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = ""; }; + 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = ""; }; + 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = ""; }; + 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = ""; }; 00E356EE1AD99517003FC87E /* HybridMobileDeployCompanionTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HybridMobileDeployCompanionTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 00E356F21AD99517003FC87E /* HybridMobileDeployCompanionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HybridMobileDeployCompanionTests.m; sourceTree = ""; }; - 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj; sourceTree = ""; }; - 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj; sourceTree = ""; }; + 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = ""; }; + 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* HybridMobileDeployCompanion.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HybridMobileDeployCompanion.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = iOS/AppDelegate.h; sourceTree = ""; }; 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = iOS/AppDelegate.m; sourceTree = ""; }; @@ -133,9 +141,10 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = iOS/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = iOS/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = iOS/main.m; sourceTree = ""; }; - 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = node_modules/react-native/React/React.xcodeproj; sourceTree = ""; }; - 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = ""; }; - 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = node_modules/react-native/Libraries/Text/RCTText.xcodeproj; sourceTree = ""; }; + 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; + 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; + 81551E0A1B3B427200F5B9F1 /* HybridMobileDeploy.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = HybridMobileDeploy.xcodeproj; path = ../../HybridMobileDeploy.xcodeproj; sourceTree = ""; }; + 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -161,6 +170,7 @@ 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */, 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */, 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, + 81551E1B1B3B428000F5B9F1 /* libHybridMobileDeploy.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -278,9 +288,18 @@ name = Products; sourceTree = ""; }; + 81551E0B1B3B427200F5B9F1 /* Products */ = { + isa = PBXGroup; + children = ( + 81551E0F1B3B427200F5B9F1 /* libHybridMobileDeploy.a */, + ); + name = Products; + sourceTree = ""; + }; 832341AE1AAA6A7D00B99B32 /* Libraries */ = { isa = PBXGroup; children = ( + 81551E0A1B3B427200F5B9F1 /* HybridMobileDeploy.xcodeproj */, 146833FF1AC3E56700842450 /* React.xcodeproj */, 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, 00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */, @@ -390,6 +409,10 @@ productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; projectDirPath = ""; projectReferences = ( + { + ProductGroup = 81551E0B1B3B427200F5B9F1 /* Products */; + ProjectRef = 81551E0A1B3B427200F5B9F1 /* HybridMobileDeploy.xcodeproj */; + }, { ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; @@ -514,6 +537,13 @@ remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 81551E0F1B3B427200F5B9F1 /* libHybridMobileDeploy.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libHybridMobileDeploy.a; + remoteRef = 81551E0E1B3B427200F5B9F1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 832341B51AAA6A8300B99B32 /* libRCTText.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -629,6 +659,7 @@ "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "$(SRCROOT)/node_modules/react-native/React/**", + "$(SRCROOT)/../..", ); INFOPLIST_FILE = "$(SRCROOT)/iOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; @@ -645,6 +676,7 @@ "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "$(SRCROOT)/node_modules/react-native/React/**", + "$(SRCROOT)/../..", ); INFOPLIST_FILE = "$(SRCROOT)/iOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; diff --git a/Examples/HybridMobileDeployCompanion/iOS/AppDelegate.m b/Examples/HybridMobileDeployCompanion/iOS/AppDelegate.m index d1c70518a..67cdc4dcb 100644 --- a/Examples/HybridMobileDeployCompanion/iOS/AppDelegate.m +++ b/Examples/HybridMobileDeployCompanion/iOS/AppDelegate.m @@ -11,6 +11,8 @@ #import "RCTRootView.h" +#import "HybridMobileDeploy.h" + @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions @@ -31,7 +33,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( * on the same Wi-Fi network. */ - jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle"]; + //jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle"]; /** * OPTION 2 @@ -42,8 +44,8 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( * * see http://facebook.github.io/react-native/docs/runningondevice.html */ - -// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; + + jsCodeLocation = [HybridMobileDeploy appBundleUrl]; RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"HybridMobileDeployCompanion" diff --git a/Examples/HybridMobileDeployCompanion/iOS/main.jsbundle b/Examples/HybridMobileDeployCompanion/iOS/main.jsbundle deleted file mode 100644 index b702b30c6..000000000 --- a/Examples/HybridMobileDeployCompanion/iOS/main.jsbundle +++ /dev/null @@ -1,8 +0,0 @@ -// Offline JS -// To re-generate the offline bundle, run this from the root of your project: -// -// $ react-native bundle --minify -// -// See http://facebook.github.io/react-native/docs/runningondevice.html for more details. - -throw new Error('Offline JS file is empty. See iOS/main.jsbundle for instructions'); diff --git a/Examples/HybridMobileDeployCompanion/index.ios.js b/Examples/HybridMobileDeployCompanion/index.ios.js index 4e993bf5b..f053099f6 100644 --- a/Examples/HybridMobileDeployCompanion/index.ios.js +++ b/Examples/HybridMobileDeployCompanion/index.ios.js @@ -9,11 +9,41 @@ var { AppRegistry, StyleSheet, Text, + TouchableOpacity, View, } = React; +var Button = require("react-native-button"); + +var HybridMobileDeploy = require('react-native-hybrid-mobile-deploy')('http://localhost:3000/', ''); + var HybridMobileDeployCompanion = React.createClass({ + componentDidMount: function() { + this.fetchData(); + }, + fetchData: function() { + HybridMobileDeploy.queryUpdate((err, update) => { + this.setState({ update: update, updateString: JSON.stringify(update) }); + }); + }, + getInitialState: function() { + return { update: false, updateString: "" }; + }, + handlePress: function() { + console.log("pressed"); + }, render: function() { + var updateView; + if (this.state.update) { + updateView = ( + + Update Available: {'\n'} {this.state.update.scriptVersion} - {this.state.update.description} + + + ); + }; return ( @@ -26,6 +56,7 @@ var HybridMobileDeployCompanion = React.createClass({ Press Cmd+R to reload,{'\n'} Cmd+D or shake for dev menu + {updateView} ); } diff --git a/Examples/HybridMobileDeployCompanion/package.json b/Examples/HybridMobileDeployCompanion/package.json index be56a244f..9f292ba3b 100644 --- a/Examples/HybridMobileDeployCompanion/package.json +++ b/Examples/HybridMobileDeployCompanion/package.json @@ -3,9 +3,10 @@ "version": "0.0.1", "private": true, "scripts": { - "start": "node_modules/react-native/packager/packager.sh" + "start": "node_modules/react-native/packager/packager.sh --root ../../" }, "dependencies": { - "react-native": "^0.4.4" + "react-native": "^0.8.0-rc", + "react-native-button": "^1.2.0" } } diff --git a/HybridMobileDeploy.android.js b/HybridMobileDeploy.android.js new file mode 100644 index 000000000..5536c5747 --- /dev/null +++ b/HybridMobileDeploy.android.js @@ -0,0 +1,17 @@ +/** + * Stub of UpdateManager for Android. + * + * @providesModule UpdateManager + * @flow + */ +'use strict'; + +var warning = require('warning'); + +var HybridMobileDeploy = { + test: function() { + warning("Not yet implemented for Android."); + } +}; + +module.exports = HybridMobileDeploy; diff --git a/HybridMobileDeploy.h b/HybridMobileDeploy.h new file mode 100644 index 000000000..417e23e37 --- /dev/null +++ b/HybridMobileDeploy.h @@ -0,0 +1,9 @@ +#import "RCTBridgeModule.h" + +@interface HybridMobileDeploy : NSObject ++ (NSString *) getBundlePath:(NSString*)bundleName; ++ (NSURL *) getNativeBundleURL:(NSString*)bundleName; ++ (NSURL *)appBundleUrl; ++ (NSURL *)appBundleUrl:(NSString*)bundleName + nativeBundleName:(NSString*)nativeBundleName; +@end diff --git a/HybridMobileDeploy.ios.js b/HybridMobileDeploy.ios.js new file mode 100644 index 000000000..3af5a05ac --- /dev/null +++ b/HybridMobileDeploy.ios.js @@ -0,0 +1,33 @@ +/** + * @providesModule HybridMobileDeploy + * @flow + */ + +'use strict'; + +var NativeHybridMobileDeploy = require('react-native').NativeModules.HybridMobileDeploy; +var requestFetchAdapter = require("./request-fetch-adapter.js"); +var semver = require('semver'); +var Sdk = require("hybrid-mobile-deploy-sdk/script/acquisition-sdk"); +var serverUrl; +var appName; +var sdk; + +var HybridMobileDeploy = { + queryUpdate: function(cb) { + var pkg = {nativeVersion: "1.2.3", scriptVersion: "1.2.0"}; + sdk.queryUpdateWithCurrentPackage(pkg, cb); + }, + installUpdate: function(update) { + NativeHybridMobileDeploy.installUpdateFromUrl(update.updateUrl, update.bundleName, (err) => console.log(err), () => console.log("success")); + } +}; + +module.exports = function(serverUrl, deploymentKey, ignoreNativeVersion) { + sdk = new Sdk(requestFetchAdapter, { + serverUrl: serverUrl, + deploymentKey: deploymentKey, + ignoreNativeVersion: ignoreNativeVersion + }); + return HybridMobileDeploy; +}; diff --git a/HybridMobileDeploy.m b/HybridMobileDeploy.m new file mode 100644 index 000000000..905eee00b --- /dev/null +++ b/HybridMobileDeploy.m @@ -0,0 +1,115 @@ +#import "HybridMobileDeploy.h" + +#import "RCTRootView.h" +#import "RCTUtils.h" + +@implementation HybridMobileDeploy + +RCT_EXPORT_MODULE() + ++ (NSString *) getBundleFolderPath +{ + NSString* home = NSHomeDirectory(); + NSString* bundleFolder = [home stringByAppendingPathComponent:@"HybridMobileDeploy"]; + return bundleFolder; +} + ++ (NSString *) getBundlePath:(NSString*)bundleName +{ + NSString * bundleFolderPath = [self getBundleFolderPath]; + NSString* appBundleName = [bundleName stringByAppendingString:@".jsbundle"]; + return [bundleFolderPath stringByAppendingPathComponent:appBundleName]; +} + ++ (NSURL *) getNativeBundleURL:(NSString*)bundleName +{ + return [[NSBundle mainBundle] URLForResource:bundleName withExtension:@"jsbundle"]; +} + ++ (NSURL *) appBundleUrl +{ + return [self appBundleUrl:@"bundle" + nativeBundleName:@"main"]; +} + ++ (NSURL *) appBundleUrl:(NSString*)bundleName + nativeBundleName:(NSString*)nativeBundleName +{ + NSFileManager *fileManager = [NSFileManager defaultManager]; + + NSString *bundlePath = [self getBundlePath:bundleName]; + if ([fileManager fileExistsAtPath:bundlePath]) { + return [[NSURL alloc] initFileURLWithPath:bundlePath]; + } else { + return [self getNativeBundleURL:nativeBundleName]; + } +} + ++ (void) loadBundle:(NSString*)moduleName + nativeBundleName:(NSString*)nativeBundleName +{ + dispatch_async(dispatch_get_main_queue(), ^{ + RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:[self appBundleUrl:moduleName nativeBundleName:nativeBundleName] + moduleName:moduleName + launchOptions:nil]; + + UIViewController *rootViewController = [[UIViewController alloc] init]; + rootViewController.view = rootView; + [UIApplication sharedApplication].delegate.window.rootViewController = rootViewController; + }); +} + +RCT_EXPORT_METHOD(installUpdateFromUrl:(NSString*)updateUrl + bundleName:(NSString*)bundleName + nativeBundleName:(NSString*)nativeBundleName + failureCallback:(RCTResponseSenderBlock)failureCallback + successCallback:(RCTResponseSenderBlock)successCallback) +{ + NSError *parameterError; + NSMutableDictionary *errorData; + if (!updateUrl) { + errorData = [NSMutableDictionary dictionary]; + [errorData setValue:@"missing-updateUrl" forKey:NSLocalizedDescriptionKey]; + } else if (!bundleName) { + errorData = [NSMutableDictionary dictionary]; + [errorData setValue:@"missing-bundleName" forKey:NSLocalizedDescriptionKey]; + } + + if (errorData) { + parameterError = [NSError errorWithDomain:@"HybridMobileDeploy"code:200 userInfo:errorData]; + NSDictionary *rctError = RCTMakeError(@"Error with input to installUpdateFromUrl", parameterError, errorData); + failureCallback(@[rctError]); + } else { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSURL* url = [NSURL URLWithString:updateUrl]; + NSError *err; + + NSString *updateContents = [[NSString alloc] initWithContentsOfURL:url + encoding:NSUTF8StringEncoding + error:&err]; + if (err) { + failureCallback(@[err]); + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + NSError *saveError; + NSString *bundleFolderPath = [HybridMobileDeploy getBundleFolderPath]; + if (![[NSFileManager defaultManager] fileExistsAtPath:bundleFolderPath]) { + [[NSFileManager defaultManager] createDirectoryAtPath:bundleFolderPath withIntermediateDirectories:YES attributes:nil error:&saveError]; + } + [updateContents writeToFile:[HybridMobileDeploy getBundlePath:bundleName] + atomically:YES + encoding:NSUTF8StringEncoding + error:&saveError]; + if (saveError) { + failureCallback(@[saveError]); + } else { + [HybridMobileDeploy loadBundle:bundleName nativeBundleName:nativeBundleName]; + successCallback(@[]); + } + }); + } + }); + } +} + +@end diff --git a/HybridMobileDeploy.xcodeproj/project.pbxproj b/HybridMobileDeploy.xcodeproj/project.pbxproj new file mode 100644 index 000000000..170a64671 --- /dev/null +++ b/HybridMobileDeploy.xcodeproj/project.pbxproj @@ -0,0 +1,252 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 13BE3DEE1AC21097009241FE /* HybridMobileDeploy.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* HybridMobileDeploy.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 /* libHybridMobileDeploy.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libHybridMobileDeploy.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 13BE3DEC1AC21097009241FE /* HybridMobileDeploy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HybridMobileDeploy.h; sourceTree = ""; }; + 13BE3DED1AC21097009241FE /* HybridMobileDeploy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HybridMobileDeploy.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 /* libHybridMobileDeploy.a */, + ); + name = Products; + sourceTree = ""; + }; + 58B511D21A9E6C8500147676 = { + isa = PBXGroup; + children = ( + 13BE3DEC1AC21097009241FE /* HybridMobileDeploy.h */, + 13BE3DED1AC21097009241FE /* HybridMobileDeploy.m */, + 134814211AA4EA7D00B7C361 /* Products */, + ); + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 58B511DA1A9E6C8500147676 /* HybridMobileDeploy */ = { + isa = PBXNativeTarget; + buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "HybridMobileDeploy" */; + buildPhases = ( + 58B511D71A9E6C8500147676 /* Sources */, + 58B511D81A9E6C8500147676 /* Frameworks */, + 58B511D91A9E6C8500147676 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = HybridMobileDeploy; + productName = RCTDataManager; + productReference = 134814201AA4EA6300B7C361 /* libHybridMobileDeploy.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 "HybridMobileDeploy" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 58B511D21A9E6C8500147676; + productRefGroup = 58B511D21A9E6C8500147676; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 58B511DA1A9E6C8500147676 /* HybridMobileDeploy */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 58B511D71A9E6C8500147676 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13BE3DEE1AC21097009241FE /* HybridMobileDeploy.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 = 7.0; + 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 = 7.0; + 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)/../../React/**", + "$(SRCROOT)/../../node_modules/react-native/React/**", + "$(SRCROOT)/node_modules/react-native/React/**", + ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = HybridMobileDeploy; + 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)/../../React/**", + ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = HybridMobileDeploy; + SKIP_INSTALL = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "HybridMobileDeploy" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 58B511ED1A9E6C8500147676 /* Debug */, + 58B511EE1A9E6C8500147676 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "HybridMobileDeploy" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 58B511F01A9E6C8500147676 /* Debug */, + 58B511F11A9E6C8500147676 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 58B511D31A9E6C8500147676 /* Project object */; +} diff --git a/HybridMobileDeploy.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/HybridMobileDeploy.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/HybridMobileDeploy.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/README.md b/README.md new file mode 100644 index 000000000..dc9aa628e --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +hybrid-mobile-deploy-react-native +=== + +React Native module for deploying script updates + +Running the Example +--- + +* Make sure you have https://github.com/Microsoft/hybrid-mobile-deploy cloned beside the react-native project in a folder called `website`. This is hacky, and will be cleaned up as soon as React Native's packager supports symlinks. +* Start the Hybrid Mobile Deploy server with `gulp serve`, after installing the prerequisites described in the [project readme](https://github.com/Microsoft/hybrid-mobile-deploy/blob/master/README.md) +* From the root of this project, run `npm install` +* `cd` into `Examples/HybridMobileDeployCompanion` +* Open `index.ios.js` and add a deployment key (generate one using the UI at http://localhost:4000/) +* Run `npm start` to launch the packager +* Open `HybridMobileDeployCompanion.xcodeproj` in Xcode +* Launch the project diff --git a/package.json b/package.json new file mode 100644 index 000000000..11c7304fd --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "react-native-hybrid-mobile-deploy", + "version": "0.0.1", + "main": "HybridMobileDeploy.ios.js", + "keywords": "react-native", + "dependencies": { + "hybrid-mobile-deploy-sdk": "file:../website/sdk/bin", + "semver": "^4.3.6" + }, + "devDependencies": { + "react-native": "^0.8.0-rc" + } +} diff --git a/request-fetch-adapter.js b/request-fetch-adapter.js new file mode 100644 index 000000000..c3efb8895 --- /dev/null +++ b/request-fetch-adapter.js @@ -0,0 +1,30 @@ +module.exports.request = function request(verb, url, body, callback) { + if (typeof body === "function") { + callback = body; + body = null; + } + + var headers = { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }; + + if (body && typeof body === "object") { + body = JSON.stringify(body); + } + + var statusCode; + + fetch(url, { + method: verb, + headers: headers, + body: body + }).then(function(response) { + statusCode = response.status; + return response.text(); + }).then(function(body) { + callback(null, {statusCode: statusCode, body: body}); + }).catch(function(err) { + callback(err); + }); +}