Skip to content

Instantly share code, notes, and snippets.

@hirbod
Last active January 10, 2024 09:11
Show Gist options
  • Select an option

  • Save hirbod/07c6641970c9406ff35a7271dda1f01c to your computer and use it in GitHub Desktop.

Select an option

Save hirbod/07c6641970c9406ff35a7271dda1f01c to your computer and use it in GitHub Desktop.
expo-config-plugin: animated webP support for Expo SDK44+ and Custom Dev Client (with FastImage) support

I recommend to not use this anymore. Switch over to expo-image

If anybody needs animated webP support with Expo (Custom Dev Client) for the native <Image /> and <FastImage /> (read comments):

Android

// create a file like plugins/withAnimatedWebPSupport.js -> this is for the native <Image />

const {
    createRunOncePlugin,
    withGradleProperties
} = require('@expo/config-plugins');

const withAnimatedWebPSupport = (config) => {

    const propertyToModify = {
        type: 'property',
        key: 'expo.webp.animated',
        value: true,
    };

    return withGradleProperties(config, (config) => {
        config.modResults = config.modResults.filter(
            (item) => !(item.type === propertyToModify.type && item.key === propertyToModify.key)
        );

        config.modResults.push(propertyToModify);

        return config;
    });
};

module.exports = createRunOncePlugin(withAnimatedWebPSupport, 'animated-webp-support', '1.0.0');
// create a plugins/withFastImageWebPSupportAndroid.js - This is for `<FastImage />` Support
// note that this will need withAnimatedWebPSupport, because I've replaced it in that if-condition. Feel free to customize if you only want webP with FastImage

const {
    withAppBuildGradle,
} = require("@expo/config-plugins");


const withCustomAppBuildGradle = (config) => {
    const insertString = `implementation "com.github.zjupure:webpdecoder:2.0.4.12.0"`;
    return withAppBuildGradle(config, (config) => {
        if (
            config.modResults.contents.includes(
                insertString
            )
        ) {
            return config;
        }

        config.modResults.contents = config.modResults.contents.replace(
            `if (isWebpAnimatedEnabled) {`,
            `if (isWebpAnimatedEnabled) {
            ${insertString}`
        );
        return config;
    });
};

module.exports = function withFastImageWebPSupportAndroid(config) {
    config = withCustomAppBuildGradle(config);
    return config;
};

iOS

// create a plugins/withFastImageWebPSupportIOS.js
const {
    WarningAggregator,
    withAppDelegate,
    createRunOncePlugin
} = require('@expo/config-plugins');


const RNFI_EXPO_WEBP_IMPORT = `#import "AppDelegate.h"
// expo-config-plugin fast-image webp animated support
#import "SDImageCodersManager.h"
#import <SDWebImageWebPCoder/SDImageWebPCoder.h>
// end config plugin
`

const RNFI_EXPO_WEBP_DID_FINISH_LAUNCHING_IDENTIFIER = `- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{`

const RNFI_EXPO_WEBP_DID_FINISH_LAUNCHING_CODE = `
  // start expo-config-plugin fast-image webp animated support
  [SDImageCodersManager.sharedManager addCoder: SDImageWebPCoder.sharedCoder];
  // end expo-config-plugin fast-image webp animated support
`

function modifyAppDelegate(appDelegate) {
    if (!appDelegate.includes(RNFI_EXPO_WEBP_IMPORT)) {
        appDelegate = appDelegate.replace(
            /#import "AppDelegate.h"/g,
            RNFI_EXPO_WEBP_IMPORT,
        );
    }
    if (appDelegate.includes(RNFI_EXPO_WEBP_DID_FINISH_LAUNCHING_IDENTIFIER)) {
        if (!appDelegate.includes(RNFI_EXPO_WEBP_DID_FINISH_LAUNCHING_CODE)) {
            const block = RNFI_EXPO_WEBP_DID_FINISH_LAUNCHING_IDENTIFIER + RNFI_EXPO_WEBP_DID_FINISH_LAUNCHING_CODE
            appDelegate = appDelegate.replace(RNFI_EXPO_WEBP_DID_FINISH_LAUNCHING_IDENTIFIER, block)
        } else {
            WarningAggregator.addWarningIOS('withFastImageWebPSupportIOSAppDelegate', `FastImage webP support already setup in AppDelegate`)
        }
    } else {
        throw new Error('Failed to detect didFinishLaunchingWithOptions in AppDelegate')
    }
    return appDelegate
}

const withFastImageWebPSupportIOS = (config) => {
    return withAppDelegate(config, (config) => {
        if (['objc', 'objcpp'].includes(config.modResults.language)) {
            config.modResults.contents = modifyAppDelegate(config.modResults.contents)
        } else {
            WarningAggregator.addWarningIOS('withFastImageWebPSupportIOSAppDelegate', `Cannot setup FastImage webP support, the project AppDelegate is not a supported language: ${config.modResults.language}`)
        }
        return config
    })
}

module.exports = createRunOncePlugin(withFastImageWebPSupportIOS, 'rnfi-expo-animated-webp-support', '1.0.0');

Install plugin in your app.json / app.config.js (expo.plugins)

    ['./plugins/withAnimatedWebPSupport'],
    ['./plugins/withFastImageWebPSupportIOS.js'],
    ['./plugins/withFastImageWebPSupportAndroid.js'],

Credits:

Based on other config plugins by @GollyJer, @wodin, @barthap, @EvanBacon and @nandorojo Thanks guys!

Native <Image> component by RN

In case you only need native <Image /> support, just install this package: https://github.com/Aleksefo/react-native-webp-format

and only use the withAnimatedWebPSupport Plugin for Android.

@hirbod
Copy link
Copy Markdown
Author

hirbod commented Jul 25, 2023

Yes, all expo modules work without expo. The Expo modules core just adds 150KB of overhead, so its really not harming

@KishorJena
Copy link
Copy Markdown

and how to use it with bare react native?

@hirbod
Copy link
Copy Markdown
Author

hirbod commented Jul 25, 2023

Just install it :D

yarn add expo-image
npx pod-install

And for bare, just follow these extra instructions:
https://docs.expo.dev/bare/installing-expo-modules/

@KishorJena
Copy link
Copy Markdown

KishorJena commented Jul 26, 2023

ahh but I cant use expo :(
I used implementation("com.github.zjupure:webpdecoder:2.1.4.12.0") in react-native-fast-image build gradle.
I noticed that if I put of RN beside then some webp works fine. remove component disables webp in . i.e. images becomes static.

btw is too laggy while playing animated webp and gifs thats why I want to use fastimage.

@hirbod
Copy link
Copy Markdown
Author

hirbod commented Jul 26, 2023

As said, you're not using Expo when you install expo-modules or any expo-package. It works with every RN install.

@KishorJena
Copy link
Copy Markdown

for some reason expo-module installation did not go well. created new RN project and added
implementation "com.github.zjupure:webpdecoder:2.2.${glideVersion}"
as you suggested in your answer on stackoverflow. and it worked
earlier it was not working. but clean project might removed any conflicts. because I had tried to use fresco in root project as well then removed and added glide. This time I have added the glide webdecoder directly without touching anything else :D

@GollyJer
Copy link
Copy Markdown

@hirbod Have you switched over to expo-image?

I'm trying but animated webp and svg aren't rendering. Animated gifs work fine.
As far as I can tell it should work by simply adding the expo-image package and doing a new eas build. No need for any plugins.

@hirbod
Copy link
Copy Markdown
Author

hirbod commented Jan 10, 2024

See the statement in the main post. We’re using expo-image and I don’t recommended anybody using FastImage anymore!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment