Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Snapchat SnapKit config plugin #153

Open
ansh opened this issue Dec 10, 2022 · 5 comments
Open

Snapchat SnapKit config plugin #153

ansh opened this issue Dec 10, 2022 · 5 comments
Labels
enhancement New feature or request

Comments

@ansh
Copy link

ansh commented Dec 10, 2022

Library

https://www.npmjs.com/package/@snapchat/snap-kit-react-native

Summary

A Snapchat SnapKit config plugin would be great! Would allow us to use Snapchat's React Native package and it's forgo its complex setup process. (https://docs.snap.com/snap-kit/creative-kit/Tutorials/react-native#step-2-update-your-androidmanifestxml-file)

I already managed to get a config plugin sort of working. Can't figure out how to do step 3 (Step 3: Define Paths in Your res/xml/filre_paths.xml File) within the config plugin

Any existing examples?

import {
  AndroidConfig,
  createRunOncePlugin,
  IOSConfig,
  withXcodeProject,
  withAppBuildGradle,
  ConfigPlugin,
  withStringsXml,
  withProjectBuildGradle,
  withAndroidManifest,
} from "@expo/config-plugins";
import {
  createGeneratedHeaderComment,
  MergeResults,
  mergeContents,
  removeGeneratedContents,
} from "@expo/config-plugins/build/utils/generateCode";
import { ExpoConfig } from "expo/config";
const { addMetaDataItemToMainApplication, getMainApplicationOrThrow } = AndroidConfig.Manifest;

// Fork of config-plugins mergeContents, but appends the contents to the end of the file. Taken from https://github.com/expo/expo/blob/master/packages/expo-camera/plugin/src/withCamera.ts
function appendContents({
  src,
  newSrc,
  tag,
  comment,
}: {
  src: string;
  newSrc: string;
  tag: string;
  comment: string;
}): MergeResults {
  const header = createGeneratedHeaderComment(newSrc, tag, comment);
  if (!src.includes(header)) {
    // Ensure the old generated contents are removed.
    const sanitizedTarget = removeGeneratedContents(src, tag);
    const contentsToAdd = [
      // @something
      header,
      // contents
      newSrc,
      // @end
      `${comment} @generated end ${tag}`,
    ].join("\n");

    return {
      contents: sanitizedTarget ?? src + contentsToAdd,
      didMerge: true,
      didClear: !!sanitizedTarget,
    };
  }
  return { contents: src, didClear: false, didMerge: false };
}

const pkg = require("@snapchat/snap-kit-react-native/package.json");

const addSnapkitImport = (src: string): MergeResults => {
  return appendContents({
    tag: "expo-snapkit-import",
    src,
    newSrc: `allprojects { repositories { maven { url "https://storage.googleapis.com/snap-kit-build/maven" } } }`,
    comment: "//",
  });
};
const withSnapkitGradle: ConfigPlugin = (config) => {
  return withProjectBuildGradle(config, (config) => {
    if (config.modResults.language === "groovy") {
      config.modResults.contents = addSnapkitImport(config.modResults.contents).contents;
    } else {
      throw new Error(
        "Cannot add Snapkit maven gradle because the project build.gradle is not groovy"
      );
    }
    return config;
  });
};

async function addMetaDataToAndroidManifest(
  config: Pick<ExpoConfig, "android">,
  androidManifest: AndroidConfig.Manifest.AndroidManifest
): Promise<AndroidConfig.Manifest.AndroidManifest> {
  const name = "com.snapchat.kit.sdk.clientId";
  const value = "{{YOUR API KEY HERE}}";
  const mainApplication = getMainApplicationOrThrow(androidManifest); // Get the <application /> tag and assert if it doesn't exist.
  addMetaDataItemToMainApplication(
    mainApplication,
    name, // value for `android:name`
    value // value for `android:value`
  );
  return androidManifest;
}
const withCustomMetaData: ConfigPlugin = (config) => {
  return withAndroidManifest(config, async (config) => {
    // Modifiers can be async, but try to keep them fast.
    config.modResults = await addMetaDataToAndroidManifest(config, config.modResults);
    return config;
  });
};

function addProviderToAndroidManifest(androidManifest: AndroidConfig.Manifest.AndroidManifest) {
  const app = AndroidConfig.Manifest.getMainApplicationOrThrow(
    androidManifest
  ) as AndroidConfig.Manifest.ManifestApplication & { provider?: any[] };
  // Add the provider if it doesn't exist.
  if (!app.provider) {
    app.provider = [];
  }
  // if the provider doesn't have the FileProvider, add it.
  if (
    !app.provider.some((p) => p.$["android:name"] === "android.support.v4.content.FileProvider")
  ) {
    // <provider android:authorities="${applicationId}.fileprovider" android:name="android.support.v4.content.FileProvider" android:exported="false" android:grantUriPermissions="true"><meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"/></provider>
    app.provider.push({
      $: {
        "android:name": "android.support.v4.content.FileProvider",
        "android:authorities": "${applicationId}.fileprovider",
        "android:exported": "false",
        "android:grantUriPermissions": "true",
      },
      "meta-data": {
        $: {
          "android:name": "android.support.FILE_PROVIDER_PATHS",
          "android:resource": "@xml/file_paths",
        },
      },
    });
  }
  return androidManifest;
}
function withProvider(config: ExpoConfig) {
  return withAndroidManifest(config, async (config) => {
    config.modResults = addProviderToAndroidManifest(config.modResults);
    return config;
  });
}

function addPackageToQuery(androidManifest: AndroidConfig.Manifest.AndroidManifest) {
  // <package android:name="com.snapchat.android" />
  const packageToAdd = {
    package: {
      $: {
        "android:name": "com.snapchat.android",
      },
    },
  };
  // @ts-ignore since queries does exist but Expo's types don't have it.
  const queries = androidManifest.manifest.queries;
  if (!queries) {
    // @ts-ignore since queries does exist but Expo's types don't have it.
    androidManifest.manifest.queries = [...packageToAdd];
  } else {
    // @ts-ignore since queries does exist but Expo's types don't have it.
    // TODO: this will break if there are other <package> tags in the queries.
    androidManifest.manifest.queries[0].package = {
      $: { ...packageToAdd.package.$ },
    };
  }

  return androidManifest;
}
function withPackage(config: ExpoConfig) {
  return withAndroidManifest(config, async (config) => {
    config.modResults = addPackageToQuery(config.modResults);
    return config;
  });
}

const withSnapchatSdk: ConfigPlugin = (config) => {
  return withPackage((withCustomMetaData(withSnapkitGradle(config))));
  // skipping withProvider as filePath was giving me issues.
  // return withPackage(withProvider(withCustomMetaData(withSnapkitGradle(config))));
};

export default createRunOncePlugin(withSnapchatSdk, pkg.name, pkg.version);
@ansh ansh added the enhancement New feature or request label Dec 10, 2022
@yingyingbangbagng
Copy link

I faced some problem while request access to the Snapchat camera kit and I didn't receive any response from Snapchat company, so what can I do to get the licenses to access the Snapchat camera kit, how can I solve this problem?

@ansh
Copy link
Author

ansh commented Jan 12, 2023

That is unrelated please mention that in Snapchat’s repo @yingyingbangbagng

@singhayush1403
Copy link

@ansh were you able to add it using the plugins? Stuck on the same step

@ansh
Copy link
Author

ansh commented Mar 24, 2023 via email

@singhayush1403
Copy link

@ansh Could you guide me? Stuck on the part where I have to modify file_paths.xml using the config functions provided by expo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants
@ansh @singhayush1403 @yingyingbangbagng and others