App linking for UIExplorer iOS

Summary: Add a simple URL parser to add linking support for UIExplorer iOS. Android should be very similar

Reviewed By: javache

Differential Revision: D2931764

fb-gh-sync-id: 0b029106160620267b82bdba510635ce224c5381
shipit-source-id: 0b029106160620267b82bdba510635ce224c5381
This commit is contained in:
Eric Vicenti 2016-02-22 16:15:44 -08:00 committed by facebook-github-bot-6
parent 19f81be9b4
commit e0019f69c1
6 changed files with 109 additions and 9 deletions

View File

@ -1276,4 +1276,4 @@
/* End XCConfigurationList section */
};
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
}
}

View File

@ -16,6 +16,7 @@
#import "RCTBridge.h"
#import "RCTJavaScriptLoader.h"
#import "RCTLinkingManager.h"
#import "RCTRootView.h"
@interface AppDelegate() <RCTBridgeDelegate>
@ -80,6 +81,14 @@
return sourceURL;
}
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
return [RCTLinkingManager application:application openURL:url
sourceApplication:sourceApplication annotation:annotation];
}
- (void)loadSourceForBridge:(RCTBridge *)bridge
withBlock:(RCTSourceLoadBlock)loadCallback
{

View File

@ -18,10 +18,28 @@
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.reactjs.ios</string>
<key>CFBundleURLSchemes</key>
<array>
<string>rnuiexplorer</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string>You need to add NSLocationWhenInUseUsageDescription key in Info.plist to enable geolocation, otherwise it is going to *fail silently*!</string>
<key>UILaunchStoryboardName</key>
@ -38,11 +56,5 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>NSAppTransportSecurity</key>
<dict>
<!--See http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/-->
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</dict>
</plist>

View File

@ -24,6 +24,7 @@ const UIExplorerNavigationReducer = require('./UIExplorerNavigationReducer');
const UIExplorerStateTitleMap = require('./UIExplorerStateTitleMap');
const {
Alert,
Animated,
AppRegistry,
NavigationExperimental,
@ -54,7 +55,36 @@ import type {
UIExplorerExample,
} from './UIExplorerList.ios'
function PathActionMap(path: string): ?Object {
// Warning! Hacky parsing for example code. Use a library for this!
const exampleParts = path.split('/example/');
const exampleKey = exampleParts[1];
if (exampleKey) {
if (!UIExplorerList.Modules[exampleKey]) {
Alert.alert(`${exampleKey} example could not be found!`);
return null;
}
return UIExplorerActions.ExampleAction(exampleKey);
}
return null;
}
function URIActionMap(uri: ?string): ?Object {
// Warning! Hacky parsing for example code. Use a library for this!
if (!uri) {
return null;
}
const parts = uri.split('rnuiexplorer:/');
if (!parts[1]) {
return null;
}
const path = parts[1];
return PathActionMap(path);
}
class UIExplorerApp extends React.Component {
_navigationRootRef: ?NavigationRootContainer;
_renderNavigation: Function;
componentWillMount() {
this._renderNavigation = this._renderNavigation.bind(this);
@ -64,7 +94,9 @@ class UIExplorerApp extends React.Component {
<NavigationRootContainer
persistenceKey="UIExplorerState"
reducer={UIExplorerNavigationReducer}
ref={navRootRef => { this._navigationRootRef = navRootRef; }}
renderNavigation={this._renderNavigation}
linkingActionMap={URIActionMap}
/>
);
}

View File

@ -164,7 +164,7 @@ class NavigationAnimatedView extends React.Component {
if (lastState) {
lastState.children.forEach((child, index) => {
if (!NavigationStateUtils.get(nextState, child.key)) {
if (!NavigationStateUtils.get(nextState, child.key) && index !== nextState.index) {
nextScenes.push({
index,
state: child,

View File

@ -12,6 +12,7 @@
'use strict';
const AsyncStorage = require('AsyncStorage');
const Linking = require('Linking');
const React = require('React');
const BackAndroid = require('BackAndroid');
const Platform = require('Platform');
@ -36,17 +37,44 @@ function getBackAction(): BackAction {
}
type Props = {
/*
* Set up the rendering of the app for a given navigation state
*/
renderNavigation: NavigationRenderer;
/*
* A function that will output the latest navigation state as a function of
* the (optional) previous state, and an action
*/
reducer: NavigationReducer;
/*
* Provide this key, and the container will store the navigation state in
* AsyncStorage through refreshes, with the provided key
*/
persistenceKey: ?string;
/*
* The default action to be passed into the reducer when getting the first
* state. Defaults to {type: 'RootContainerInitialAction'}
*/
initialAction: NavigationAction;
/*
* Provide linkingActionMap to instruct the container to subscribe to linking
* events, and use this mapper to convert URIs into actions that your app can
* handle
*/
linkingActionMap: (uri: string) => NavigationAction;
};
class NavigationRootContainer extends React.Component {
_handleOpenURLEvent: Function;
props: Props;
constructor(props: Props) {
super(props);
this.handleNavigation = this.handleNavigation.bind(this);
this._handleOpenURLEvent = this._handleOpenURLEvent.bind(this);
let navState = null;
if (!this.props.persistenceKey) {
navState = this.props.reducer(null, props.initialAction);
@ -54,6 +82,10 @@ class NavigationRootContainer extends React.Component {
this.state = { navState };
}
componentDidMount() {
if (this.props.LinkingActionMap) {
Linking.getInitialURL().then(this._handleOpenURL.bind(this));
Platform.OS === 'ios' && Linking.addEventListener('url', this._handleOpenURLEvent);
}
if (this.props.persistenceKey) {
AsyncStorage.getItem(this.props.persistenceKey, (err, storedString) => {
if (err || !storedString) {
@ -68,6 +100,21 @@ class NavigationRootContainer extends React.Component {
});
}
}
componentWillUnmount() {
Platform.OS === 'ios' && Linking.removeEventListener('url', this._handleOpenURLEvent);
}
_handleOpenURLEvent(event: {url: string}) {
this._handleOpenURL(event.url);
}
_handleOpenURL(url: ?string) {
if (!this.props.LinkingActionMap) {
return;
}
const action = this.props.LinkingActionMap(url);
if (action) {
this.handleNavigation(action);
}
}
getChildContext(): Object {
return {
onNavigate: this.handleNavigation,
@ -101,7 +148,7 @@ NavigationRootContainer.childContextTypes = {
NavigationRootContainer.defaultProps = {
initialAction: {
type: 'NavigationRootContainerInitialAction',
type: 'RootContainerInitialAction',
},
};