소스 검색

release 0.1.0, redux support, explicit setOnNavigatorEvent, simplified API - no more direct work with Screen

talkol 8 년 전
부모
커밋
a2d94dde9a
48개의 변경된 파일750개의 추가작업 그리고 262개의 파일을 삭제
  1. 3
    0
      RELEASES.md
  2. 17
    0
      example-redux/README.md
  3. BIN
      example-redux/img/colors.png
  4. BIN
      example-redux/img/navicon_add@2x.png
  5. BIN
      example-redux/img/navicon_edit@2x.png
  6. BIN
      example-redux/img/navicon_menu@2x.png
  7. BIN
      example-redux/img/one@2x.png
  8. BIN
      example-redux/img/one_selected@2x.png
  9. BIN
      example-redux/img/three@2x.png
  10. BIN
      example-redux/img/three_selected@2x.png
  11. BIN
      example-redux/img/two@2x.png
  12. BIN
      example-redux/img/two_selected@2x.png
  13. 1
    7
      example-redux/index.android.js
  14. 2
    51
      example-redux/index.ios.js
  15. 45
    14
      example-redux/ios/exampleRedux.xcodeproj/project.pbxproj
  16. 31
    42
      example-redux/ios/exampleRedux/AppDelegate.m
  17. 7
    8
      example-redux/ios/exampleRedux/Info.plist
  18. 9
    1
      example-redux/package.json
  19. 70
    0
      example-redux/src/app.js
  20. 15
    0
      example-redux/src/components/ExampleComponent.js
  21. 2
    0
      example-redux/src/reducers/app/actionTypes.js
  22. 20
    0
      example-redux/src/reducers/app/actions.js
  23. 17
    0
      example-redux/src/reducers/app/reducer.js
  24. 2
    0
      example-redux/src/reducers/counter/actionTypes.js
  25. 9
    0
      example-redux/src/reducers/counter/actions.js
  26. 21
    0
      example-redux/src/reducers/counter/reducer.js
  27. 7
    0
      example-redux/src/reducers/index.js
  28. 93
    0
      example-redux/src/screens/FirstTabScreen.js
  29. 69
    0
      example-redux/src/screens/LoginScreen.js
  30. 77
    0
      example-redux/src/screens/PushedScreen.js
  31. 74
    0
      example-redux/src/screens/SecondTabScreen.js
  32. 14
    0
      example-redux/src/screens/index.js
  33. 3
    1
      example/README.md
  34. 1
    7
      example/index.android.js
  35. 1
    1
      example/ios/example/Info.plist
  36. 4
    4
      example/src/app.js
  37. 8
    17
      example/src/screens/FirstTabScreen.js
  38. 8
    16
      example/src/screens/ModalScreen.js
  39. 7
    17
      example/src/screens/PushedScreen.js
  40. 6
    10
      example/src/screens/SecondTabScreen.js
  41. 2
    8
      example/src/screens/SideMenu.js
  42. 7
    15
      example/src/screens/StyledScreen.js
  43. 5
    16
      example/src/screens/ThirdTabScreen.js
  44. 20
    0
      example/src/screens/index.js
  45. 4
    1
      package.json
  46. 42
    1
      src/Navigation.js
  47. 25
    18
      src/Screen.js
  48. 2
    7
      src/platformSpecific.ios.js

+ 3
- 0
RELEASES.md 파일 보기

@@ -0,0 +1,3 @@
1
+# Release Notes
2
+
3
+### 0.1.0

+ 17
- 0
example-redux/README.md 파일 보기

@@ -0,0 +1,17 @@
1
+# example-redux
2
+
3
+A simple usage example for an app based on redux. If you're not using redux, take a look at [example](../example) (without redux). The non-redux example will also include more examples of pure navigation features since we don't want to maintain two full examples. Make sure to look at it as well.
4
+
5
+## Installation - iOS
6
+
7
+* In the `example/` folder, run `npm install`
8
+
9
+> Make sure you're using npm ver 3. If you normally use npm ver 2 on your system and reluctant to upgrade, you can install [npm 3 alongside 2](https://www.npmjs.com/package/npm3). For more details see https://github.com/wix/react-native-navigation/issues/1. In addition, redux also causes some issues with npm 2. If you have to use npm 2 on this project, fix those by running `npm run fix_npm2` in your project directory after running `npm install`.
10
+
11
+* Open `example/ios/example.xcodeproj` in Xcode and press the play button
12
+
13
+## Folder Structure
14
+
15
+* `screens/` - all your app screens, every screen is a redux connected ("smart") component
16
+* `components/` - all react sub-components, they are not aware of redux ("dumb") and get everything by props
17
+* `reducers/` - all of your redux goodness (reducts and their actions) goes here, all business logic should be here

BIN
example-redux/img/colors.png 파일 보기


BIN
example-redux/img/navicon_add@2x.png 파일 보기


BIN
example-redux/img/navicon_edit@2x.png 파일 보기


BIN
example-redux/img/navicon_menu@2x.png 파일 보기


BIN
example-redux/img/one@2x.png 파일 보기


BIN
example-redux/img/one_selected@2x.png 파일 보기


BIN
example-redux/img/three@2x.png 파일 보기


BIN
example-redux/img/three_selected@2x.png 파일 보기


BIN
example-redux/img/two@2x.png 파일 보기


BIN
example-redux/img/two_selected@2x.png 파일 보기


+ 1
- 7
example-redux/index.android.js 파일 보기

@@ -16,13 +16,7 @@ class exampleRedux extends Component {
16 16
     return (
17 17
       <View style={styles.container}>
18 18
         <Text style={styles.welcome}>
19
-          Welcome to React Native!
20
-        </Text>
21
-        <Text style={styles.instructions}>
22
-          To get started, edit index.android.js
23
-        </Text>
24
-        <Text style={styles.instructions}>
25
-          Shake or press menu button for dev menu
19
+          Coming Soon
26 20
         </Text>
27 21
       </View>
28 22
     );

+ 2
- 51
example-redux/index.ios.js 파일 보기

@@ -1,52 +1,3 @@
1
-/**
2
- * Sample React Native App
3
- * https://github.com/facebook/react-native
4
- */
5
-'use strict';
6
-import React, {
7
-  AppRegistry,
8
-  Component,
9
-  StyleSheet,
10
-  Text,
11
-  View
12
-} from 'react-native';
1
+import App from './src/app';
13 2
 
14
-class exampleRedux extends Component {
15
-  render() {
16
-    return (
17
-      <View style={styles.container}>
18
-        <Text style={styles.welcome}>
19
-          Welcome to React Native!
20
-        </Text>
21
-        <Text style={styles.instructions}>
22
-          To get started, edit index.ios.js
23
-        </Text>
24
-        <Text style={styles.instructions}>
25
-          Press Cmd+R to reload,{'\n'}
26
-          Cmd+D or shake for dev menu
27
-        </Text>
28
-      </View>
29
-    );
30
-  }
31
-}
32
-
33
-const styles = StyleSheet.create({
34
-  container: {
35
-    flex: 1,
36
-    justifyContent: 'center',
37
-    alignItems: 'center',
38
-    backgroundColor: '#F5FCFF',
39
-  },
40
-  welcome: {
41
-    fontSize: 20,
42
-    textAlign: 'center',
43
-    margin: 10,
44
-  },
45
-  instructions: {
46
-    textAlign: 'center',
47
-    color: '#333333',
48
-    marginBottom: 5,
49
-  },
50
-});
51
-
52
-AppRegistry.registerComponent('exampleRedux', () => exampleRedux);
3
+const app = new App();

+ 45
- 14
example-redux/ios/exampleRedux.xcodeproj/project.pbxproj 파일 보기

@@ -22,6 +22,7 @@
22 22
 		13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
23 23
 		146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
24 24
 		832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
25
+		CCCF23211C9C238500455574 /* libReactNativeControllers.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CCCF23201C9C236100455574 /* libReactNativeControllers.a */; };
25 26
 /* End PBXBuildFile section */
26 27
 
27 28
 /* Begin PBXContainerItemProxy section */
@@ -102,20 +103,27 @@
102 103
 			remoteGlobalIDString = 58B5119B1A9E6C1200147676;
103 104
 			remoteInfo = RCTText;
104 105
 		};
106
+		CCCF231F1C9C236100455574 /* PBXContainerItemProxy */ = {
107
+			isa = PBXContainerItemProxy;
108
+			containerPortal = CCCF23111C9C236100455574 /* ReactNativeControllers.xcodeproj */;
109
+			proxyType = 2;
110
+			remoteGlobalIDString = D8AFADBD1BEE6F3F00A4592D;
111
+			remoteInfo = ReactNativeControllers;
112
+		};
105 113
 /* End PBXContainerItemProxy section */
106 114
 
107 115
 /* Begin PBXFileReference section */
108
-		008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = main.jsbundle; path = main.jsbundle; sourceTree = "<group>"; };
109
-		00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = "<group>"; };
110
-		00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = "<group>"; };
111
-		00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = ../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj; sourceTree = "<group>"; };
112
-		00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj; sourceTree = "<group>"; };
113
-		00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = ../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj; sourceTree = "<group>"; };
116
+		008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; };
117
+		00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = "<group>"; };
118
+		00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = "<group>"; };
119
+		00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = "<group>"; };
120
+		00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = "<group>"; };
121
+		00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = "<group>"; };
114 122
 		00E356EE1AD99517003FC87E /* exampleReduxTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = exampleReduxTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
115 123
 		00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
116 124
 		00E356F21AD99517003FC87E /* exampleReduxTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = exampleReduxTests.m; sourceTree = "<group>"; };
117
-		139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = ../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj; sourceTree = "<group>"; };
118
-		139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = ../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj; sourceTree = "<group>"; };
125
+		139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = "<group>"; };
126
+		139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = "<group>"; };
119 127
 		13B07F961A680F5B00A75B9A /* exampleRedux.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = exampleRedux.app; sourceTree = BUILT_PRODUCTS_DIR; };
120 128
 		13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = exampleRedux/AppDelegate.h; sourceTree = "<group>"; };
121 129
 		13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = exampleRedux/AppDelegate.m; sourceTree = "<group>"; };
@@ -123,9 +131,10 @@
123 131
 		13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = exampleRedux/Images.xcassets; sourceTree = "<group>"; };
124 132
 		13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = exampleRedux/Info.plist; sourceTree = "<group>"; };
125 133
 		13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = exampleRedux/main.m; sourceTree = "<group>"; };
126
-		146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = ../node_modules/react-native/React/React.xcodeproj; sourceTree = "<group>"; };
127
-		78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = "<group>"; };
128
-		832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../node_modules/react-native/Libraries/Text/RCTText.xcodeproj; sourceTree = "<group>"; };
134
+		146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = "<group>"; };
135
+		78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
136
+		832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
137
+		CCCF23111C9C236100455574 /* ReactNativeControllers.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactNativeControllers.xcodeproj; path = "../node_modules/react-native-controllers/ios/ReactNativeControllers.xcodeproj"; sourceTree = "<group>"; };
129 138
 /* End PBXFileReference section */
130 139
 
131 140
 /* Begin PBXFrameworksBuildPhase section */
@@ -140,6 +149,7 @@
140 149
 			isa = PBXFrameworksBuildPhase;
141 150
 			buildActionMask = 2147483647;
142 151
 			files = (
152
+				CCCF23211C9C238500455574 /* libReactNativeControllers.a in Frameworks */,
143 153
 				146834051AC3E58100842450 /* libReact.a in Frameworks */,
144 154
 				00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */,
145 155
 				00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */,
@@ -272,6 +282,7 @@
272 282
 				832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */,
273 283
 				00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */,
274 284
 				139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */,
285
+				CCCF23111C9C236100455574 /* ReactNativeControllers.xcodeproj */,
275 286
 			);
276 287
 			name = Libraries;
277 288
 			sourceTree = "<group>";
@@ -305,6 +316,14 @@
305 316
 			name = Products;
306 317
 			sourceTree = "<group>";
307 318
 		};
319
+		CCCF23121C9C236100455574 /* Products */ = {
320
+			isa = PBXGroup;
321
+			children = (
322
+				CCCF23201C9C236100455574 /* libReactNativeControllers.a */,
323
+			);
324
+			name = Products;
325
+			sourceTree = "<group>";
326
+		};
308 327
 /* End PBXGroup section */
309 328
 
310 329
 /* Begin PBXNativeTarget section */
@@ -411,6 +430,10 @@
411 430
 					ProductGroup = 146834001AC3E56700842450 /* Products */;
412 431
 					ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */;
413 432
 				},
433
+				{
434
+					ProductGroup = CCCF23121C9C236100455574 /* Products */;
435
+					ProjectRef = CCCF23111C9C236100455574 /* ReactNativeControllers.xcodeproj */;
436
+				},
414 437
 			);
415 438
 			projectRoot = "";
416 439
 			targets = (
@@ -491,6 +514,13 @@
491 514
 			remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */;
492 515
 			sourceTree = BUILT_PRODUCTS_DIR;
493 516
 		};
517
+		CCCF23201C9C236100455574 /* libReactNativeControllers.a */ = {
518
+			isa = PBXReferenceProxy;
519
+			fileType = archive.ar;
520
+			path = libReactNativeControllers.a;
521
+			remoteRef = CCCF231F1C9C236100455574 /* PBXContainerItemProxy */;
522
+			sourceTree = BUILT_PRODUCTS_DIR;
523
+		};
494 524
 /* End PBXReferenceProxy section */
495 525
 
496 526
 /* Begin PBXResourcesBuildPhase section */
@@ -526,7 +556,6 @@
526 556
 			runOnlyForDeploymentPostprocessing = 0;
527 557
 			shellPath = /bin/sh;
528 558
 			shellScript = "export NODE_BINARY=node\n../node_modules/react-native/packager/react-native-xcode.sh";
529
-			showEnvVarsInLog = 1;
530 559
 		};
531 560
 /* End PBXShellScriptBuildPhase section */
532 561
 
@@ -617,8 +646,9 @@
617 646
 					"$(inherited)",
618 647
 					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
619 648
 					"$(SRCROOT)/../node_modules/react-native/React/**",
649
+					"$(SRCROOT)/../node_modules/react-native-controllers/ios/**",
620 650
 				);
621
-				INFOPLIST_FILE = "exampleRedux/Info.plist";
651
+				INFOPLIST_FILE = exampleRedux/Info.plist;
622 652
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
623 653
 				OTHER_LDFLAGS = "-ObjC";
624 654
 				PRODUCT_NAME = exampleRedux;
@@ -633,8 +663,9 @@
633 663
 					"$(inherited)",
634 664
 					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
635 665
 					"$(SRCROOT)/../node_modules/react-native/React/**",
666
+					"$(SRCROOT)/../node_modules/react-native-controllers/ios/**",
636 667
 				);
637
-				INFOPLIST_FILE = "exampleRedux/Info.plist";
668
+				INFOPLIST_FILE = exampleRedux/Info.plist;
638 669
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
639 670
 				OTHER_LDFLAGS = "-ObjC";
640 671
 				PRODUCT_NAME = exampleRedux;

+ 31
- 42
example-redux/ios/exampleRedux/AppDelegate.m 파일 보기

@@ -1,14 +1,13 @@
1
-/**
2
- * Copyright (c) 2015-present, Facebook, Inc.
3
- * All rights reserved.
4
- *
5
- * This source code is licensed under the BSD-style license found in the
6
- * LICENSE file in the root directory of this source tree. An additional grant
7
- * of patent rights can be found in the PATENTS file in the same directory.
8
- */
9
-
10 1
 #import "AppDelegate.h"
11 2
 
3
+// **********************************************
4
+// *** DON'T MISS: THE NEXT LINE IS IMPORTANT ***
5
+// **********************************************
6
+#import "RCCManager.h"
7
+
8
+// IMPORTANT: if you're getting an Xcode error that RCCManager.h isn't found, you've probably ran "npm install"
9
+// with npm ver 2. You'll need to "npm install" with npm 3 (see https://github.com/wix/react-native-navigation/issues/1)
10
+
12 11
 #import "RCTRootView.h"
13 12
 
14 13
 @implementation AppDelegate
@@ -16,41 +15,31 @@
16 15
 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
17 16
 {
18 17
   NSURL *jsCodeLocation;
19
-
20
-  /**
21
-   * Loading JavaScript code - uncomment the one you want.
22
-   *
23
-   * OPTION 1
24
-   * Load from development server. Start the server from the repository root:
25
-   *
26
-   * $ npm start
27
-   *
28
-   * To run on device, change `localhost` to the IP address of your computer
29
-   * (you can get this by typing `ifconfig` into the terminal and selecting the
30
-   * `inet` value under `en0:`) and make sure your computer and iOS device are
31
-   * on the same Wi-Fi network.
32
-   */
33
-
34 18
   jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
35
-
36
-  /**
37
-   * OPTION 2
38
-   * Load from pre-bundled file on disk. The static bundle is automatically
39
-   * generated by "Bundle React Native code and images" build step.
40
-   */
41
-
42
-//   jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
43
-
44
-  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
45
-                                                      moduleName:@"exampleRedux"
46
-                                               initialProperties:nil
47
-                                                   launchOptions:launchOptions];
48
-
19
+  // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
20
+  
21
+  
22
+  // **********************************************
23
+  // *** DON'T MISS: THIS IS HOW WE BOOTSTRAP *****
24
+  // **********************************************
49 25
   self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
50
-  UIViewController *rootViewController = [UIViewController new];
51
-  rootViewController.view = rootView;
52
-  self.window.rootViewController = rootViewController;
53
-  [self.window makeKeyAndVisible];
26
+  self.window.backgroundColor = [UIColor whiteColor];
27
+  [[RCCManager sharedIntance] initBridgeWithBundleURL:jsCodeLocation];
28
+  
29
+  /*
30
+   // original RN bootstrap - remove this part
31
+   RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
32
+   moduleName:@"example"
33
+   initialProperties:nil
34
+   launchOptions:launchOptions];
35
+   self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
36
+   UIViewController *rootViewController = [UIViewController new];
37
+   rootViewController.view = rootView;
38
+   self.window.rootViewController = rootViewController;
39
+   [self.window makeKeyAndVisible];
40
+   */
41
+  
42
+  
54 43
   return YES;
55 44
 }
56 45
 

+ 7
- 8
example-redux/ios/exampleRedux/Info.plist 파일 보기

@@ -15,7 +15,7 @@
15 15
 	<key>CFBundlePackageType</key>
16 16
 	<string>APPL</string>
17 17
 	<key>CFBundleShortVersionString</key>
18
-	<string>1.0</string>
18
+	<string>1.0.0</string>
19 19
 	<key>CFBundleSignature</key>
20 20
 	<string>????</string>
21 21
 	<key>CFBundleVersion</key>
@@ -35,14 +35,13 @@
35 35
 		<string>UIInterfaceOrientationLandscapeRight</string>
36 36
 	</array>
37 37
 	<key>UIViewControllerBasedStatusBarAppearance</key>
38
-	<false/>
38
+	<true/>
39 39
 	<key>NSLocationWhenInUseUsageDescription</key>
40 40
 	<string></string>
41
-  <key>NSAppTransportSecurity</key>
42
-  <dict>
43
-    <!--See http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/ -->
44
-    <key>NSAllowsArbitraryLoads</key>
45
-    <true/>
46
-  </dict>
41
+	<key>NSAppTransportSecurity</key>
42
+	<dict>
43
+		<key>NSAllowsArbitraryLoads</key>
44
+		<true/>
45
+	</dict>
47 46
 </dict>
48 47
 </plist>

+ 9
- 1
example-redux/package.json 파일 보기

@@ -3,10 +3,18 @@
3 3
   "version": "0.0.1",
4 4
   "private": true,
5 5
   "scripts": {
6
-    "start": "node node_modules/react-native/local-cli/cli.js start"
6
+    "start": "node node_modules/react-native/local-cli/cli.js start",
7
+    "fix_npm2": "npm run _fix_npm2_step1; npm run _fix_npm2_step2",
8
+    "_fix_npm2_step1": "rm -rf node_modules/react-native/node_modules/react; rm -rf node_modules/react-native/node_modules/fbjs; rm -rf node_modules/react/node_modules/fbjs",
9
+    "_fix_npm2_step2": "cd node_modules; find . -name .babelrc | grep -v packager | xargs rm"
7 10
   },
8 11
   "dependencies": {
9 12
     "react-native": "^0.21.0",
13
+    "fbjs": "^0.6.1",
14
+    "react-redux": "^4.0.6",
15
+    "redux": "^3.0.5",
16
+    "redux-thunk": "^1.0.3",
17
+    "seamless-immutable": "^5.0.1",
10 18
     "react-native-navigation": "latest"
11 19
   }
12 20
 }

+ 70
- 0
example-redux/src/app.js 파일 보기

@@ -0,0 +1,70 @@
1
+import { createStore, applyMiddleware, combineReducers } from 'redux';
2
+import { Provider } from 'react-redux';
3
+import { Navigation } from 'react-native-navigation';
4
+import thunk from 'redux-thunk';
5
+import * as reducers from './reducers';
6
+import * as appActions from './reducers/app/actions';
7
+
8
+// redux related book keeping
9
+const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
10
+const reducer = combineReducers(reducers);
11
+const store = createStoreWithMiddleware(reducer);
12
+
13
+// screen related book keeping
14
+import { registerScreens } from './screens';
15
+registerScreens(store, Provider);
16
+
17
+// notice that this is just a simple class, it's not a React component
18
+export default class App {
19
+  constructor() {
20
+    // since react-redux only works on components, we need to subscribe this class manually
21
+    store.subscribe(this.onStoreUpdate.bind(this));
22
+    store.dispatch(appActions.appInitialized());
23
+  }
24
+  onStoreUpdate() {
25
+    const { root } = store.getState().app;
26
+    // handle a root change
27
+    // if your app doesn't change roots in runtime, you can remove onStoreUpdate() altogether
28
+    if (this.currentRoot !== root) {
29
+      this.currentRoot = root;
30
+      this.startApp(root);
31
+    }
32
+  }
33
+  startApp(root) {
34
+    switch (root) {
35
+      case 'login':
36
+        Navigation.startSingleScreenApp({
37
+          screen: {
38
+            screen: 'example.LoginScreen',
39
+            title: 'Login',
40
+            navigatorStyle: {}
41
+          }
42
+        });
43
+        return;
44
+      case 'after-login':
45
+        Navigation.startTabBasedApp({
46
+          tabs: [
47
+            {
48
+              label: 'One',
49
+              screen: 'example.FirstTabScreen',
50
+              icon: require('../img/one.png'),
51
+              selectedIcon: require('../img/one_selected.png'),
52
+              title: 'Screen One',
53
+              navigatorStyle: {}
54
+            },
55
+            {
56
+              label: 'Two',
57
+              screen: 'example.SecondTabScreen',
58
+              icon: require('../img/two.png'),
59
+              selectedIcon: require('../img/two_selected.png'),
60
+              title: 'Screen Two',
61
+              navigatorStyle: {}
62
+            }
63
+          ]
64
+        });
65
+        return;
66
+      default:
67
+        console.error('Unknown app root');
68
+    }
69
+  }
70
+}

+ 15
- 0
example-redux/src/components/ExampleComponent.js 파일 보기

@@ -0,0 +1,15 @@
1
+import React, {
2
+  Component,
3
+  Text
4
+} from 'react-native';
5
+
6
+export default class ExampleComponent extends Component {
7
+  constructor(props) {
8
+    super(props);
9
+  }
10
+  render() {
11
+    return (
12
+      <Text>I am a regular non-redux aware component</Text>
13
+    );
14
+  }
15
+}

+ 2
- 0
example-redux/src/reducers/app/actionTypes.js 파일 보기

@@ -0,0 +1,2 @@
1
+export const INITIALIZED = 'example.app.INITIALIZED';
2
+export const ROOT_CHANGED = 'example.app.ROOT_CHANGED';

+ 20
- 0
example-redux/src/reducers/app/actions.js 파일 보기

@@ -0,0 +1,20 @@
1
+import * as types from './actionTypes';
2
+
3
+export function appInitialized() {
4
+  return async function(dispatch, getState) {
5
+    // since all business logic should be inside redux actions
6
+    // this is a good place to put your app initialization code
7
+    dispatch(changeAppRoot('login'));
8
+  };
9
+}
10
+
11
+export function changeAppRoot(root) {
12
+  return {type: types.ROOT_CHANGED, root: root};
13
+}
14
+
15
+export function login() {
16
+  return async function(dispatch, getState) {
17
+    // login logic would go here, and when it's done, we switch app roots
18
+    dispatch(changeAppRoot('after-login'));
19
+  };
20
+}

+ 17
- 0
example-redux/src/reducers/app/reducer.js 파일 보기

@@ -0,0 +1,17 @@
1
+import * as types from './actionTypes';
2
+import Immutable from 'seamless-immutable';
3
+
4
+const initialState = Immutable({
5
+  root: undefined // 'login' / 'after-login'
6
+});
7
+
8
+export default function app(state = initialState, action = {}) {
9
+  switch (action.type) {
10
+    case types.ROOT_CHANGED:
11
+      return state.merge({
12
+        root: action.root
13
+      });
14
+    default:
15
+      return state;
16
+  }
17
+}

+ 2
- 0
example-redux/src/reducers/counter/actionTypes.js 파일 보기

@@ -0,0 +1,2 @@
1
+export const INCREMENT = 'example.counter.INCREMENT';
2
+export const DECREMENT = 'example.counter.DECREMENT';

+ 9
- 0
example-redux/src/reducers/counter/actions.js 파일 보기

@@ -0,0 +1,9 @@
1
+import * as types from './actionTypes';
2
+
3
+export function increment() {
4
+  return {type: types.INCREMENT};
5
+}
6
+
7
+export function decrement() {
8
+  return {type: types.DECREMENT};
9
+}

+ 21
- 0
example-redux/src/reducers/counter/reducer.js 파일 보기

@@ -0,0 +1,21 @@
1
+import * as types from './actionTypes';
2
+import Immutable from 'seamless-immutable';
3
+
4
+const initialState = Immutable({
5
+  count: 0
6
+});
7
+
8
+export default function counter(state = initialState, action = {}) {
9
+  switch (action.type) {
10
+    case types.INCREMENT:
11
+      return state.merge({
12
+        count: state.count + 1
13
+      });
14
+    case types.DECREMENT:
15
+      return state.merge({
16
+        count: state.count - 1
17
+      });
18
+    default:
19
+      return state;
20
+  }
21
+}

+ 7
- 0
example-redux/src/reducers/index.js 파일 보기

@@ -0,0 +1,7 @@
1
+import app from './app/reducer';
2
+import counter from './counter/reducer';
3
+
4
+export {
5
+  app,
6
+  counter
7
+};

+ 93
- 0
example-redux/src/screens/FirstTabScreen.js 파일 보기

@@ -0,0 +1,93 @@
1
+import React, {
2
+  Component,
3
+  Text,
4
+  View,
5
+  ScrollView,
6
+  TouchableOpacity,
7
+  StyleSheet,
8
+  AlertIOS
9
+} from 'react-native';
10
+import { connect } from 'react-redux';
11
+import * as counterActions from '../reducers/counter/actions';
12
+
13
+// this is a traditional React component connected to the redux store
14
+class FirstTabScreen extends Component {
15
+  static navigatorButtons = {
16
+    rightButtons: [
17
+      {
18
+        title: 'Edit',
19
+        id: 'edit'
20
+      },
21
+      {
22
+        icon: require('../../img/navicon_add.png'),
23
+        id: 'add'
24
+      }
25
+    ]
26
+  };
27
+  constructor(props) {
28
+    super(props);
29
+    // if you want to listen on navigator events, set this up
30
+    this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
31
+  }
32
+  onNavigatorEvent(event) {
33
+    if (event.id == 'edit') {
34
+      AlertIOS.alert('NavBar', 'Edit button pressed');
35
+    }
36
+    if (event.id == 'add') {
37
+      AlertIOS.alert('NavBar', 'Add button pressed');
38
+    }
39
+  }
40
+  render() {
41
+    return (
42
+      <View style={{flex: 1, padding: 20}}>
43
+
44
+        <Text style={styles.text}>
45
+          <Text style={{fontWeight: '500'}}>Same Counter: </Text> {this.props.counter.count}
46
+        </Text>
47
+
48
+        <TouchableOpacity onPress={ this.onIncrementPress.bind(this) }>
49
+          <Text style={styles.button}>Increment Counter</Text>
50
+        </TouchableOpacity>
51
+
52
+        <TouchableOpacity onPress={ this.onPushPress.bind(this) }>
53
+          <Text style={styles.button}>Push Screen</Text>
54
+        </TouchableOpacity>
55
+
56
+      </View>
57
+    );
58
+  }
59
+  onIncrementPress() {
60
+    this.props.dispatch(counterActions.increment());
61
+  }
62
+  onPushPress() {
63
+    this.props.navigator.push({
64
+      title: "More",
65
+      screen: "example.PushedScreen"
66
+    });
67
+  }
68
+}
69
+
70
+const styles = StyleSheet.create({
71
+  text: {
72
+    textAlign: 'center',
73
+    fontSize: 18,
74
+    marginBottom: 10,
75
+    marginTop:10,
76
+  },
77
+  button: {
78
+    textAlign: 'center',
79
+    fontSize: 18,
80
+    marginBottom: 10,
81
+    marginTop:10,
82
+    color: 'blue'
83
+  }
84
+});
85
+
86
+// which props do we want to inject, given the global state?
87
+function mapStateToProps(state) {
88
+  return {
89
+    counter: state.counter
90
+  };
91
+}
92
+
93
+export default connect(mapStateToProps)(FirstTabScreen);

+ 69
- 0
example-redux/src/screens/LoginScreen.js 파일 보기

@@ -0,0 +1,69 @@
1
+import React, {
2
+  Component,
3
+  Text,
4
+  View,
5
+  ScrollView,
6
+  TouchableOpacity,
7
+  StyleSheet,
8
+  AlertIOS
9
+} from 'react-native';
10
+import { connect } from 'react-redux';
11
+import * as counterActions from '../reducers/counter/actions';
12
+import * as appActions from '../reducers/app/actions';
13
+
14
+// this is a traditional React component connected to the redux store
15
+class LoginScreen extends Component {
16
+  constructor(props) {
17
+    super(props);
18
+  }
19
+  render() {
20
+    return (
21
+      <View style={{flex: 1, padding: 20}}>
22
+
23
+        <Text style={styles.text}>
24
+          <Text style={{fontWeight: '500'}}>Counter: </Text> {this.props.counter.count}
25
+        </Text>
26
+
27
+        <TouchableOpacity onPress={ this.onIncrementPress.bind(this) }>
28
+          <Text style={styles.button}>Increment Counter</Text>
29
+        </TouchableOpacity>
30
+
31
+        <TouchableOpacity onPress={ this.onLoginPress.bind(this) }>
32
+          <Text style={styles.button}>Login</Text>
33
+        </TouchableOpacity>
34
+
35
+      </View>
36
+    );
37
+  }
38
+  onIncrementPress() {
39
+    this.props.dispatch(counterActions.increment());
40
+  }
41
+  onLoginPress() {
42
+    this.props.dispatch(appActions.login());
43
+  }
44
+}
45
+
46
+const styles = StyleSheet.create({
47
+  text: {
48
+    textAlign: 'center',
49
+    fontSize: 18,
50
+    marginBottom: 10,
51
+    marginTop:10,
52
+  },
53
+  button: {
54
+    textAlign: 'center',
55
+    fontSize: 18,
56
+    marginBottom: 10,
57
+    marginTop:10,
58
+    color: 'blue'
59
+  }
60
+});
61
+
62
+// which props do we want to inject, given the global state?
63
+function mapStateToProps(state) {
64
+  return {
65
+    counter: state.counter
66
+  };
67
+}
68
+
69
+export default connect(mapStateToProps)(LoginScreen);

+ 77
- 0
example-redux/src/screens/PushedScreen.js 파일 보기

@@ -0,0 +1,77 @@
1
+import React, {
2
+  Component,
3
+  Text,
4
+  View,
5
+  ScrollView,
6
+  TouchableOpacity,
7
+  StyleSheet
8
+} from 'react-native';
9
+import { connect } from 'react-redux';
10
+import * as counterActions from '../reducers/counter/actions';
11
+
12
+// this is a traditional React component connected to the redux store
13
+class PushedScreen extends Component {
14
+  constructor(props) {
15
+    super(props);
16
+  }
17
+  render() {
18
+    return (
19
+      <View style={{flex: 1, padding: 20}}>
20
+
21
+        <Text style={styles.text}>
22
+          <Text style={{fontWeight: '500'}}>Counter: </Text> {this.props.counter.count}
23
+        </Text>
24
+
25
+        <TouchableOpacity onPress={ this.onIncrementPress.bind(this) }>
26
+          <Text style={styles.button}>Increment Counter</Text>
27
+        </TouchableOpacity>
28
+
29
+        <TouchableOpacity onPress={ this.onPushPress.bind(this) }>
30
+          <Text style={styles.button}>Push Another</Text>
31
+        </TouchableOpacity>
32
+
33
+        <TouchableOpacity onPress={ this.onPopPress.bind(this) }>
34
+          <Text style={styles.button}>Pop Screen</Text>
35
+        </TouchableOpacity>
36
+
37
+      </View>
38
+    );
39
+  }
40
+  onIncrementPress() {
41
+    this.props.dispatch(counterActions.increment());
42
+  }
43
+  onPushPress() {
44
+    this.props.navigator.push({
45
+      title: "More",
46
+      screen: "example.PushedScreen"
47
+    });
48
+  }
49
+  onPopPress() {
50
+    this.props.navigator.pop();
51
+  }
52
+}
53
+
54
+const styles = StyleSheet.create({
55
+  text: {
56
+    textAlign: 'center',
57
+    fontSize: 18,
58
+    marginBottom: 10,
59
+    marginTop:10,
60
+  },
61
+  button: {
62
+    textAlign: 'center',
63
+    fontSize: 18,
64
+    marginBottom: 10,
65
+    marginTop:10,
66
+    color: 'blue'
67
+  }
68
+});
69
+
70
+// which props do we want to inject, given the global state?
71
+function mapStateToProps(state) {
72
+  return {
73
+    counter: state.counter
74
+  };
75
+}
76
+
77
+export default connect(mapStateToProps)(PushedScreen);

+ 74
- 0
example-redux/src/screens/SecondTabScreen.js 파일 보기

@@ -0,0 +1,74 @@
1
+import React, {
2
+  Component,
3
+  Text,
4
+  Image,
5
+  View,
6
+  ScrollView,
7
+  TouchableOpacity,
8
+  StyleSheet,
9
+  AlertIOS
10
+} from 'react-native';
11
+import { connect } from 'react-redux';
12
+import * as counterActions from '../reducers/counter/actions';
13
+
14
+// this is a traditional React component connected to the redux store
15
+class SecondTabScreen extends Component {
16
+  static navigatorStyle = {
17
+    drawUnderNavBar: true,
18
+    drawUnderTabBar: true,
19
+    navBarTranslucent: true
20
+  };
21
+  constructor(props) {
22
+    super(props);
23
+    this.buttonsCounter = 0;
24
+  }
25
+  render() {
26
+    return (
27
+      <ScrollView style={{flex: 1}}>
28
+
29
+        <Image style={{width: undefined, height: 100}} source={require('../../img/colors.png')} />
30
+
31
+        <View style={{padding: 20}}>
32
+
33
+          <Text style={styles.text}>
34
+            <Text style={{fontWeight: '500'}}>Here Too: </Text> {this.props.counter.count}
35
+          </Text>
36
+
37
+          <TouchableOpacity onPress={ this.onIncrementPress.bind(this) }>
38
+            <Text style={styles.button}>Increment Counter</Text>
39
+          </TouchableOpacity>
40
+
41
+        </View>
42
+
43
+      </ScrollView>
44
+    );
45
+  }
46
+  onIncrementPress() {
47
+    this.props.dispatch(counterActions.increment());
48
+  }
49
+}
50
+
51
+const styles = StyleSheet.create({
52
+  text: {
53
+    textAlign: 'center',
54
+    fontSize: 18,
55
+    marginBottom: 10,
56
+    marginTop:10,
57
+  },
58
+  button: {
59
+    textAlign: 'center',
60
+    fontSize: 18,
61
+    marginBottom: 10,
62
+    marginTop:10,
63
+    color: 'blue'
64
+  }
65
+});
66
+
67
+// which props do we want to inject, given the global state?
68
+function mapStateToProps(state) {
69
+  return {
70
+    counter: state.counter
71
+  };
72
+}
73
+
74
+export default connect(mapStateToProps)(SecondTabScreen);

+ 14
- 0
example-redux/src/screens/index.js 파일 보기

@@ -0,0 +1,14 @@
1
+import { Navigation } from 'react-native-navigation';
2
+
3
+import LoginScreen from './LoginScreen';
4
+import FirstTabScreen from './FirstTabScreen';
5
+import SecondTabScreen from './SecondTabScreen';
6
+import PushedScreen from './PushedScreen';
7
+
8
+// register all screens of the app (including internal ones)
9
+export function registerScreens(store, Provider) {
10
+  Navigation.registerComponent('example.LoginScreen', () => LoginScreen, store, Provider);
11
+  Navigation.registerComponent('example.FirstTabScreen', () => FirstTabScreen, store, Provider);
12
+  Navigation.registerComponent('example.SecondTabScreen', () => SecondTabScreen, store, Provider);
13
+  Navigation.registerComponent('example.PushedScreen', () => PushedScreen, store, Provider);
14
+}

+ 3
- 1
example/README.md 파일 보기

@@ -1,9 +1,11 @@
1 1
 # example
2 2
 
3
+A simple usage example. If you're using redux, take a look at [example-redux](../example-redux).
4
+
3 5
 ## Installation - iOS
4 6
 
5 7
 * In the `example/` folder, run `npm install`
6 8
 
7
-> Make sure you're using npm ver 3. If you normally use npm ver 2 on your system and reluctant to upgrade, you can install [npm 3 alongside 2](https://www.npmjs.com/package/npm3).
9
+> Make sure you're using npm ver 3. If you normally use npm ver 2 on your system and reluctant to upgrade, you can install [npm 3 alongside 2](https://www.npmjs.com/package/npm3). For more details see https://github.com/wix/react-native-navigation/issues/1
8 10
 
9 11
 * Open `example/ios/example.xcodeproj` in Xcode and press the play button

+ 1
- 7
example/index.android.js 파일 보기

@@ -16,13 +16,7 @@ class example extends Component {
16 16
     return (
17 17
       <View style={styles.container}>
18 18
         <Text style={styles.welcome}>
19
-          Welcome to React Native!
20
-        </Text>
21
-        <Text style={styles.instructions}>
22
-          To get started, edit index.android.js
23
-        </Text>
24
-        <Text style={styles.instructions}>
25
-          Shake or press menu button for dev menu
19
+          Coming Soon
26 20
         </Text>
27 21
       </View>
28 22
     );

+ 1
- 1
example/ios/example/Info.plist 파일 보기

@@ -15,7 +15,7 @@
15 15
 	<key>CFBundlePackageType</key>
16 16
 	<string>APPL</string>
17 17
 	<key>CFBundleShortVersionString</key>
18
-	<string>1.0</string>
18
+	<string>1.0.0</string>
19 19
 	<key>CFBundleSignature</key>
20 20
 	<string>????</string>
21 21
 	<key>CFBundleVersion</key>

+ 4
- 4
example/src/app.js 파일 보기

@@ -1,10 +1,10 @@
1 1
 import { Navigation } from 'react-native-navigation';
2 2
 
3
-import './screens/FirstTabScreen';
4
-import './screens/SecondTabScreen';
5
-import './screens/ThirdTabScreen';
6
-import './screens/SideMenu';
3
+// screen related book keeping
4
+import { registerScreens } from './screens';
5
+registerScreens();
7 6
 
7
+// this will start our app
8 8
 Navigation.startTabBasedApp({
9 9
   tabs: [
10 10
     {

+ 8
- 17
example/src/screens/FirstTabScreen.js 파일 보기

@@ -1,4 +1,5 @@
1 1
 import React, {
2
+  Component,
2 3
   Text,
3 4
   View,
4 5
   ScrollView,
@@ -7,16 +8,7 @@ import React, {
7 8
   AlertIOS
8 9
 } from 'react-native';
9 10
 
10
-// important imports, the magic is here
11
-import { Navigation, Screen } from 'react-native-navigation';
12
-
13
-// need to import every screen we push
14
-import './PushedScreen';
15
-import './StyledScreen';
16
-import './ModalScreen';
17
-
18
-// instead of React.Component, we extend Screen (imported above)
19
-class FirstTabScreen extends Screen {
11
+export default class FirstTabScreen extends Component {
20 12
   static navigatorButtons = {
21 13
     leftButtons: [{
22 14
       icon: require('../../img/navicon_menu.png'),
@@ -35,10 +27,12 @@ class FirstTabScreen extends Screen {
35 27
   };
36 28
   constructor(props) {
37 29
     super(props);
30
+    // if you want to listen on navigator events, set this up
31
+    this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
38 32
   }
39 33
   onNavigatorEvent(event) {
40 34
     if (event.id == 'menu') {
41
-      this.navigator.toggleDrawer({
35
+      this.props.navigator.toggleDrawer({
42 36
         side: 'left',
43 37
         animated: true
44 38
       });
@@ -70,19 +64,19 @@ class FirstTabScreen extends Screen {
70 64
     );
71 65
   }
72 66
   onPushPress() {
73
-    this.navigator.push({
67
+    this.props.navigator.push({
74 68
       title: "More",
75 69
       screen: "example.PushedScreen"
76 70
     });
77 71
   }
78 72
   onPushStyledPress() {
79
-    this.navigator.push({
73
+    this.props.navigator.push({
80 74
       title: "Styled",
81 75
       screen: "example.StyledScreen"
82 76
     });
83 77
   }
84 78
   onModalPress() {
85
-    this.navigator.showModal({
79
+    this.props.navigator.showModal({
86 80
       title: "Modal",
87 81
       screen: "example.ModalScreen"
88 82
     });
@@ -98,6 +92,3 @@ const styles = StyleSheet.create({
98 92
     color: 'blue'
99 93
   }
100 94
 });
101
-
102
-// every screen must be registered with a unique name
103
-Navigation.registerScreen('example.FirstTabScreen', () => FirstTabScreen);

+ 8
- 16
example/src/screens/ModalScreen.js 파일 보기

@@ -1,4 +1,5 @@
1 1
 import React, {
2
+  Component,
2 3
   Text,
3 4
   View,
4 5
   ScrollView,
@@ -6,15 +7,7 @@ import React, {
6 7
   StyleSheet
7 8
 } from 'react-native';
8 9
 
9
-// important imports, the magic is here
10
-import { Navigation, Screen } from 'react-native-navigation';
11
-
12
-// need to import every screen we push
13
-import './PushedScreen';
14
-import './StyledScreen';
15
-
16
-// instead of React.Component, we extend Screen (imported above)
17
-class ModalScreen extends Screen {
10
+export default class ModalScreen extends Component {
18 11
   static navigatorButtons = {
19 12
     leftButtons: [{
20 13
       title: 'Close',
@@ -23,6 +16,8 @@ class ModalScreen extends Screen {
23 16
   };
24 17
   constructor(props) {
25 18
     super(props);
19
+    // if you want to listen on navigator events, set this up
20
+    this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
26 21
   }
27 22
   render() {
28 23
     return (
@@ -45,23 +40,23 @@ class ModalScreen extends Screen {
45 40
   }
46 41
   onNavigatorEvent(event) {
47 42
     if (event.id == 'close') {
48
-      this.navigator.dismissModal();
43
+      this.props.navigator.dismissModal();
49 44
     }
50 45
   }
51 46
   onPushPress() {
52
-    this.navigator.push({
47
+    this.props.navigator.push({
53 48
       title: "More",
54 49
       screen: "example.PushedScreen"
55 50
     });
56 51
   }
57 52
   onPushStyledPress() {
58
-    this.navigator.push({
53
+    this.props.navigator.push({
59 54
       title: "More",
60 55
       screen: "example.StyledScreen"
61 56
     });
62 57
   }
63 58
   onClosePress() {
64
-    this.navigator.dismissModal();
59
+    this.props.navigator.dismissModal();
65 60
   }
66 61
 }
67 62
 
@@ -74,6 +69,3 @@ const styles = StyleSheet.create({
74 69
     color: 'blue'
75 70
   }
76 71
 });
77
-
78
-// every screen must be registered with a unique name
79
-Navigation.registerScreen('example.ModalScreen', () => ModalScreen);

+ 7
- 17
example/src/screens/PushedScreen.js 파일 보기

@@ -1,4 +1,5 @@
1 1
 import React, {
2
+  Component,
2 3
   Text,
3 4
   View,
4 5
   ScrollView,
@@ -6,15 +7,7 @@ import React, {
6 7
   StyleSheet
7 8
 } from 'react-native';
8 9
 
9
-// important imports, the magic is here
10
-import { Navigation, Screen } from 'react-native-navigation';
11
-
12
-// need to import every screen we push
13
-import './PushedScreen';
14
-import './StyledScreen';
15
-
16
-// instead of React.Component, we extend Screen (imported above)
17
-class PushedScreen extends Screen {
10
+export default class PushedScreen extends Component {
18 11
   constructor(props) {
19 12
     super(props);
20 13
   }
@@ -46,25 +39,25 @@ class PushedScreen extends Screen {
46 39
     );
47 40
   }
48 41
   onPushPress() {
49
-    this.navigator.push({
42
+    this.props.navigator.push({
50 43
       title: "More",
51 44
       screen: "example.PushedScreen"
52 45
     });
53 46
   }
54 47
   onPushStyledPress() {
55
-    this.navigator.push({
48
+    this.props.navigator.push({
56 49
       title: "More",
57 50
       screen: "example.StyledScreen"
58 51
     });
59 52
   }
60 53
   onPopPress() {
61
-    this.navigator.pop();
54
+    this.props.navigator.pop();
62 55
   }
63 56
   onPopToRootPress() {
64
-    this.navigator.popToRoot();
57
+    this.props.navigator.popToRoot();
65 58
   }
66 59
   onResetToPress() {
67
-    this.navigator.resetTo({
60
+    this.props.navigator.resetTo({
68 61
       title: "New Root",
69 62
       screen: "example.PushedScreen"
70 63
     });
@@ -80,6 +73,3 @@ const styles = StyleSheet.create({
80 73
     color: 'blue'
81 74
   }
82 75
 });
83
-
84
-// every screen must be registered with a unique name
85
-Navigation.registerScreen('example.PushedScreen', () => PushedScreen);

+ 6
- 10
example/src/screens/SecondTabScreen.js 파일 보기

@@ -1,4 +1,5 @@
1 1
 import React, {
2
+  Component,
2 3
   Text,
3 4
   View,
4 5
   ScrollView,
@@ -7,14 +8,12 @@ import React, {
7 8
   AlertIOS
8 9
 } from 'react-native';
9 10
 
10
-// important imports, the magic is here
11
-import { Navigation, Screen } from 'react-native-navigation';
12
-
13
-// instead of React.Component, we extend Screen (imported above)
14
-class SecondTabScreen extends Screen {
11
+export default class SecondTabScreen extends Component {
15 12
   constructor(props) {
16 13
     super(props);
17 14
     this.buttonsCounter = 0;
15
+    // if you want to listen on navigator events, set this up
16
+    this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
18 17
   }
19 18
   render() {
20 19
     return (
@@ -32,7 +31,7 @@ class SecondTabScreen extends Screen {
32 31
     );
33 32
   }
34 33
   onChangeTitlePress() {
35
-    this.navigator.setTitle({
34
+    this.props.navigator.setTitle({
36 35
       title: Math.round(Math.random() * 100000).toString()
37 36
     });
38 37
   }
@@ -59,7 +58,7 @@ class SecondTabScreen extends Screen {
59 58
     }
60 59
     this.buttonsCounter++;
61 60
 
62
-    this.navigator.setButtons({
61
+    this.props.navigator.setButtons({
63 62
       rightButtons: buttons,
64 63
       animated: true
65 64
     });
@@ -86,6 +85,3 @@ const styles = StyleSheet.create({
86 85
     color: 'blue'
87 86
   }
88 87
 });
89
-
90
-// every screen must be registered with a unique name
91
-Navigation.registerScreen('example.SecondTabScreen', () => SecondTabScreen);

+ 2
- 8
example/src/screens/SideMenu.js 파일 보기

@@ -1,4 +1,5 @@
1 1
 import React, {
2
+  Component,
2 3
   Text,
3 4
   View,
4 5
   ScrollView,
@@ -7,11 +8,7 @@ import React, {
7 8
   AlertIOS
8 9
 } from 'react-native';
9 10
 
10
-// important imports, the magic is here
11
-import { Navigation, Screen } from 'react-native-navigation';
12
-
13
-// instead of React.Component, we extend Screen (imported above)
14
-class SideMenu extends Screen {
11
+export default class SideMenu extends Component {
15 12
   constructor(props) {
16 13
     super(props);
17 14
   }
@@ -35,6 +32,3 @@ const styles = StyleSheet.create({
35 32
     color: 'blue'
36 33
   }
37 34
 });
38
-
39
-// every screen must be registered with a unique name
40
-Navigation.registerScreen('example.SideMenu', () => SideMenu);

+ 7
- 15
example/src/screens/StyledScreen.js 파일 보기

@@ -1,4 +1,5 @@
1 1
 import React, {
2
+  Component,
2 3
   Text,
3 4
   View,
4 5
   ScrollView,
@@ -8,15 +9,7 @@ import React, {
8 9
   AlertIOS
9 10
 } from 'react-native';
10 11
 
11
-// important imports, the magic is here
12
-import { Navigation, Screen } from 'react-native-navigation';
13
-
14
-// need to import every screen we push
15
-import './PushedScreen';
16
-import './StyledScreen';
17
-
18
-// instead of React.Component, we extend Screen (imported above)
19
-class StyledScreen extends Screen {
12
+export default class StyledScreen extends Component {
20 13
   static navigatorStyle = {
21 14
     drawUnderNavBar: true,
22 15
     drawUnderTabBar: true,
@@ -31,6 +24,8 @@ class StyledScreen extends Screen {
31 24
   };
32 25
   constructor(props) {
33 26
     super(props);
27
+    // if you want to listen on navigator events, set this up
28
+    this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
34 29
   }
35 30
   render() {
36 31
     return (
@@ -63,19 +58,19 @@ class StyledScreen extends Screen {
63 58
     }
64 59
   }
65 60
   onPushPress() {
66
-    this.navigator.push({
61
+    this.props.navigator.push({
67 62
       title: "More",
68 63
       screen: "example.PushedScreen"
69 64
     });
70 65
   }
71 66
   onPushStyledPress() {
72
-    this.navigator.push({
67
+    this.props.navigator.push({
73 68
       title: "More",
74 69
       screen: "example.StyledScreen"
75 70
     });
76 71
   }
77 72
   onPopPress() {
78
-    this.navigator.pop();
73
+    this.props.navigator.pop();
79 74
   }
80 75
 }
81 76
 
@@ -88,6 +83,3 @@ const styles = StyleSheet.create({
88 83
     color: 'blue'
89 84
   }
90 85
 });
91
-
92
-// every screen must be registered with a unique name
93
-Navigation.registerScreen('example.StyledScreen', () => StyledScreen);

+ 5
- 16
example/src/screens/ThirdTabScreen.js 파일 보기

@@ -1,4 +1,5 @@
1 1
 import React, {
2
+  Component,
2 3
   Text,
3 4
   View,
4 5
   ScrollView,
@@ -6,16 +7,7 @@ import React, {
6 7
   StyleSheet
7 8
 } from 'react-native';
8 9
 
9
-// important imports, the magic is here
10
-import { Navigation, Screen } from 'react-native-navigation';
11
-
12
-// need to import every screen we push
13
-import './PushedScreen';
14
-import './StyledScreen';
15
-import './ModalScreen';
16
-
17
-// instead of React.Component, we extend Screen (imported above)
18
-class ThirdTabScreen extends Screen {
10
+export default class ThirdTabScreen extends Component {
19 11
   constructor(props) {
20 12
     super(props);
21 13
   }
@@ -39,19 +31,19 @@ class ThirdTabScreen extends Screen {
39 31
     );
40 32
   }
41 33
   onPushPress() {
42
-    this.navigator.push({
34
+    this.props.navigator.push({
43 35
       title: "More",
44 36
       screen: "example.PushedScreen"
45 37
     });
46 38
   }
47 39
   onPushStyledPress() {
48
-    this.navigator.push({
40
+    this.props.navigator.push({
49 41
       title: "Styled",
50 42
       screen: "example.StyledScreen"
51 43
     });
52 44
   }
53 45
   onModalPress() {
54
-    this.navigator.showModal({
46
+    this.props.navigator.showModal({
55 47
       title: "Modal",
56 48
       screen: "example.ModalScreen"
57 49
     });
@@ -67,6 +59,3 @@ const styles = StyleSheet.create({
67 59
     color: 'blue'
68 60
   }
69 61
 });
70
-
71
-// every screen must be registered with a unique name
72
-Navigation.registerScreen('example.ThirdTabScreen', () => ThirdTabScreen);

+ 20
- 0
example/src/screens/index.js 파일 보기

@@ -0,0 +1,20 @@
1
+import { Navigation } from 'react-native-navigation';
2
+
3
+import FirstTabScreen from './FirstTabScreen';
4
+import SecondTabScreen from './SecondTabScreen';
5
+import ThirdTabScreen from './ThirdTabScreen';
6
+import PushedScreen from './PushedScreen';
7
+import StyledScreen from './StyledScreen';
8
+import ModalScreen from './ModalScreen';
9
+import SideMenu from './SideMenu';
10
+
11
+// register all screens of the app (including internal ones)
12
+export function registerScreens() {
13
+  Navigation.registerComponent('example.FirstTabScreen', () => FirstTabScreen);
14
+  Navigation.registerComponent('example.SecondTabScreen', () => SecondTabScreen);
15
+  Navigation.registerComponent('example.ThirdTabScreen', () => ThirdTabScreen);
16
+  Navigation.registerComponent('example.PushedScreen', () => PushedScreen);
17
+  Navigation.registerComponent('example.StyledScreen', () => StyledScreen);
18
+  Navigation.registerComponent('example.ModalScreen', () => ModalScreen);
19
+  Navigation.registerComponent('example.SideMenu', () => SideMenu);
20
+}

+ 4
- 1
package.json 파일 보기

@@ -7,7 +7,7 @@
7 7
     "type": "git",
8 8
     "url": "https://github.com/wix/react-native-navigation.git"
9 9
   },
10
-  "version": "0.0.5",
10
+  "version": "0.1.0",
11 11
   "description": "React Native Navigation - truly native navigation for iOS and Android",
12 12
   "nativePackage": true,
13 13
   "bugs": {
@@ -22,5 +22,8 @@
22 22
   },
23 23
   "dependencies": {
24 24
     "react-native-controllers": "^1.2.3"
25
+  },
26
+  "optionalDependencies": {
27
+    "react-redux": "*"
25 28
   }
26 29
 }

+ 42
- 1
src/Navigation.js 파일 보기

@@ -1,5 +1,6 @@
1
-import { AppRegistry } from 'react-native';
1
+import React, { AppRegistry } from 'react-native';
2 2
 import platformSpecific from './platformSpecific';
3
+import Screen from './Screen';
3 4
 
4 5
 const registeredScreens = {};
5 6
 
@@ -8,6 +9,45 @@ function registerScreen(screenID, generator) {
8 9
   AppRegistry.registerComponent(screenID, generator);
9 10
 }
10 11
 
12
+function registerComponent(screenID, generator, store = undefined, Provider = undefined) {
13
+  if (store && Provider) return _registerComponentRedux(screenID, generator, store, Provider);
14
+  else return _registerComponentNoRedux(screenID, generator);
15
+}
16
+
17
+function _registerComponentNoRedux(screenID, generator) {
18
+  const generatorWrapper = function() {
19
+    const InternalComponent = generator();
20
+    return class extends Screen {
21
+      static navigatorStyle = InternalComponent.navigatorStyle || {};
22
+      static navigatorButtons = InternalComponent.navigatorButtons || {};
23
+      render() {
24
+        return (
25
+          <InternalComponent navigator={this.navigator} {...this.props} />
26
+        );
27
+      }
28
+    };
29
+  }
30
+  registerScreen(screenID, generatorWrapper);
31
+}
32
+
33
+function _registerComponentRedux(screenID, generator, store, Provider) {
34
+  const generatorWrapper = function() {
35
+    const InternalComponent = generator();
36
+    return class extends Screen {
37
+      static navigatorStyle = InternalComponent.navigatorStyle || {};
38
+      static navigatorButtons = InternalComponent.navigatorButtons || {};
39
+      render() {
40
+        return (
41
+          <Provider store={store}>
42
+            <InternalComponent navigator={this.navigator} {...this.props} />
43
+          </Provider>
44
+        );
45
+      }
46
+    };
47
+  }
48
+  registerScreen(screenID, generatorWrapper);
49
+}
50
+
11 51
 function getRegisteredScreen(screenID) {
12 52
   const generator = registeredScreens[screenID];
13 53
   if (!generator) {
@@ -28,6 +68,7 @@ function dismissModal(params = {}) {
28 68
 export default Navigation = {
29 69
   registerScreen,
30 70
   getRegisteredScreen,
71
+  registerComponent,
31 72
   showModal,
32 73
   dismissModal,
33 74
   startTabBasedApp: platformSpecific.startTabBasedApp,

+ 25
- 18
src/Screen.js 파일 보기

@@ -3,9 +3,11 @@ import platformSpecific from './platformSpecific';
3 3
 import Navigation from './Navigation';
4 4
 
5 5
 class Navigator {
6
-  constructor(navigatorID, screenInstance) {
6
+  constructor(navigatorID, navigatorEventID) {
7 7
     this.navigatorID = navigatorID;
8
-    this.screenInstance = screenInstance;
8
+    this.navigatorEventID = navigatorEventID;
9
+    this.navigatorEventHandler = null;
10
+    this.navigatorEventSubscription = null;
9 11
   }
10 12
   push(params = {}) {
11 13
     return platformSpecific.navigatorPush(this, params);
@@ -26,8 +28,7 @@ class Navigator {
26 28
     return Navigation.dismissModal(params);
27 29
   }
28 30
   setButtons(params = {}) {
29
-    const navigatorEventID = this.screenInstance.listenOnNavigatorEvents();
30
-    return platformSpecific.navigatorSetButtons(this, navigatorEventID, params);
31
+    return platformSpecific.navigatorSetButtons(this, this.navigatorEventID, params);
31 32
   }
32 33
   setTitle(params = {}) {
33 34
     return platformSpecific.navigatorSetTitle(this, params);
@@ -35,6 +36,22 @@ class Navigator {
35 36
   toggleDrawer(params = {}) {
36 37
     return platformSpecific.navigatorToggleDrawer(this, params);
37 38
   }
39
+  setOnNavigatorEvent(callback) {
40
+    this.navigatorEventHandler = callback;
41
+    if (!this.navigatorEventSubscription) {
42
+      this.navigatorEventSubscription = NativeAppEventEmitter.addListener(this.navigatorEventID, (event) => this.onNavigatorEvent(event));
43
+    }
44
+  }
45
+  onNavigatorEvent(event) {
46
+    if (this.navigatorEventHandler) {
47
+      this.navigatorEventHandler(event);
48
+    }
49
+  }
50
+  cleanup() {
51
+    if (this.navigatorEventSubscription) {
52
+      this.navigatorEventSubscription.remove();
53
+    }
54
+  }
38 55
 }
39 56
 
40 57
 export default class Screen extends Component {
@@ -43,23 +60,13 @@ export default class Screen extends Component {
43 60
   constructor(props) {
44 61
     super(props);
45 62
     if (props.navigatorID) {
46
-      this.navigator = new Navigator(props.navigatorID, this);
47
-    }
48
-    if (props.listenForEvents) {
49
-      this.listenOnNavigatorEvents();
63
+      this.navigator = new Navigator(props.navigatorID, props.navigatorEventID);
50 64
     }
51 65
   }
52
-  listenOnNavigatorEvents() {
53
-    if (!this.navigatorEventSubscription) {
54
-      this.navigatorEventSubscription = NativeAppEventEmitter.addListener(this.props.navigatorEventID, (event) => this.onNavigatorEvent(event));
55
-    }
56
-    return this.props.navigatorEventID;
57
-  }
58
-  onNavigatorEvent(event) {}
59 66
   componentWillUnmount() {
60
-    this.navigator = undefined;
61
-    if (this.navigatorEventSubscription) {
62
-      this.navigatorEventSubscription.remove();
67
+    if (this.navigator) {
68
+      this.navigator.cleanup();
69
+      this.navigator = undefined;
63 70
     }
64 71
   }
65 72
 }

+ 2
- 7
src/platformSpecific.ios.js 파일 보기

@@ -58,8 +58,7 @@ function startTabBasedApp(params) {
58 58
                   passProps={{
59 59
                     navigatorID: navigatorID,
60 60
                     screenInstanceID: screenInstanceID,
61
-                    navigatorEventID: navigatorEventID,
62
-                    listenForEvents: !!(navigatorButtons.leftButtons || navigatorButtons.rightButtons)
61
+                    navigatorEventID: navigatorEventID
63 62
                   }}
64 63
                   style={navigatorStyle}
65 64
                   leftButtons={navigatorButtons.leftButtons}
@@ -122,8 +121,7 @@ function startSingleScreenApp(params) {
122 121
           passProps={{
123 122
             navigatorID: navigatorID,
124 123
             screenInstanceID: screenInstanceID,
125
-            navigatorEventID: navigatorEventID,
126
-            listenForEvents: !!(navigatorButtons.leftButtons || navigatorButtons.rightButtons)
124
+            navigatorEventID: navigatorEventID
127 125
           }}
128 126
           style={navigatorStyle}
129 127
           leftButtons={navigatorButtons.leftButtons}
@@ -177,7 +175,6 @@ function navigatorPush(navigator, params) {
177 175
   passProps.navigatorID = navigator.navigatorID;
178 176
   passProps.screenInstanceID = screenInstanceID;
179 177
   passProps.navigatorEventID = navigatorEventID;
180
-  passProps.listenForEvents = !!(navigatorButtons.leftButtons || navigatorButtons.rightButtons);
181 178
   Controllers.NavigationControllerIOS(navigator.navigatorID).push({
182 179
     title: params.title,
183 180
     component: params.screen,
@@ -217,7 +214,6 @@ function navigatorResetTo(navigator, params) {
217 214
   passProps.navigatorID = navigator.navigatorID;
218 215
   passProps.screenInstanceID = screenInstanceID;
219 216
   passProps.navigatorEventID = navigatorEventID;
220
-  passProps.listenForEvents = !!(navigatorButtons.leftButtons || navigatorButtons.rightButtons);
221 217
   Controllers.NavigationControllerIOS(navigator.navigatorID).resetTo({
222 218
     title: params.title,
223 219
     component: params.screen,
@@ -279,7 +275,6 @@ function showModal(params) {
279 275
       passProps.navigatorID = navigatorID;
280 276
       passProps.screenInstanceID = screenInstanceID;
281 277
       passProps.navigatorEventID = navigatorEventID;
282
-      passProps.listenForEvents = !!(navigatorButtons.leftButtons || navigatorButtons.rightButtons);
283 278
       return (
284 279
         <NavigationControllerIOS
285 280
           id={navigatorID}