Open ortonomy opened 4 years ago
@ortonomy Thanks a lot. It was working perfectly for me until I tried to add expo-notifications
in my project. As soon as I add import * as Notifications from 'expo-notifications';
in the main app, I see a blank screen in the share extension. Did you ever encounter something similar to this by any chance?
Here's my pod file:
platform :ios, '11.0'
require_relative '../node_modules/react-native-unimodules/cocoapods.rb'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
def add_flipper_pods!(versions = {})
versions['Flipper'] ||= '~> 0.33.1'
versions['DoubleConversion'] ||= '1.1.7'
versions['Flipper-Folly'] ||= '~> 2.1'
versions['Flipper-Glog'] ||= '0.3.6'
versions['Flipper-PeerTalk'] ||= '~> 0.0.4'
versions['Flipper-RSocket'] ||= '~> 1.0'
pod 'FlipperKit', versions['Flipper'], :configuration => 'Debug'
pod 'FlipperKit/FlipperKitLayoutPlugin', versions['Flipper'], :configuration => 'Debug'
pod 'FlipperKit/SKIOSNetworkPlugin', versions['Flipper'], :configuration => 'Debug'
pod 'FlipperKit/FlipperKitUserDefaultsPlugin', versions['Flipper'], :configuration => 'Debug'
pod 'FlipperKit/FlipperKitReactPlugin', versions['Flipper'], :configuration => 'Debug'
# List all transitive dependencies for FlipperKit pods
# to avoid them being linked in Release builds
pod 'Flipper', versions['Flipper'], :configuration => 'Debug'
pod 'Flipper-DoubleConversion', versions['DoubleConversion'], :configuration => 'Debug'
pod 'Flipper-Folly', versions['Flipper-Folly'], :configuration => 'Debug'
pod 'Flipper-Glog', versions['Flipper-Glog'], :configuration => 'Debug'
pod 'Flipper-PeerTalk', versions['Flipper-PeerTalk'], :configuration => 'Debug'
pod 'Flipper-RSocket', versions['Flipper-RSocket'], :configuration => 'Debug'
pod 'FlipperKit/Core', versions['Flipper'], :configuration => 'Debug'
pod 'FlipperKit/CppBridge', versions['Flipper'], :configuration => 'Debug'
pod 'FlipperKit/FBCxxFollyDynamicConvert', versions['Flipper'], :configuration => 'Debug'
pod 'FlipperKit/FBDefines', versions['Flipper'], :configuration => 'Debug'
pod 'FlipperKit/FKPortForwarding', versions['Flipper'], :configuration => 'Debug'
pod 'FlipperKit/FlipperKitHighlightOverlay', versions['Flipper'], :configuration => 'Debug'
pod 'FlipperKit/FlipperKitLayoutTextSearchable', versions['Flipper'], :configuration => 'Debug'
pod 'FlipperKit/FlipperKitNetworkPlugin', versions['Flipper'], :configuration => 'Debug'
end
# Post Install processing for Flipper
def flipper_post_install(installer)
installer.pods_project.targets.each do |target|
if target.name == 'YogaKit'
target.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '4.1'
end
end
end
end
target 'myapp' do
# Pods for myapp
pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector"
pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec"
pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired"
pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety"
pod 'React', :path => '../node_modules/react-native/'
pod 'React-Core', :path => '../node_modules/react-native/'
pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules'
pod 'React-Core/DevSupport', :path => '../node_modules/react-native/'
pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS'
pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation'
pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob'
pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image'
pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS'
pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network'
pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings'
pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text'
pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration'
pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/'
pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact'
pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi'
pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor'
pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector'
pod 'ReactCommon/callinvoker', :path => "../node_modules/react-native/ReactCommon"
pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon"
pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga', :modular_headers => true
pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
use_unimodules!
use_native_modules!
# Enables Flipper.
#
# Note that if you have use_frameworks! enabled, Flipper will not work and
# you should disable these next few lines.
add_flipper_pods!
target 'MyShareEx' do
use_native_modules!
inherit! :complete
end
post_install do |installer|
flipper_post_install(installer)
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'NO'
end
end
end
end
@ortonomy Hey. you tried to use Share in ios 14? My code worked fine for ios 13 and below, but with the transitions to ios 14, an empty window is displayed and quickly closes. I haven't changed the code until I can understand what could be the reason
in the simulator - works great, on a real device - no
@ortonomy Hey. you tried to use Share in ios 14? My code worked fine for ios 13 and below, but with the transitions to ios 14, an empty window is displayed and quickly closes. I haven't changed the code until I can understand what could be the reason
in the simulator - works great, on a real device - no
I have the same issue. In the simulator - works, on a real device - no (IOS 13.7 and IOS 14) react-native 0.63.2
hi @mgvictor7 and @azesmway -- mine is working on iOS 14:
For reference, I've upgraded to the latest React Native (0.63.2) which has an upgrade guide here which massively simplifies your pod file: https://react-native-community.github.io/upgrade-helper/?from=0.62.2&to=0.63.2
For example, after upgrade, this is what my pod file looks like:
# frozen_string_literal: true
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
platform :ios, '10.0'
target 'KeewishReact' do
config = use_native_modules!
use_react_native!(path: config['reactNativePath'])
# # React native permissions
permissions_path = '../node_modules/react-native-permissions/ios'
pod 'Permission-Camera', path: "#{permissions_path}/Camera.podspec"
# for share extension
target 'KeewishShare' do
inherit! :complete
end
target 'KeewishReactTests' do
inherit! :complete
# Pods for testing
end
# Enables Flipper.
# Note that if you have use_frameworks! enabled, Flipper will not work and
# you should disable these next few lines.
use_flipper!
post_install do |installer|
flipper_post_install(installer)
# https://github.com/facebook/react-native/issues/25792
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'NO'
end
end
end
end
FYI: I also have NOT used this extension. I built it myself based on content of this extension:
header
//
// ShareViewController.h
// KeewishShare
//
// Created by Gregory Orton on 24/07/2020.
//
#import <UIKit/UIKit.h>
#import <React/RCTBridgeModule.h>
@interface KeewishShareExtension : UIViewController<RCTBridgeModule>
- (void) shareView;
@end
.m
//
// ShareViewController.m
// KeewishShare
//
// Created by Gregory Orton on 24/07/2020.
//
#import <Foundation/Foundation.h>
#import "KeewishShareExtension.h"
#import <React/RCTRootView.h>
#import <React/RCTBundleURLProvider.h>
#import <MobileCoreServices/MobileCoreServices.h>
NSExtensionContext* extensionContext;
#define URL_IDENTIFIER (NSString *)kUTTypeURL
#define IMAGE_IDENTIFIER (NSString *)kUTTypeImage
//#define TEXT_IDENTIFIER (NSString *)kUTTypePlainText
@implementation KeewishShareExtension {
}
RCT_EXPORT_MODULE();
- (void) shareView {
extensionContext = self.extensionContext; // global for later call to async promise
// set up react native instance
NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL: jsCodeLocation
moduleName: @"KeewishReactShare"
initialProperties: nil
launchOptions: nil];
UIViewController *rootViewController = [UIViewController alloc];
rootViewController.view = rootView;
[self addChildViewController: rootViewController];
rootViewController.view.frame = self.view.bounds;
rootViewController.view.translatesAutoresizingMaskIntoConstraints = false;
[[self view] addSubview:rootViewController.view];
NSArray* constraints = [NSArray arrayWithObjects:
[rootViewController.view.leftAnchor constraintEqualToAnchor: self.view.leftAnchor],
[rootViewController.view.rightAnchor constraintEqualToAnchor: self.view.rightAnchor],
[rootViewController.view.topAnchor constraintEqualToAnchor: self.view.topAnchor],
[rootViewController.view.bottomAnchor constraintEqualToAnchor: self.view.bottomAnchor], nil
];
[NSLayoutConstraint activateConstraints:constraints];
[self didMoveToParentViewController: self];
}
- (void) viewDidLoad {
[super viewDidLoad];
// object variable for extension doesn't work for react-native. It must be assigned to gloabl
// variable extensionContext
extensionContext = self.extensionContext;
// generate react native bundle and views
[self shareView];
}
RCT_REMAP_METHOD(getParameters,
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
[self extractData: extensionContext withCallback:^(NSDictionary* result, NSException* err) {
if(err) {
reject(@"error", err.description, nil);
} else {
resolve(result);
}
}];
}
- (void) extractData: (NSExtensionContext *)context withCallback:(void(^)(NSDictionary* result, NSException *exception))callback {
@try {
// get items shared
NSExtensionItem *item = [context.inputItems firstObject];
__block NSItemProvider *provider = item.attachments.firstObject;
if ([provider hasItemConformingToTypeIdentifier:IMAGE_IDENTIFIER]){
[provider loadItemForTypeIdentifier:IMAGE_IDENTIFIER options:nil completionHandler:^(id<NSSecureCoding> item, NSError *error) {
NSURL *url = (NSURL *)item;
NSDictionary *result = @{@"data": [url absoluteString], @"extension": [[[url absoluteString] pathExtension] lowercaseString], @"type": @"image"};
if(callback) {
callback(result, nil);
}
}];
return;
}
if([provider hasItemConformingToTypeIdentifier:URL_IDENTIFIER]) {
[provider loadItemForTypeIdentifier:URL_IDENTIFIER options:nil completionHandler:^(id<NSSecureCoding> item, NSError *error) {
NSURL *url = (NSURL *)item;
NSDictionary *result = @{@"data": [url absoluteString], @"type": @"url"};
if(callback) {
callback(result, nil);
}
}];
return;
}
// if ([provider hasItemConformingToTypeIdentifier:TEXT_IDENTIFIER]){
// NSString *text = (NSString *)provider;
// return;
// }
if(callback) {
callback(nil, [NSException exceptionWithName:@"Error" reason:@"couldn't find provider" userInfo:nil]);
}
}
@catch (NSException *exception) {
}
}
RCT_EXPORT_METHOD(close) {
[extensionContext completeRequestReturningItems:nil
completionHandler:nil];
// exit(0);
}
+ (BOOL)requiresMainQueueSetup
{
// only do this if your module initialization relies on calling UIKit!
return YES;
}
@end
@ortonomy Hi. If it is possible, can I see - KeewishShareExtension.h, KeewishShareExtension.m and index.js, since my transparent window pops up and everything, tried in various ways?
@azesmway - you just saw them. I've labelled them.
@ortonomy I apologize for my english, I am writing through google translator. I tried to repeat everything as you wrote - an empty window appears and nothing happens
@ortonomy thanks for everything. But still can't get it to work on real devices. When execute this code un real device jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; jsCodeLocation return null!
When execute in the simulator, jsCodeLocation return http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false
@mgvictor7 Hi. The solution from this post helped me. Worked on a real device https://github.com/alinz/react-native-share-extension/issues/182#issuecomment-587006020
@ortonomy But now there is a new problem. Opening the sharing window causes the entire application to start and in no way can I restrict this launch.
AppRegistry.registerComponent ('MyShare', () => MyShare)
AppRegistry.registerComponent (appName, () => App)
Calling "MyShare" compiles and runs "App"
Hey @ortonomy,
Thanks for the walkthrough! It works almost perfectly. I am having an issue retrieving the data on the native side, it is return an error of null.
Do you know what is missing? I can see the modal and everything just no data.
You need to access your module from import { NativeModules } from 'react-native'
NativeModules.your_module_name.data().then(d => ....)
@ortonomy appreciate you putting together this solution. Could you show your JavaScript usage of your functioning product? I’m going to make a tutorial following your instructions to hopefully help some people (including myself) out with this.
Thank you for your help w/ this, @ortonomy .
I'm developing a basic sharing starter app & am running into the following compilation-time error:
Apple Mach-O Linker Error
"_OBJC_CLASS_$_RCTRootView", referenced from:
"_OBJC_CLASS_$_RCTBundleURLProvider", referenced from:
"_RCTRegisterModule", referenced from:
Linker command failed with exit code 1 (use -v to see invocation)
It appears that maybe the the share extension doesn't know where to look for React Native dependencies?
Have tried:
$(inherited)
in header & library search paths (no real change in functionality)Legacy Mode
to compile (just shows a different version of the error above)Any pointers on what I'm doing wrong? Again, thank you for all your help.
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
platform :ios, '11.0'
target 'episogood' do
config = use_native_modules!
use_react_native!(:path => config["reactNativePath"])
target 'EpisogoodShare' do
inherit! :complete
end
# Enables Flipper.
#
# Note that if you have use_frameworks! enabled, Flipper will not work and
# you should disable these next few lines.
use_flipper!
post_install do |installer|
flipper_post_install(installer)
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'NO'
end
end
end
end
#import <UIKit/UIKit.h>
#import <React/RCTBridgeModule.h>
@interface ShareViewController : UIViewController<RCTBridgeModule>
@end
#import "ShareViewController.h"
#import <React/RCTRootView.h>
#import <React/RCTBundleURLProvider.h>
#import <MobileCoreServices/MobileCoreServices.h>
#define URL_IDENTIFIER (NSString *)kUTTypeURL
NSExtensionContext* extensionContext;
@implementation ShareViewController {
}
RCT_EXPORT_MODULE();
- (void) viewDidLoad {
[super viewDidLoad];
extensionContext = self.extensionContext; // global for later call to async promise
// set up react native instance
NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL: jsCodeLocation
moduleName: @"Share"
initialProperties: nil
launchOptions: nil];
UIViewController *rootViewController = [UIViewController alloc];
rootViewController.view = rootView;
[self addChildViewController: rootViewController];
rootViewController.view.frame = self.view.bounds;
rootViewController.view.translatesAutoresizingMaskIntoConstraints = false;
[[self view] addSubview:rootViewController.view];
NSArray* constraints = [NSArray arrayWithObjects:
[rootViewController.view.leftAnchor constraintEqualToAnchor: self.view.leftAnchor],
[rootViewController.view.rightAnchor constraintEqualToAnchor: self.view.rightAnchor],
[rootViewController.view.topAnchor constraintEqualToAnchor: self.view.topAnchor],
[rootViewController.view.bottomAnchor constraintEqualToAnchor: self.view.bottomAnchor], nil
];
[NSLayoutConstraint activateConstraints:constraints];
[self didMoveToParentViewController: self];
}
RCT_REMAP_METHOD(getParameters,
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
[self extractData: extensionContext withCallback:^(NSDictionary* result, NSException* err) {
if(err) {
reject(@"error", err.description, nil);
} else {
resolve(result);
}
}];
}
- (void) extractData: (NSExtensionContext *)context withCallback:(void(^)(NSDictionary* result, NSException *exception))callback {
@try {
// get items shared
NSExtensionItem *item = [context.inputItems firstObject];
__block NSItemProvider *provider = item.attachments.firstObject;
if([provider hasItemConformingToTypeIdentifier:URL_IDENTIFIER]) {
[provider loadItemForTypeIdentifier:URL_IDENTIFIER options:nil completionHandler:^(id<NSSecureCoding> item, NSError *error) {
NSURL *url = (NSURL *)item;
NSDictionary *result = @{@"data": [url absoluteString], @"type": @"url"};
if(callback) {
callback(result, nil);
}
}];
return;
}
if(callback) {
callback(nil, [NSException exceptionWithName:@"Error" reason:@"couldn't find provider" userInfo:nil]);
}
}
@catch (NSException *exception) {
}
}
@end
You need to access your module from
import { NativeModules } from 'react-native'
NativeModules.your_module_name.data().then(d => ....)
Hello, I used your guide to have this library working, I'm able to get the data on my "Sharing" extension
`import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
import Share from './Share';
AppRegistry.registerComponent('AlertaShare', () => Share) <---- Can access data here using NativeModules
AppRegistry.registerComponent(appName, () => App);`
but I'm stuck trying to move that data to my main react native app, wondering if you have any pointers, tried using Linking but it for whatever reason it does not work
@ReneMarquez -- use redux/mobx and redux-persist
to share state? In iOS the share applet and the react native app are essentially two different apps.
I used to AppGroups to make sure the state is shared between the two. ( I needed to store photos for later upload to a server)
Thank you for your help w/ this, @ortonomy .
I'm developing a basic sharing starter app & am running into the following compilation-time error:
Apple Mach-O Linker Error "_OBJC_CLASS_$_RCTRootView", referenced from: "_OBJC_CLASS_$_RCTBundleURLProvider", referenced from: "_RCTRegisterModule", referenced from: Linker command failed with exit code 1 (use -v to see invocation)
It appears that maybe the the share extension doesn't know where to look for React Native dependencies?
Have tried:
- Adding
$(inherited)
in header & library search paths (no real change in functionality)- Adding the React Libraries to the ShareExtension dependencies (then, it can't find the newly-added dependencies)
- Using
Legacy Mode
to compile (just shows a different version of the error above)Any pointers on what I'm doing wrong? Again, thank you for all your help.
react-native-share-starter
Podfile
require_relative '../node_modules/react-native/scripts/react_native_pods' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' platform :ios, '11.0' target 'episogood' do config = use_native_modules! use_react_native!(:path => config["reactNativePath"]) target 'EpisogoodShare' do inherit! :complete end # Enables Flipper. # # Note that if you have use_frameworks! enabled, Flipper will not work and # you should disable these next few lines. use_flipper! post_install do |installer| flipper_post_install(installer) installer.pods_project.targets.each do |target| target.build_configurations.each do |config| config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'NO' end end end end
ShareviewController.h
#import <UIKit/UIKit.h> #import <React/RCTBridgeModule.h> @interface ShareViewController : UIViewController<RCTBridgeModule> @end
ShareviewController.m
#import "ShareViewController.h" #import <React/RCTRootView.h> #import <React/RCTBundleURLProvider.h> #import <MobileCoreServices/MobileCoreServices.h> #define URL_IDENTIFIER (NSString *)kUTTypeURL NSExtensionContext* extensionContext; @implementation ShareViewController { } RCT_EXPORT_MODULE(); - (void) viewDidLoad { [super viewDidLoad]; extensionContext = self.extensionContext; // global for later call to async promise // set up react native instance NSURL *jsCodeLocation; jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL: jsCodeLocation moduleName: @"Share" initialProperties: nil launchOptions: nil]; UIViewController *rootViewController = [UIViewController alloc]; rootViewController.view = rootView; [self addChildViewController: rootViewController]; rootViewController.view.frame = self.view.bounds; rootViewController.view.translatesAutoresizingMaskIntoConstraints = false; [[self view] addSubview:rootViewController.view]; NSArray* constraints = [NSArray arrayWithObjects: [rootViewController.view.leftAnchor constraintEqualToAnchor: self.view.leftAnchor], [rootViewController.view.rightAnchor constraintEqualToAnchor: self.view.rightAnchor], [rootViewController.view.topAnchor constraintEqualToAnchor: self.view.topAnchor], [rootViewController.view.bottomAnchor constraintEqualToAnchor: self.view.bottomAnchor], nil ]; [NSLayoutConstraint activateConstraints:constraints]; [self didMoveToParentViewController: self]; } RCT_REMAP_METHOD(getParameters, resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { [self extractData: extensionContext withCallback:^(NSDictionary* result, NSException* err) { if(err) { reject(@"error", err.description, nil); } else { resolve(result); } }]; } - (void) extractData: (NSExtensionContext *)context withCallback:(void(^)(NSDictionary* result, NSException *exception))callback { @try { // get items shared NSExtensionItem *item = [context.inputItems firstObject]; __block NSItemProvider *provider = item.attachments.firstObject; if([provider hasItemConformingToTypeIdentifier:URL_IDENTIFIER]) { [provider loadItemForTypeIdentifier:URL_IDENTIFIER options:nil completionHandler:^(id<NSSecureCoding> item, NSError *error) { NSURL *url = (NSURL *)item; NSDictionary *result = @{@"data": [url absoluteString], @"type": @"url"}; if(callback) { callback(result, nil); } }]; return; } if(callback) { callback(nil, [NSException exceptionWithName:@"Error" reason:@"couldn't find provider" userInfo:nil]); } } @catch (NSException *exception) { } } @end
Did you ever solve this?
Really appreciate the work that @jvandenaardweg went to over in issue #182 to try and help everybody out, but I thought I'd open this here to share a solution that doesn't need to modify build settings or fork any repos. I would suggest just writing your own share extension and using some skeleton code from this issue. You can still follow most of the install steps, but without importing this lib, and a few additional steps.
In X-Code, having opened up your project
UIViewController
and implementviewDidLoad
to load react nativeMyShareExtension.h
MyShareExtension.m
Conclusion
You still need to follow the install steps to restrict the types that your app can receive (The plist) and ensure the transport settings let you connect to the react native bundler server, but these were the most critical steps to getting my share extension working.