Skip to content

Instantly share code, notes, and snippets.

@hirbod
Last active January 10, 2024 09:11
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hirbod/07c6641970c9406ff35a7271dda1f01c to your computer and use it in GitHub Desktop.
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
Author

hirbod commented Jan 6, 2022

Be careful: the iOS plugin is using a dangerous mod to modify appDelegate.m. Android is using a dangerous mod withAppBuildGradle. You might need to trigger expo prebuild --clean or adjust the code if the search and replace strings have changed.

Would be lovely if someone could make a AppDelegate Subscriber Edition out of it.

https://docs.expo.dev/modules/appdelegate-subscribers/

@GollyJer
Copy link

GollyJer commented Jan 7, 2022

Hey @hirbod! I'm pumped to find this and will give it a shot soon for adding animated webp to fast-image.

Just wanted to let you know the original withAnimatedWebPSupport.js was written by me with @wodin pointing me in the right direction.

I wrote up a stackoverflow Q&A in hopes that this starts becoming more searchable.

I'll let you know if/when I'm able to verify animated webp is working in fast-image and maybe you can throw the answer in here if you have a SO account?

🍻

@hirbod
Copy link
Author

hirbod commented Jan 7, 2022

@GollyJer Sorry I did not mention you in the credits. I did a good portion by myself and copied from plenty other libs. Will add you to the credits. I can promise you it is working, its running on my app flawlessly :)

@hirbod
Copy link
Author

hirbod commented Jan 7, 2022

@GollyJer I've updated the credits :)

@GollyJer
Copy link

GollyJer commented Jan 7, 2022

Nice. Thanks.

Saw your answer on SO. Pumped to try it out tomorrow. 💪

@hirbod
Copy link
Author

hirbod commented Jan 13, 2022

@GollyJer any news? :)

@GollyJer
Copy link

Finished up all this today. Working great!
We're able to use fast-image across our app on iOS and Android.
This allows us to remove a shitload of code that determined what version of iOS, what file type, what type of animation.... blah, blah, blah... then do something different.

Now we just use webp and animated webp with fast-image regardless of operating system. This is great.

@hirbod
Copy link
Author

hirbod commented Jan 15, 2022

Nice! Would be lovely if you accept my both answers on SO :)

@hirbod
Copy link
Author

hirbod commented May 6, 2022

@GollyJer careful, the config-plugin does not currently work on iOS with Expo SDK 45. AppDelegate.m changed to .mm and there is currently no clean way to patch Swift App Delegate files. Might need to pick a brutforce method and patch it with FileSystem.

@GollyJer
Copy link

GollyJer commented May 7, 2022

Thanks for the warning. This is going to be a major pain. Hopefully we can figure something out.

@hirbod
Copy link
Author

hirbod commented May 8, 2022

@GollyJer had a quick chat with Evan on Twitter and I think I can port it to SDK 45

@hirbod
Copy link
Author

hirbod commented May 9, 2022

@GollyJer I've upgraded the iOS part. It is working with SDK 44 and SDK 45 now.

@GollyJer
Copy link

GollyJer commented May 9, 2022

@hirbod Nice. I'll let you know how it goes. We're prepping for our next release in a week or so and I'll upgrade to 45 as a last step. 👍

@GollyJer
Copy link

GollyJer commented Jul 1, 2022

@hirbod Finally got upgraded to v45. The iOS changes worked great. Thanks!

@mikob
Copy link

mikob commented Aug 24, 2022

Works in SDK 46, thank you!

@lxndr-rl
Copy link

Thanks for this :D

@hirbod
Copy link
Author

hirbod commented Jul 25, 2023

Don't use it. Just switch over to expo-image

@KishorJena
Copy link

can't we use it for bare react native . i am not using expo

@hirbod
Copy link
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

and how to use it with bare react native?

@hirbod
Copy link
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

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
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

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

@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
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