Compare commits

..
Author SHA1 Message Date
semantic-release-bot af22acc509 chore: 2.0.0
## @config-plugins/react-native-dynamic-app-icon [2.0.0](https://github.com/expo/config-plugins/compare/@config-plugins/[email protected]...@config-plugins/[email protected]) (2022-06-14)

### ⚠ BREAKING CHANGES

* add SDK 45 support (#79)

### Features

* add SDK 45 support ([#79](https://github.com/expo/config-plugins/issues/79)) ([96c72dd](https://github.com/expo/config-plugins/commit/96c72dda469ace2b9eafd38ba4d21f1bcd2e3cdf))

### Other chores

* publish packages [no-ci] ([0c7b413](https://github.com/expo/config-plugins/commit/0c7b413a765e4b1ff92b9e3edc2b62077c41ce46))
2022-06-14 12:21:20 +00:00
Evan Bacon 994fadc8dd chore(react-native-siri-shortcut): add tests (#88)
* add: tests for siri

* add: unit tests

* chore: update repo settings

* chore: noop android

* Update withReactNativeSiriShortcut.ts
2022-06-14 14:17:45 +02:00
22 changed files with 969 additions and 19 deletions

No files matched your search

+1
View File
@@ -42,6 +42,7 @@ body:
- '@config-plugins/react-native-google-cast'
- '@config-plugins/react-native-pdf'
- '@config-plugins/react-native-quick-actions'
- '@config-plugins/react-native-siri-shortcut'
- '@config-plugins/react-native-webrtc'
validations:
required: true
+3
View File
@@ -39,6 +39,9 @@ contact_links:
- about: A react-native interface for Touch 3D home screen quick actions
name: 📦 react-native-quick-actions issues
url: https://github.com/jordanbyron/react-native-quick-actions/issues
- about: A React Native library that enables you to use iOS 12+ Siri Shortcuts.
name: 📦 react-native-siri-shortcut issues
url: https://github.com/Gustash/react-native-siri-shortcut/issues
- about: WebRTC for React Native
name: 📦 react-native-webrtc issues
url: https://github.com/react-native-webrtc/react-native-webrtc/issues
+6
View File
@@ -83,6 +83,12 @@ updates:
interval: daily
time: '09:00'
timezone: America/Los_Angeles
- package-ecosystem: npm
directory: packages/react-native-siri-shortcut
schedule:
interval: daily
time: '09:00'
timezone: America/Los_Angeles
- package-ecosystem: npm
directory: packages/react-native-webrtc
schedule:
+1
View File
@@ -41,6 +41,7 @@ jobs:
react-native-ble-plx,
react-native-blob-util,
react-native-branch,
react-native-siri-shortcut,
react-native-google-cast,
react-native-pdf,
# react-native-quick-actions,
@@ -0,0 +1,80 @@
node_modules/**/*
.expo/*
npm-debug.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
*.orig.*
web-build/
# macOS
.DS_Store
/artifacts
/scripts/start.command
/ios
/android
# @generated expo-cli sync-e7dcf75f4e856f7b6f3239b3f3a7dd614ee755a8
# The following patterns were generated by expo-cli
# OSX
#
.DS_Store
# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
project.xcworkspace
# Android/IntelliJ
#
build/
.idea
.gradle
local.properties
*.iml
*.hprof
# node.js
#
node_modules/
npm-debug.log
yarn-error.log
# BUCK
buck-out/
\.buckd/
*.keystore
!debug.keystore
# Bundle artifacts
*.jsbundle
# CocoaPods
/ios/Pods/
# Expo
.expo/
web-build/
dist/
# @end expo-cli
Whitespace-only changes.
@@ -0,0 +1,16 @@
const path = require("path");
const folderName = path.basename(__dirname);
const cleanName = folderName.replace(/[_\s-]/g, "");
const appId = "dev.bacon." + cleanName;
module.exports = ({ config }) => {
if (!config.extra) config.extra = {};
// Expose CI env variables to the app
config.extra.CI = process.env.CI;
if (!config.ios) config.ios = {};
config.ios.bundleIdentifier = appId;
if (!config.android) config.android = {};
config.android.package = appId;
return config;
};
+14
View File
@@ -0,0 +1,14 @@
{
"expo": {
"icon": "https://icogen.vercel.app/api/icon",
"plugins": [
[
"@config-plugins/react-native-siri-shortcut",
[
"com.example.InitiateWorkout",
"com.example.FinishWorkout"
]
]
]
}
}
@@ -0,0 +1,6 @@
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
};
};
+5
View File
@@ -0,0 +1,5 @@
import { registerRootComponent } from "expo";
import App from "./src/App";
registerRootComponent(App);
@@ -0,0 +1,6 @@
// Learn more https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require("@expo/metro-config");
const config = getDefaultConfig(__dirname);
module.exports = config;
@@ -0,0 +1,25 @@
{
"name": "@config-plugins-app/react-native-siri-shortcut",
"main": "./index.js",
"version": "1.0.0",
"scripts": {
"start": "expo start --dev-client",
"ios": "expo run:ios",
"android": "expo run:android"
},
"dependencies": {
"@config-plugins/react-native-siri-shortcut": "*",
"expo": "^45.0.0",
"expo-splash-screen": "~0.15.1",
"react": "17.0.2",
"react-native": "0.68.2",
"react-native-siri-shortcut": "^3.1.0",
"expo-status-bar": "~1.3.0",
"react-dom": "17.0.2",
"react-native-web": "0.17.7"
},
"devDependencies": {
"@babel/core": "^7.12.9"
},
"private": true
}
@@ -0,0 +1,264 @@
import React, { Component } from 'react';
import {
StyleSheet,
Text,
ScrollView,
Button,
SafeAreaView,
View,
EmitterSubscription,
} from 'react-native';
import {
ShortcutOptions,
ShortcutData,
ShortcutInfo,
donateShortcut,
suggestShortcuts,
clearAllShortcuts,
clearShortcutsWithIdentifiers,
presentShortcut,
getShortcuts,
addShortcutListener,
getInitialShortcut,
AddToSiriButton,
SiriButtonStyles,
} from 'react-native-siri-shortcut';
const opts1: ShortcutOptions = {
activityType: 'com.github.gustash.SiriShortcutsModuleExample.sayHello',
title: 'Say Hi',
userInfo: {
foo: 1,
bar: 'baz',
baz: 34.5,
},
requiredUserInfoKeys: ['foo', 'bar', 'baz'],
keywords: ['kek', 'foo', 'bar'],
persistentIdentifier:
'com.github.gustash.SiriShortcutsModuleExample.sayHello',
isEligibleForSearch: true,
isEligibleForPrediction: true,
suggestedInvocationPhrase: 'Say something',
needsSave: true,
};
const opts2: ShortcutOptions = {
activityType: 'com.github.gustash.SiriShortcutsModuleExample.somethingElse',
title: 'Something Else',
persistentIdentifier: 'some.persistent.identifier',
isEligibleForSearch: true,
isEligibleForPrediction: true,
suggestedInvocationPhrase: "What's up?",
description: 'Just a random description',
};
type State = {
shortcutInfo: any | null,
shortcutActivityType: string | null,
addToSiriStyle: 0 | 1 | 2 | 3 | 4 | 5,
shortcuts: Array<ShortcutData>,
};
export default class App extends Component<void, State> {
listener: EmitterSubscription | null = null;
state: State = {
shortcutInfo: null,
shortcutActivityType: null,
addToSiriStyle: SiriButtonStyles.blackOutline,
shortcuts: [],
};
componentDidMount() {
this.handleInitialShortcut();
this.listener = addShortcutListener(this.handleSiriShortcut.bind(this));
// This will suggest these two shortcuts so that they appear
// in Settings > Siri & Search, even if they are not yet
// donated. Suitable for shortcuts that you expect the user
// may want to use. (https://developer.apple.com/documentation/sirikit/shortcut_management/suggesting_shortcuts_to_users)
suggestShortcuts([opts1, opts2]);
this.updateShortcutList();
}
componentWillUnmount() {
if (this.listener) {
this.listener.remove();
}
}
async handleInitialShortcut() {
const initialShortcut = await getInitialShortcut();
if (!initialShortcut) {
return;
}
this.handleSiriShortcut(initialShortcut);
}
handleSiriShortcut({ userInfo, activityType }: ShortcutInfo) {
this.setState({
shortcutInfo: userInfo,
shortcutActivityType: activityType,
});
}
setupShortcut1() {
donateShortcut(opts1);
}
setupShortcut2() {
donateShortcut(opts2);
}
async clearShortcut1() {
try {
await clearShortcutsWithIdentifiers([
'com.github.gustash.SiriShortcutsModuleExample.sayHello',
]);
alert('Cleared Shortcut 1');
} catch (e) {
alert("You're not running iOS 12!");
}
}
async clearShortcut2() {
try {
await clearShortcutsWithIdentifiers(['some.persistent.identifier']);
alert('Cleared Shortcut 2');
} catch (e) {
alert("You're not running iOS 12!");
}
}
async updateShortcutList() {
try {
const shortcuts = await getShortcuts();
this.setState({
shortcuts,
});
} catch (e) {
alert("You're not running iOS 12!");
}
}
async clearBothShortcuts() {
try {
await clearShortcutsWithIdentifiers([
'com.github.gustash.SiriShortcutsModuleExample.sayHello',
'some.persistent.identifier',
]);
alert('Cleared Both Shortcuts');
} catch (e) {
alert("You're not running iOS 12!");
}
}
async clearShortcuts() {
try {
await clearAllShortcuts();
alert('Deleted all the shortcuts');
} catch (e) {
alert("You're not running iOS 12!");
}
}
swapSiriButtonTheme() {
const { addToSiriStyle } = this.state;
const styles = Object.keys(SiriButtonStyles).map(
key => SiriButtonStyles[key],
);
const index = styles.findIndex(style => style === addToSiriStyle);
if (index === styles.length - 1) this.setState({ addToSiriStyle: styles[0] });
else this.setState({ addToSiriStyle: styles[index + 1] });
}
render() {
const { shortcutInfo, shortcutActivityType, addToSiriStyle, shortcuts } =
this.state;
return (
<SafeAreaView style={styles.container}>
<ScrollView
style={styles.container}
contentContainerStyle={styles.contentContainer}>
<Text>Shortcut Activity Type: {shortcutActivityType || 'None'}</Text>
<Text>
Shortcut Info:{' '}
{shortcutInfo ? JSON.stringify(shortcutInfo) : 'No shortcut data.'}
</Text>
<Button
title="Create Shortcut 1"
onPress={this.setupShortcut1.bind(this)}
/>
<Button
title="Create Shortcut 2"
onPress={this.setupShortcut2.bind(this)}
/>
<Button
title="Clear Shortcut 1"
onPress={this.clearShortcut1.bind(this)}
/>
<Button
title="Clear Shortcut 2"
onPress={this.clearShortcut2.bind(this)}
/>
<Button
title="Clear Both Shortcuts"
onPress={this.clearBothShortcuts.bind(this)}
/>
<Button
title="Delete All Shortcuts"
onPress={this.clearShortcuts.bind(this)}
/>
<Button
title="Update list of shortcuts"
onPress={this.updateShortcutList.bind(this)}
/>
<AddToSiriButton
buttonStyle={addToSiriStyle}
onPress={() => {
presentShortcut(opts1, ({ status }) => {
console.log(`I was ${status}`);
});
}}
shortcut={opts1}
/>
<Button
title="Swap Siri Button Theme"
onPress={this.swapSiriButtonTheme.bind(this)}
/>
{shortcuts.length ? (
shortcuts.map(({ identifier, phrase, options }, i) => (
<View key={identifier}>
<Text>Shortcut {i + 1}:</Text>
<Text>Identifier - {identifier}</Text>
<Text>Phrase - {phrase}</Text>
<Text>Options - {JSON.stringify(options)}</Text>
</View>
))
) : (
<Text>No Shortcuts yet</Text>
)}
</ScrollView>
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
contentContainer: {
justifyContent: 'center',
alignItems: 'center',
},
});
@@ -0,0 +1,6 @@
import React from 'react';
import { View, Text } from 'react-native';
export default function App() {
return (<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}><Text>No-op on this platform</Text></View>)
}
@@ -1,3 +1,19 @@
## @config-plugins/react-native-dynamic-app-icon [2.0.0](https://github.com/expo/config-plugins/compare/@config-plugins/[email protected]...@config-plugins/[email protected]) (2022-06-14)
### ⚠ BREAKING CHANGES
* add SDK 45 support (#79)
### Features
* add SDK 45 support ([#79](https://github.com/expo/config-plugins/issues/79)) ([96c72dd](https://github.com/expo/config-plugins/commit/96c72dda469ace2b9eafd38ba4d21f1bcd2e3cdf))
### Other chores
* publish packages [no-ci] ([0c7b413](https://github.com/expo/config-plugins/commit/0c7b413a765e4b1ff92b9e3edc2b62077c41ce46))
### @config-plugins/react-native-dynamic-app-icon [1.0.3](https://github.com/expo/config-plugins/compare/@config-plugins/[email protected]...@config-plugins/[email protected]) (2022-05-12)
@@ -0,0 +1 @@
module.exports = require('expo-module-scripts/jest-preset-plugin');
@@ -35,5 +35,6 @@
},
"devDependencies": {
"expo-module-scripts": "^2.0.0"
}
},
"upstreamPackage": "react-native-siri-shortcut"
}
@@ -0,0 +1,306 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`addSiriShortcutAppDelegateImport adds import to Expo Modules AppDelegate 1`] = `
"
#import \\"AppDelegate.h\\"
// @generated begin react-native-siri-shortcut - expo prebuild (DO NOT MODIFY) sync-34828decdfd158f5b96fe11a1e590a684e296e0e
#import <RNSiriShortcuts/RNSiriShortcuts.h>
// @generated end react-native-siri-shortcut
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <React/RCTLinkingManager.h>
#import <React/RCTConvert.h>
#import <React/RCTAppSetupUtils.h>
#if RCT_NEW_ARCH_ENABLED
#import <React/CoreModulesPlugins.h>
#import <React/RCTCxxBridgeDelegate.h>
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
#import <React/RCTSurfacePresenter.h>
#import <React/RCTSurfacePresenterBridgeAdapter.h>
#import <ReactCommon/RCTTurboModuleManager.h>
#import <react/config/ReactNativeConfig.h>
@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
RCTTurboModuleManager *_turboModuleManager;
RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
facebook::react::ContextContainer::Shared _contextContainer;
}
@end
#endif
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RCTAppSetupPrepareApp(application);
RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions];
#if RCT_NEW_ARCH_ENABLED
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
_contextContainer->insert(\\"ReactNativeConfig\\", _reactNativeConfig);
_bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
#endif
UIView *rootView = [self.reactDelegate createRootViewWithBridge:bridge moduleName:@\\"main\\" initialProperties:nil];
rootView.backgroundColor = [UIColor whiteColor];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [self.reactDelegate createRootViewController];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
[super application:application didFinishLaunchingWithOptions:launchOptions];
return YES;
}
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge
{
// If you'd like to export some custom RCTBridgeModules, add them here!
return @[];
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@\\"index\\"];
#else
return [[NSBundle mainBundle] URLForResource:@\\"main\\" withExtension:@\\"jsbundle\\"];
#endif
}
// Linking API
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options];
}
// Universal Links
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
BOOL result = [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || result;
}
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
return [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
return [super application:application didFailToRegisterForRemoteNotificationsWithError:error];
}
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
#if RCT_NEW_ARCH_ENABLED
#pragma mark - RCTCxxBridgeDelegate
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
{
_turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
delegate:self
jsInvoker:bridge.jsCallInvoker];
return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
}
#pragma mark RCTTurboModuleManagerDelegate
- (Class)getModuleClassFromName:(const char *)name
{
return RCTCoreModulesClassProvider(name);
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
{
return nullptr;
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
initParams:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return nullptr;
}
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
{
return RCTAppSetupDefaultModuleFromClass(moduleClass);
}
#endif
@end
"
`;
exports[`addSiriShortcutAppDelegateInit adds init to Expo Modules AppDelegate 1`] = `
"
#import \\"AppDelegate.h\\"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <React/RCTLinkingManager.h>
#import <React/RCTConvert.h>
#import <React/RCTAppSetupUtils.h>
#if RCT_NEW_ARCH_ENABLED
#import <React/CoreModulesPlugins.h>
#import <React/RCTCxxBridgeDelegate.h>
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
#import <React/RCTSurfacePresenter.h>
#import <React/RCTSurfacePresenterBridgeAdapter.h>
#import <ReactCommon/RCTTurboModuleManager.h>
#import <react/config/ReactNativeConfig.h>
@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
RCTTurboModuleManager *_turboModuleManager;
RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
facebook::react::ContextContainer::Shared _contextContainer;
}
@end
#endif
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RCTAppSetupPrepareApp(application);
RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions];
#if RCT_NEW_ARCH_ENABLED
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
_contextContainer->insert(\\"ReactNativeConfig\\", _reactNativeConfig);
_bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
#endif
UIView *rootView = [self.reactDelegate createRootViewWithBridge:bridge moduleName:@\\"main\\" initialProperties:nil];
rootView.backgroundColor = [UIColor whiteColor];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [self.reactDelegate createRootViewController];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
[super application:application didFinishLaunchingWithOptions:launchOptions];
return YES;
}
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge
{
// If you'd like to export some custom RCTBridgeModules, add them here!
return @[];
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@\\"index\\"];
#else
return [[NSBundle mainBundle] URLForResource:@\\"main\\" withExtension:@\\"jsbundle\\"];
#endif
}
// Linking API
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options];
}
// Universal Links
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
// @generated begin react-native-siri-shortcut-delegate - expo prebuild (DO NOT MODIFY) sync-291450a04ff243636b0bd0aef4a614f7218690b5
BOOL shortcutResult = [RNSSSiriShortcuts application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
if (shortcutResult) {
return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || shortcutResult;
}
// @generated end react-native-siri-shortcut-delegate
BOOL result = [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || result;
}
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
return [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
return [super application:application didFailToRegisterForRemoteNotificationsWithError:error];
}
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
#if RCT_NEW_ARCH_ENABLED
#pragma mark - RCTCxxBridgeDelegate
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
{
_turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
delegate:self
jsInvoker:bridge.jsCallInvoker];
return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
}
#pragma mark RCTTurboModuleManagerDelegate
- (Class)getModuleClassFromName:(const char *)name
{
return RCTCoreModulesClassProvider(name);
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
{
return nullptr;
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
initParams:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return nullptr;
}
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
{
return RCTAppSetupDefaultModuleFromClass(moduleClass);
}
#endif
@end
"
`;
@@ -0,0 +1,146 @@
// SDK +45: https://github.com/expo/expo/blob/sdk-45/templates/expo-template-bare-minimum/ios/HelloWorld/AppDelegate.m
export const ExpoModulesAppDelegate = `
#import "AppDelegate.h"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <React/RCTLinkingManager.h>
#import <React/RCTConvert.h>
#import <React/RCTAppSetupUtils.h>
#if RCT_NEW_ARCH_ENABLED
#import <React/CoreModulesPlugins.h>
#import <React/RCTCxxBridgeDelegate.h>
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
#import <React/RCTSurfacePresenter.h>
#import <React/RCTSurfacePresenterBridgeAdapter.h>
#import <ReactCommon/RCTTurboModuleManager.h>
#import <react/config/ReactNativeConfig.h>
@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
RCTTurboModuleManager *_turboModuleManager;
RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
facebook::react::ContextContainer::Shared _contextContainer;
}
@end
#endif
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RCTAppSetupPrepareApp(application);
RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions];
#if RCT_NEW_ARCH_ENABLED
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
_bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
#endif
UIView *rootView = [self.reactDelegate createRootViewWithBridge:bridge moduleName:@"main" initialProperties:nil];
rootView.backgroundColor = [UIColor whiteColor];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [self.reactDelegate createRootViewController];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
[super application:application didFinishLaunchingWithOptions:launchOptions];
return YES;
}
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge
{
// If you'd like to export some custom RCTBridgeModules, add them here!
return @[];
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}
// Linking API
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options];
}
// Universal Links
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
BOOL result = [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || result;
}
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
return [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
return [super application:application didFailToRegisterForRemoteNotificationsWithError:error];
}
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
#if RCT_NEW_ARCH_ENABLED
#pragma mark - RCTCxxBridgeDelegate
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
{
_turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
delegate:self
jsInvoker:bridge.jsCallInvoker];
return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
}
#pragma mark RCTTurboModuleManagerDelegate
- (Class)getModuleClassFromName:(const char *)name
{
return RCTCoreModulesClassProvider(name);
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
{
return nullptr;
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
initParams:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return nullptr;
}
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
{
return RCTAppSetupDefaultModuleFromClass(moduleClass);
}
#endif
@end
`;
@@ -0,0 +1,42 @@
import {
addSiriShortcutAppDelegateImport,
addSiriShortcutAppDelegateInit,
} from "../withReactNativeSiriShortcut";
import { ExpoModulesAppDelegate } from "./fixtures/AppDelegate";
describe(addSiriShortcutAppDelegateImport, () => {
it(`adds import to Expo Modules AppDelegate`, () => {
const results = addSiriShortcutAppDelegateImport(ExpoModulesAppDelegate);
// matches a static snapshot
expect(results.contents).toMatchSnapshot();
expect(results.contents).toMatch(/react-native-siri-shortcut/);
expect(results.contents).toMatch(/RNSiriShortcuts\/RNSiriShortcuts\.h/);
// did add new content
expect(results.didMerge).toBe(true);
// didn't remove old content
expect(results.didClear).toBe(false);
});
it(`fails to add to a malformed app delegate`, () => {
expect(() => addSiriShortcutAppDelegateImport(`foobar`)).toThrow(/foobar/);
});
});
describe(addSiriShortcutAppDelegateInit, () => {
it(`adds init to Expo Modules AppDelegate`, () => {
const results = addSiriShortcutAppDelegateInit(ExpoModulesAppDelegate);
// matches a static snapshot
expect(results.contents).toMatchSnapshot();
expect(results.contents).toMatch(/react-native-siri-shortcut-delegate/);
expect(results.contents).toMatch(
/RNSSSiriShortcuts application:application/
);
// did add new content
expect(results.didMerge).toBe(true);
// didn't remove old content
expect(results.didClear).toBe(false);
});
it(`fails to add to a malformed app delegate`, () => {
expect(() => addSiriShortcutAppDelegateInit(`foobar`)).toThrow(/foobar/);
});
});
@@ -17,16 +17,23 @@ const withReactNativeSiriShortcut: ConfigPlugin<void | string[]> = (
config,
activityTypes
) => {
config = withSiriShortcutAppDelegate(config);
withSiriShortcutAppDelegate(config);
withSiriEntitlements(config);
const items = activityTypes || [];
if (!Array.isArray(items) || !items.length) {
return config;
}
return withReactNativeSiriShortcutInfoPlist(config, items);
};
const withReactNativeSiriShortcutInfoPlist: ConfigPlugin<string[]> = (
config,
activityTypes
) => {
return withInfoPlist(config, (config) => {
config.modResults.NSUserActivityTypes = items;
config.modResults.NSUserActivityTypes = activityTypes;
return config;
});
};
@@ -43,25 +50,23 @@ export function addSiriShortcutAppDelegateImport(src: string): MergeResults {
}
export function addSiriShortcutAppDelegateInit(src: string): MergeResults {
const newSrc = [];
newSrc.push(
" BOOL shortcutResult = [RNSSSiriShortcuts application:application continueUserActivity:userActivity restorationHandler:restorationHandler];",
" if (shortcutResult) {",
" return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || shortcutResult;",
" }"
);
console.log(src);
return mergeContents({
tag: "react-native-siri-shortcut-delegate",
src,
newSrc: newSrc.join("\n"),
newSrc: [
" BOOL shortcutResult = [RNSSSiriShortcuts application:application continueUserActivity:userActivity restorationHandler:restorationHandler];",
" if (shortcutResult) {",
" return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || shortcutResult;",
" }",
].join("\n"),
anchor:
/ return \[super application:application continueUserActivity:userActivity restorationHandler:restorationHandler\] \|\| result;/,
offset: -1,
comment: "//",
});
}
/** Append the siri entitlement on iOS */
const withSiriEntitlements: ConfigPlugin = (config) => {
return withEntitlementsPlist(config, (config) => {
config.modResults["com.apple.developer.siri"] = true;
@@ -70,7 +75,7 @@ const withSiriEntitlements: ConfigPlugin = (config) => {
};
const withSiriShortcutAppDelegate: ConfigPlugin = (config) => {
let cfg = withAppDelegate(config, (config) => {
return withAppDelegate(config, (config) => {
if (["objc", "objcpp"].includes(config.modResults.language)) {
try {
config.modResults.contents = addSiriShortcutAppDelegateImport(
@@ -94,9 +99,6 @@ const withSiriShortcutAppDelegate: ConfigPlugin = (config) => {
}
return config;
});
cfg = withSiriEntitlements(cfg);
return cfg;
};
const pkg = {
+5 -2
View File
@@ -16941,6 +16941,11 @@ [email protected]:
resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-4.2.4.tgz#4df42819759c4d3c74252c8678c2772cfa2271a6"
integrity sha512-OOX+W2G4YYufvryonn6Kw6YnyT8ZThkxPHZBD04NLHaZmicUaaDVII/PZ3M5fD1o5N62+T+8K4bCS5Un2ggvkA==
react-native-siri-shortcut@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/react-native-siri-shortcut/-/react-native-siri-shortcut-3.1.0.tgz#920adf41eb6c72c93ef8571259c8d35358e50e71"
integrity sha512-MM0f7HS3ADU2gUh0Q6n935uhdfA3KgjpN8hEEhf2RSf4azcRNh8zNMwZD08pGSrP9/JYj1kYndQ0oytpwPyk/A==
[email protected]:
version "0.17.7"
resolved "https://registry.yarnpkg.com/react-native-web/-/react-native-web-0.17.7.tgz#038899dbc94467a0ca0be214b88a30e0c117b176"
@@ -20118,10 +20123,8 @@ watchpack@^1.6.1:
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453"
integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==
dependencies:
chokidar "^3.4.1"
graceful-fs "^4.1.2"
neo-async "^2.5.0"
watchpack-chokidar2 "^2.0.1"
optionalDependencies:
chokidar "^3.4.1"
watchpack-chokidar2 "^2.0.1"