kristerkari/react-native-svg-transformer

Cannot pass PlatformColor or DynamicColorIOS as fill prop

habovh opened this issue ยท 9 comments

Description

Hi,

I've been using the library for quite a while now, and I'm in the process of implementing Dark Mode on the app.

However, it seems that the transformer won't accept fill props with colors that came from the new PlatformColor or DynamicColorIOS React Native APIs.

Not sure exactly if the culprit is the transformer or the underlying SVGR.

Any ideas on how we can add support for these new color APIs?

Error logged

[Fri Nov 13 2020 20:04:35.610]  WARN     "[object Object]" is not a valid color or brush 
G@http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:123276:36
RNSVGSvgView
Svg@http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:122985:36
SvgComponent

Thanks @habovh! Could you please paste the code that you are using here? It would make it easier to reproduce the problem.

Anyway, it looks like the final fill value is a result of calling the .toString of an object, so maybe the value that gets passed to fill is an object instead of a string.

Hi @kristerkari, sure thing!

You're actually pointing right at the problem: these colours should not be transformed to strings, but used directly by the underlying native components. I know the API is quite new on React Native's side, so I'm not quite sure how this can translate for a library like svg-transformer, but in my opinion it's a feature that would make a lot of sense.

Here's a snippet that can help you reproduce the issue:

import { PlatformColor } from 'react-native'
import LocationIcon from './icons/location.svg'

const fillColor = PlatformColor('systemYellow')

const MyComponent = () => (
  <View>
    <LocationIcon width={20} height={20} fill={fillColor} />
  </View>
)

And the same with DynamicColorIOS:

import { DynamicColorIOS } from 'react-native'
import LocationIcon from './icons/location.svg'

const fillColor = DynamicColorIOS({ light: '#ff00ff', dark: '#00ffff'})

const MyComponent = () => (
  <View>
    <LocationIcon width={20} height={20} fill={fillColor} />
  </View>
)

Edit: converting a PlatformColor to a string value would defeat the purpose of keeping a reference to a semantic colour that the system would display differently depending on the current light/dark system appearance.

@habovh before I try out your code example, are you using the SVGR configuration to replace SVG image fill attribute with the react property? (https://github.com/kristerkari/react-native-svg-transformer#changing-svg-fill-color-in-js-code)

If not, then can you try to enable it and restart the Metro packager?

@kristerkari I just tested and I can confirm the issue still occurs with the SVGR configuration.

On a sidenote, I did not need to add the custom config for the fill prop to work (using string-based colours).

Edit: I still strongly believe that converting the colour to a string is not appropriate. The PlatformColor and DynamicColorIOS APIs should provide a drop-in replacement for any colour in the React Native world. I'm sure the colours could be passed as these special objects to be interpreted by React Native components later down the road.

@habovh ok after having a look at what those React Native APIs actually do, I realized that they don't return simple colors, but instead an object like dynamic: { light: "#ff00ff", dark: "#00ffff" }.

The reason why it does not work seems to be that react-native-svg library does not have support for those APIs yet and can't render the result correctly:
software-mansion/react-native-svg#1391

@kristerkari makes total sense. I guess the ball is in react-native-svg's camp now. Thanks for the reactivity though!

I'm not sure what you are doing exactly, but as a workaround something like this could be done:

import { useColorScheme } from 'react-native';
const colorScheme = useColorScheme();
const fillColor = DynamicColorIOS({ light: '#ff00ff', dark: '#00ffff'})
<LocationIcon width={20} height={20} fill={fillColor.dynamic[colorScheme]} />

@kristerkari it's actually a functioning workaround, however using the useColorScheme hook would trigger a component render in JS again. Using PlatformColor or DynamicColorIOS directly would avoid this, because the visual appearance of the color would be changed by the system directly.

I'm not sure what you are doing exactly, but as a workaround something like this could be done:

import { useColorScheme } from 'react-native';
const colorScheme = useColorScheme();
const fillColor = DynamicColorIOS({ light: '#ff00ff', dark: '#00ffff'})
<LocationIcon width={20} height={20} fill={fillColor.dynamic[colorScheme]} />

Unfortunately, this workaround does not work with PlatformColor ๐Ÿ˜ข