expo-share-extension

repository·main·Indexed 19 days ago

https://github.com/maxast/expo-share-extension

An Expo config plugin for creating custom iOS share extensions. It supports sharing media types, Apple Sign-In, and React Native Firebase with shared auth sessions. The library provides utilities for managing the extension lifecycle via close(), openHostApp(), and clearAppGroupContainer() APIs, as well as configuration options for activation rules, background appearance, and JavaScript preprocessing for web content.

Tokens
7.5K
Snippets
27
Records
32
Agent score
65%

What's inside expo-share-extension

  1. Set up entry points for the main app and share extension

    main

    You must define two separate entry points: one for your main application and one specifically for the share extension bundle.

    1. Main App (index.js): Uses registerRootComponent to boot your main app.
    2. Share Extension (index.share.js): Uses AppRegistry.registerComponent with the exact component name "shareExtension" to register your extension's root component.
    // index.js (main app)
    import { registerRootComponent } from "expo";
    import App from "./App";
    registerRootComponent(App);
    
    // index.share.js (share extension)
    import { AppRegistry } from "react-native";
    import ShareExtension from "./ShareExtension";
    
    // IMPORTANT: the first argument to registerComponent, must be "shareExtension"
    AppRegistry.registerComponent("shareExtension", () => ShareExtension);
  2. Configure Metro with withShareExtension

    main

    Wrap your Metro configuration with withShareExtension from expo-share-extension/metro to support the extension's bundle. If you don't have a metro.config.js, generate one first using npx expo customize metro.config.js.

    // metro.config.js
    const { getDefaultConfig } = require("expo/metro-config");
    const { withShareExtension } = require("expo-share-extension/metro");
    
    module.exports = withShareExtension(getDefaultConfig(__dirname), {
      // [Web-only]: Enables CSS support in Metro.
      isCSSEnabled: true,
    });
  3. Use JavaScript preprocessing for web content

    main

    You can use a JavaScript file to preprocess a webpage before the share extension is activated. This is useful for extracting data like the page title or URL.

    Important Constraints:

    1. Enabling this option sets NSExtensionActivationSupportsWebPageWithMaxCount: 1, which is mutually exclusive with the default NSExtensionActivationSupportsWebURLWithMaxCount: 1.
    2. As a result, you will no longer receive url in the initial props. To get the URL, you must extract it in your preprocessing file using window.location.href and pass it to the completionFunction.

    Implementation Requirements:

    • Create a class with a run(args) method.
    • The run method must call args.completionFunction(data) with the results you want to pass to the extension.
    • You must instantiate the class using var so it is globally accessible (e.g., var ExtensionPreprocessingJS = new ...).
    class ShareExtensionPreprocessor {
      run(args) {
        args.completionFunction({
          url: window.location.href,
          title: document.title,
        });
      }
    }
    
    var ExtensionPreprocessingJS = new ShareExtensionPreprocessor();
  4. Configure expo-share-extension in app.json

    main

    To enable the plugin, add expo-share-extension to your plugins array in app.json or app.config.js.

    Note on Font Scaling: The default React Native Text and TextInput components may have font scaling issues in the share extension. To fix this, either set allowFontScaling={false} or import the components directly from expo-share-extension.

    "expo": {
      ...
      "plugins": ["expo-share-extension"],
      ...
    }
  5. Run the basic example project

    main

    To run the provided basic example, follow these steps:

    1. Run Prebuild: Generate the native project files.
    2. Start the app: Use the Expo CLI to launch the app on iOS, or start the Metro server and build manually using Xcode.
    # 1. Run Prebuild
    npm run prebuild
    
    # 2. Start the app via Expo CLI
    npm run ios
    
    # OR: Start Metro server and build via Xcode
    npm run start
  6. Configure expo-share-extension via app.json

    main

    You can customize the behavior and appearance of the share extension by passing an options object to the expo-share-extension config plugin in your app.json or app.config.(j|t)s file.

    Exclude Expo Modules

    To reduce the share extension's bundle size, you can manually exclude unneeded Expo modules using the excludedPackages array. Note that runtime/dev modules like expo-updates and expo-dev-client are excluded by default in newer versions, but should be manually excluded in older versions.

    Custom Background Color

    Set the background color using an object with red, green, blue, and alpha keys. An alpha of 0 makes the background transparent.

    Custom Height

    Set the height of the share extension in pixels using the height key.

    React Native Firebase

    Because the share extension is a separate iOS target with its own bundle ID (e.g., com.example.app.ShareExtension), you must provide a dedicated GoogleService-Info.plist via the googleServicesFile key.

    [
      "expo-share-extension",
      {
        "excludedPackages": ["expo-dev-client", "expo-font"],
        "backgroundColor": {
          "red": 255,
          "green": 255,
          "blue": 255,
          "alpha": 0.8
        },
        "height": 500,
        "googleServicesFile": "./path-to-your-separate/GoogleService-Info.plist"
      }
    ]
  7. Configure App Group identifiers

    main

    By default, the App Group is set to your bundle identifier with a group. prefix (e.g., group.com.example.app). If you need to override this, you can set the AppGroup or AppGroupIdentifier keys within the ios.infoPlist section of your Expo configuration. The AppGroup key takes priority.

    {
      "expo": {
        "ios": {
          "infoPlist": {
            "AppGroup": "group.com.example.app"
          }
        }
      }
    }
  8. Configure Activation Rules for iOS

    main

    You must specify which types of content (files, images, videos, text, or URLs) your extension should respond to by defining activationRules in your app.json or app.config.(j|t)s plugin configuration.

    If no rules are specified, url and text are enabled by default. If you use image or video types, you must also include the NSPrivacyAccessedAPICategoryFileTimestamp privacy manifest entry in your app.json.

    [
      "expo-share-extension",
      {
        "activationRules": [
          {
            "type": "file",
            "max": 3
          },
          {
            "type": "image",
            "max": 2
          },
          {
            "type": "video",
            "max": 1
          },
          {
            "type": "text"
          },
          {
            "type": "url",
            "max": 1
          }
        ]
      }
    ]
  9. Customize the native view appearance via app.json

    main

    You can customize the look of the native share extension view by providing backgroundColor and height options in your app.json configuration under the expo-share-extension plugin key.

    To make the background transparent, set the alpha value in the backgroundColor object to 0.

    [
      "expo-share-extension",
      {
        "backgroundColor": {
          "red": 255,
          "green": 255,
          "blue": 255,
          "alpha": 0
        },
        "height": 500
      }
    ]
  10. How activation rules work in the Share Extension

    main

    Activation rules determine which content types (images, URLs, text, etc.) trigger your Share Extension in the iOS share sheet. The withShareExtensionInfoPlist plugin translates these rules into NSExtensionActivation keys in the extension's Info.plist.

    Key Behaviors

    • Automatic File Support: If you request image support but do not explicitly request file support, the plugin automatically adds a file rule with a max count equal to the image max count. This is done to ensure compatibility with screenshot overlays that send files instead of pure image objects.
    • URL vs WebPage:
      • If you provide a preprocessingFile, the plugin enables both NSExtensionActivationSupportsWebURLWithMaxCount and NSExtensionActivationSupportsWebPageWithMaxCount.
      • If no preprocessingFile is provided, only NSExtensionActivationSupportsWebURLWithMaxCount is enabled for url type rules.
    • Max Counts: For image, video, url, and file, you can specify a max property in your rule to limit how many items of that type can be shared at once.
  11. Configure the Expo Share Extension plugin

    main

    Use the withShareExtension Config Plugin to set up the iOS Share Extension in your Expo project. This plugin handles the necessary App Group entitlements, Info.plist configurations, Podfile modifications, and Xcode target creation.

    Configuration Options

    OptionTypeDescription
    activationRulesActivationRule[]Defines what types of content trigger the extension. Each rule includes a type and an optional max limit.
    backgroundColorBackgroundColorAn object { red, green, blue, alpha } where each value is a number between 0 and 255.
    heightHeightA number between 50 and 1000 representing the extension height.
    excludedPackagesstring[]A list of packages to exclude from the Podfile installation.
    googleServicesFilestringPath to the GoogleService file (e.g., for Firebase integration).
    preprocessingFilestringPath to a file used for preprocessing shared data.

    Activation Rule Types

    Available type values for activationRules:

    • "image"
    • "video"
    • "text"
    • "url"
    • "file"
    import { withShareExtension } from 'expo-share-extension';
    
    export default ({ config }) => {
      return withShareExtension(config, {
        activationRules: [
          { type: 'image', max: 5 },
          { type: 'url' }
        ],
        backgroundColor: { red: 255, green: 255, blue: 255, alpha: 255 },
        height: 500,
        excludedPackages: ['some-heavy-package'],
        googleServicesFile: './GoogleService-Info.plist',
      });
    };