From a8aedb3e78c786f1fcba99319ef88b4a3c376692 Mon Sep 17 00:00:00 2001 From: Alex Akers Date: Tue, 10 Mar 2015 12:09:35 -0700 Subject: [PATCH] [React Native][Device ID][wip] implement most basic js access --- Examples/UIExplorer/AdSupportIOSExample.js | 73 +++++++++++ .../UIExplorer.xcodeproj/project.pbxproj | 34 ++++- Examples/UIExplorer/UIExplorerList.js | 1 + Libraries/AdSupport/AdSupportIOS.js | 14 ++ Libraries/AdSupport/RCTAdSupport.h | 7 + Libraries/AdSupport/RCTAdSupport.m | 20 +++ Libraries/Picker/PickerIOS.android.js | 10 ++ Libraries/Picker/PickerIOS.ios.js | 120 ++++++++++++++++++ ReactKit/ReactKit.xcodeproj/project.pbxproj | 2 + 9 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 Examples/UIExplorer/AdSupportIOSExample.js create mode 100644 Libraries/AdSupport/AdSupportIOS.js create mode 100644 Libraries/AdSupport/RCTAdSupport.h create mode 100644 Libraries/AdSupport/RCTAdSupport.m create mode 100644 Libraries/Picker/PickerIOS.android.js create mode 100644 Libraries/Picker/PickerIOS.ios.js diff --git a/Examples/UIExplorer/AdSupportIOSExample.js b/Examples/UIExplorer/AdSupportIOSExample.js new file mode 100644 index 00000000000..1da508fd946 --- /dev/null +++ b/Examples/UIExplorer/AdSupportIOSExample.js @@ -0,0 +1,73 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + * + * @providesModule AdSupportIOSExample + */ +/* eslint no-console: 0 */ +'use strict'; + +var AdSupportIOS = require('AdSupportIOS'); + +var React = require('react-native'); +var { + StyleSheet, + Text, + View, +} = React; + +exports.framework = 'React'; +exports.title = 'Advertising ID'; +exports.description = 'Example of using the ad support API.'; + +exports.examples = [ + { + title: 'Ad Support IOS', + render: function() { + return ; + }, + } +]; + +var AdSupportIOSExample = React.createClass({ + getInitialState: function() { + return { + deviceID: 'No IDFA yet', + }; + }, + + componentDidMount: function() { + AdSupportIOS.getAdvertisingId( + this._onSuccess, + this._onFailure + ); + }, + + _onSuccess: function(deviceID) { + this.setState({ + 'deviceID': deviceID, + }); + }, + + _onFailure: function(e) { + this.setState({ + 'deviceID': 'Error!', + }); + }, + + render: function() { + return ( + + + Advertising ID: + {JSON.stringify(this.state.deviceID)} + + + ); + } +}); + +var styles = StyleSheet.create({ + title: { + fontWeight: 'bold', + }, +}); diff --git a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj index d397eea3689..05b0eaf7fd6 100644 --- a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj +++ b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 832C81C71AAF73C5007FA2F7 /* libRCTAdSupport.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832C81A61AAF6EFF007FA2F7 /* libRCTAdSupport.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -54,6 +55,13 @@ remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteInfo = RCTGeolocation; }; + 832C81A51AAF6EFF007FA2F7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 832C81A11AAF6EFE007FA2F7 /* RCTAdSupport.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; + remoteInfo = RCTAdSupport; + }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ @@ -69,6 +77,7 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 832C81A11AAF6EFE007FA2F7 /* RCTAdSupport.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAdSupport.xcodeproj; path = ../../Libraries/AdSupport/RCTAdSupport.xcodeproj; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -76,6 +85,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 832C81C71AAF73C5007FA2F7 /* libRCTAdSupport.a in Frameworks */, 134A8A2A1AACED7A00945AAE /* libRCTGeolocation.a in Frameworks */, 1341802C1AA9178B003F314A /* libRCTNetwork.a in Frameworks */, 134180011AA9153C003F314A /* libRCTText.a in Frameworks */, @@ -91,10 +101,11 @@ isa = PBXGroup; children = ( 13417FFA1AA91531003F314A /* ReactKit.xcodeproj */, - 134180261AA91779003F314A /* RCTNetwork.xcodeproj */, + 832C81A11AAF6EFE007FA2F7 /* RCTAdSupport.xcodeproj */, 134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */, - 13417FEA1AA914B8003F314A /* RCTText.xcodeproj */, 13417FE31AA91428003F314A /* RCTImage.xcodeproj */, + 134180261AA91779003F314A /* RCTNetwork.xcodeproj */, + 13417FEA1AA914B8003F314A /* RCTText.xcodeproj */, ); name = Libraries; sourceTree = ""; @@ -152,6 +163,14 @@ name = UIExplorer; sourceTree = ""; }; + 832C81A21AAF6EFE007FA2F7 /* Products */ = { + isa = PBXGroup; + children = ( + 832C81A61AAF6EFF007FA2F7 /* libRCTAdSupport.a */, + ); + name = Products; + sourceTree = ""; + }; 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( @@ -210,6 +229,10 @@ productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; projectDirPath = ""; projectReferences = ( + { + ProductGroup = 832C81A21AAF6EFE007FA2F7 /* Products */; + ProjectRef = 832C81A11AAF6EFE007FA2F7 /* RCTAdSupport.xcodeproj */; + }, { ProductGroup = 134A8A211AACED6A00945AAE /* Products */; ProjectRef = 134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */; @@ -274,6 +297,13 @@ remoteRef = 134A8A241AACED6A00945AAE /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 832C81A61AAF6EFF007FA2F7 /* libRCTAdSupport.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTAdSupport.a; + remoteRef = 832C81A51AAF6EFF007FA2F7 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ diff --git a/Examples/UIExplorer/UIExplorerList.js b/Examples/UIExplorer/UIExplorerList.js index ff0d71be8ff..63c98383883 100644 --- a/Examples/UIExplorer/UIExplorerList.js +++ b/Examples/UIExplorer/UIExplorerList.js @@ -39,6 +39,7 @@ var EXAMPLES = [ require('./SliderExample'), require('./CameraRollExample.ios'), require('./MapViewExample'), + require('./AdSupportIOSExample'), ]; var UIExplorerList = React.createClass({ diff --git a/Libraries/AdSupport/AdSupportIOS.js b/Libraries/AdSupport/AdSupportIOS.js new file mode 100644 index 00000000000..598befd3121 --- /dev/null +++ b/Libraries/AdSupport/AdSupportIOS.js @@ -0,0 +1,14 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + * + * @providesModule AdSupportIOS + */ +'use strict'; + +var AdSupport = require('NativeModules').RCTAdSupport; + +module.exports = { + getAdvertisingId: function(onSuccess, onFailure) { + AdSupport.getAdvertisingId(onSuccess, onFailure); + }, +}; diff --git a/Libraries/AdSupport/RCTAdSupport.h b/Libraries/AdSupport/RCTAdSupport.h new file mode 100644 index 00000000000..2fbd3a8c00b --- /dev/null +++ b/Libraries/AdSupport/RCTAdSupport.h @@ -0,0 +1,7 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import "RCTBridgeModule.h" + +@interface RCTAdSupport : NSObject + +@end diff --git a/Libraries/AdSupport/RCTAdSupport.m b/Libraries/AdSupport/RCTAdSupport.m new file mode 100644 index 00000000000..0cff2401255 --- /dev/null +++ b/Libraries/AdSupport/RCTAdSupport.m @@ -0,0 +1,20 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import "RCTAdSupport.h" + +#import + +@implementation RCTAdSupport + +- (void)getAdvertisingId:(RCTResponseSenderBlock)callback withErrorCallback:(RCTResponseSenderBlock)errorCallback +{ + RCT_EXPORT(); + + if ([ASIdentifierManager class]) { + callback(@[[[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]]); + } else { + return errorCallback(@[@"as_identifier_unavailable"]); + } +} + +@end diff --git a/Libraries/Picker/PickerIOS.android.js b/Libraries/Picker/PickerIOS.android.js new file mode 100644 index 00000000000..72cc87a7555 --- /dev/null +++ b/Libraries/Picker/PickerIOS.android.js @@ -0,0 +1,10 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + * + * @providesModule PickerIOS + * + * This is a controlled component version of RKPickerIOS + */ +'use strict'; + +module.exports = require('UnimplementedView'); diff --git a/Libraries/Picker/PickerIOS.ios.js b/Libraries/Picker/PickerIOS.ios.js new file mode 100644 index 00000000000..04559a70009 --- /dev/null +++ b/Libraries/Picker/PickerIOS.ios.js @@ -0,0 +1,120 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + * + * @providesModule PickerIOS + * + * This is a controlled component version of RKPickerIOS + */ +'use strict'; + +var NativeMethodsMixin = require('NativeMethodsMixin'); +var React = require('React'); +var ReactChildren = require('ReactChildren'); +var ReactIOSViewAttributes = require('ReactIOSViewAttributes'); +var RKPickerIOSConsts = require('NativeModules').RKUIManager.RCTPicker.Constants; +var StyleSheet = require('StyleSheet'); +var View = require('View'); + +var createReactIOSNativeComponentClass = + require('createReactIOSNativeComponentClass'); +var merge = require('merge'); + +var PICKER = 'picker'; + +var PickerIOS = React.createClass({ + mixins: [NativeMethodsMixin], + + propTypes: { + onValueChange: React.PropTypes.func, + selectedValue: React.PropTypes.any, // string or integer basically + }, + + getInitialState: function() { + return this._stateFromProps(this.props); + }, + + componentWillReceiveProps: function(nextProps) { + this.setState(this._stateFromProps(nextProps)); + }, + + // Translate PickerIOS prop and children into stuff that RKPickerIOS understands. + _stateFromProps: function(props) { + var selectedIndex = 0; + var items = []; + ReactChildren.forEach(props.children, function (child, index) { + if (child.props.value === props.selectedValue) { + selectedIndex = index; + } + items.push({value: child.props.value, label: child.props.label}); + }); + return {selectedIndex, items}; + }, + + render: function() { + return ( + + + + ); + }, + + _onChange: function(event) { + if (this.props.onChange) { + this.props.onChange(event); + } + if (this.props.onValueChange) { + this.props.onValueChange(event.nativeEvent.newValue); + } + + // The picker is a controlled component. This means we expect the + // on*Change handlers to be in charge of updating our + // `selectedValue` prop. That way they can also + // disallow/undo/mutate the selection of certain values. In other + // words, the embedder of this component should be the source of + // truth, not the native component. + if (this.state.selectedIndex !== event.nativeEvent.newIndex) { + this.refs[PICKER].setNativeProps({ + selectedIndex: this.state.selectedIndex + }); + } + }, +}); + +PickerIOS.Item = React.createClass({ + propTypes: { + value: React.PropTypes.any, // string or integer basically + label: React.PropTypes.string, + }, + + render: function() { + // These items don't get rendered directly. + return null; + }, +}); + +var styles = StyleSheet.create({ + rkPickerIOS: { + // The picker will conform to whatever width is given, but we do + // have to set the component's height explicitly on the + // surrounding view to ensure it gets rendered. + height: RKPickerIOSConsts.ComponentHeight, + }, +}); + +var rkPickerIOSAttributes = merge(ReactIOSViewAttributes.UIView, { + items: true, + selectedIndex: true, +}); + +var RKPickerIOS = createReactIOSNativeComponentClass({ + validAttributes: rkPickerIOSAttributes, + uiViewClassName: 'RCTPicker', +}); + +module.exports = PickerIOS; diff --git a/ReactKit/ReactKit.xcodeproj/project.pbxproj b/ReactKit/ReactKit.xcodeproj/project.pbxproj index e1c76e3afd7..c2efa2125eb 100644 --- a/ReactKit/ReactKit.xcodeproj/project.pbxproj +++ b/ReactKit/ReactKit.xcodeproj/project.pbxproj @@ -212,6 +212,8 @@ 13B07FE81A69327A00A75B9A /* RCTAlertManager.m */, 13B07FE91A69327A00A75B9A /* RCTExceptionsManager.h */, 13B07FEA1A69327A00A75B9A /* RCTExceptionsManager.m */, + 5F5F0D971A9E456B001279FA /* RCTLocationObserver.h */, + 5F5F0D981A9E456B001279FA /* RCTLocationObserver.m */, 13723B4E1A82FD3C00F88898 /* RCTStatusBarManager.h */, 13723B4F1A82FD3C00F88898 /* RCTStatusBarManager.m */, 13B07FED1A69327A00A75B9A /* RCTTiming.h */,