npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@bacons/apple-targets

v0.0.10

Published

Generate Apple Targets with Expo Prebuild

Downloads

6,285

Readme

Apple Targets plugin

[!WARNING] This is highly experimental and not part of any official Expo workflow.

An experimental Expo Config Plugin that generates native Apple Targets like Widgets or App Clips, and links them outside the /ios directory. You can open Xcode and develop the targets inside the virtual expo:targets folder and the changes will be saved outside of the ios directory. This pattern enables building things that fall outside of the scope of React Native while still obtaining all the benefits of Continuous Native Generation.

🚀 How to use

  • Add targets to targets/ directory with an expo-target.config.json file.
  • Currently, if you don't have an Info.plist, it'll be generated on npx expo prebuild. This may be changed in the future so if you have an Info.plist it'll be used, otherwise, it'll be generated.
  • Any files in a top-level target/*/assets directory will be linked as resources of the target. This was added to support Safari Extensions.
  • A single top-level *.entitlements file will be linked as the entitlements of the target. This is not currently used in EAS Capability signing, but may be in the future.
  • All top-level swift files will be linked as build sources of the target. There is currently no support for storyboard or .xib files because I can't be bothered.
  • All top-level *.xcassets will be linked as resources, and accessible in the targets. If you add files outside of Xcode, you'll need to re-run npx expo prebuild to link them.
  • In Expo SDK +52, set the ios.appleTeamId, for SDK 51 and below, set the appleTeamId prop in the Config Plugin in app.config.js:
{
  "plugins": [
    [
      "@bacons/apple-targets",
      {
        "appleTeamId": "XXXXXXXXXX"
      }
    ]
  ]
}

You can change the root directory from ./targets to something else with root: "./src/targets". Avoid doing this.

Using React Native in Targets

I'm not sure, that's not the purpose of this plugin. I built this so I could easily build iOS widgets and other minor targets with SwiftUI. I imagine it would be straightforward to use React Native in share, notification, iMessage, Safari, and photo editing extensions, you can build that on top of this plugin if you want.

expo-target.config.json

This file can have the following properties:

{
  "type": "widget",

  // Name of the target/product. Defaults to the directory name.
  "name": "My Widget",

  // Generates colorset files for the target.
  "colors": {
    // or "$accent": "red",
    "$accent": { "color": "red", "darkColor": "blue" }
  },
  "icon": "../assets/icon.png",
  // Can also be a URL
  "frameworks": [
    // Frameworks without the extension, these will be added to the target.
    "SwiftUI"
  ],
  "entitlements": {
    // Serialized entitlements. Useful for configuring with environment variables.
  },
  // Generates xcassets for the target.
  "images": {
    "thing": "../assets/thing.png"
  },

  // The iOS version fot the target.
  "deploymentTarget": "13.4"
}

You can also use .js with the typedoc for autocomplete:

/** @type {import('@bacons/apple-targets').Config} */
module.exports = {
  type: "watch",
  colors: {
    $accent: "steelblue",
  },
  deploymentTarget: "9.4",
};

Colors

There are certain values that are shared across targets. We use a predefined convention to map these values across targets.

| Name | Build Setting | Purpose | | ------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | | $accent | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME | Sets the global accent color, in widgets this is used for the tint color of buttons when editing the widget. | | $widgetBackground | ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME | Sets the background color of the widget. |

CocoaPods

Adding a file pods.rb in the root of the repo will enable you to modify the target settings for the project.

The ruby module evaluates with global access to the property podfile_properties.

For example, the following is useful for enabling React Native in an App Clip target:

require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")

exclude = []
use_expo_modules!(exclude: exclude)

if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1'
  config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"];
else
  config_command = [
    'node',
    '--no-warnings',
    '--eval',
    'require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'expo/package.json\')] }))(process.argv.slice(1))',
    'react-native-config',
    '--json',
    '--platform',
    'ios'
  ]
end

config = use_native_modules!(config_command)

use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']
use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']

use_react_native!(
  :path => config[:reactNativePath],
  :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes',
  # An absolute path to your application root.
  :app_path => "#{Pod::Config.instance.installation_root}/..",
  :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false',
)

This block executes at the end of the Podfile in a block like:

target "target_dir_name" do
   target_file
end

The name of the target must match the name of the target directory.

Examples

widget

I wrote a blog about this one and used it in production. Learn more: Expo x Apple Widgets.

/** @type {import('@bacons/apple-targets').Config} */
module.exports = {
  type: "widget",
  icon: "../../icons/widget.png",
  colors: {
    // This color is referenced in the Info.plist
    $widgetBackground: "#DB739C",

    $accent: "#F09458",

    // Optional: Add colors that can be used in SwiftUI.
    gradient1: {
      light: "#E4975D",
      dark: "#3E72A0",
    },
  },
  // Optional: Add images that can be used in SwiftUI.
  images: {
    valleys: "../../valleys.png",
  },
  // Optional: Add entitlements to the target, this one can be used to share data between the widget and the app.
  entitlements: {
    "com.apple.security.application-groups": ["group.bacon.data"],
  },
};

action

These show up in the share sheet. The icon should be transparent as it will be masked by the system.

/** @type {import('@bacons/apple-targets').Config} */
module.exports = {
  type: "action",
  name: "Inspect Element",
  icon: "./assets/icon.png",
  colors: {
    TouchBarBezel: "#DB739C",
  },
};

Add a JavaScript file to assets/index.js:

class Action {
  /**
   * `extensionName: "com.bacon.2095.axun"`
   * @param {*} arguments: {completionFunction: () => unknown; extensionName: string; }
   */
  run({ extensionName, completionFunction }) {
    // Here, you can run code that modifies the document and/or prepares
    // things to pass to your action's native code.

    // We will not modify anything, but will pass the body's background
    // style to the native code.
    completionFunction({
      /* */
    });
  }

  finalize() {
    // Runs after the native action code has completed.
  }
}

window.ExtensionPreprocessingJS = new Action();

Ensure NSExtensionJavaScriptPreprocessingFile: "index" in the Info.plist.

spotlight

Populate the Spotlight search results with your app's content.

/** @type {import('@bacons/apple-targets').Config} */
module.exports = {
  type: "spotlight",
};

Supported types

Ideally, this would be generated automatically based on a fully qualified Xcode project, but for now it's a manual process. The currently supported types are based on static analysis of the most commonly used targets in the iOS App Store. I haven't tested all of these and they may not work.

| Type | Description | | -------------------- | ---------------------------------- | | action | Share Action | | widget | Widget / Live Activity | | watch | Watch App (with companion iOS App) | | clip | App Clip | | safari | Safari Extension | | share | Share Extension | | notification-content | Notification Content Extension | | notification-service | Notification Service Extension | | intent | Siri Intent Extension | | intent-ui | Siri Intent UI Extension | | spotlight | Spotlight Index Extension | | bg-download | Background Download Extension | | quicklook-thumbnail | Quick Look Thumbnail Extension | | location-push | Location Push Service Extension | | credentials-provider | Credentials Provider Extension | | account-auth | Account Authentication Extension |

Code Signing

The codesigning is theoretically handled entirely by EAS Build. This plugin will add the requisite entitlements for target signing to work. I've only tested this end-to-end with my Pillar Valley Widget.

You can also manually sign all sub-targets if you want, I'll light a candle for you.

Xcode parsing

This plugin makes use of my proprietary Xcode parsing library, @bacons/xcode. It's mostly typed, very untested, and possibly full of bugs––however, it's still 10x nicer than the alternative.