[Packager] New asset dependency system in the works #1043

Closed
amasad opened this Issue Apr 27, 2015 · 26 comments

Comments

Projects
None yet
@amasad
Contributor

amasad commented Apr 27, 2015

The way we currently handle images is far from ideal. To get it working, you have to configure the packager to look in the right directory, you have to add the image to xcode, and then you have to require(image!image-name) where image-name is a global name, and finally you'll have to learn how to manage assets for every platform you want to target with React Native. Issues #521 and #282 are examples of how much of a hassle it can be.

I opened this issue to inform you that we're working on a replacement system that would be platform independent and mostly implemented in JS and the packager. Of course, we'll keep the current system running alongside the new one and give you time before we deprecate it. Here are our goals for this:

  • Images can exist alongside the JS code (no need for assetRoots) and be required via CommonJS-like module resolution require('./path/to/img.png') or require('packageName/img.png')
  • Images can have multiple scale versions and are expressed via the @nx notation currently used in iOS. So img.png could actually be img@0.5x.png img@3x.png or what have you and we'd automatically pick the most appropriate scale for the device.
  • This means you can publish React Native components to npm that have asset dependency and have them still work
  • In development you can edit/add/delete images and cmd+r and have it work!
  • We're still working on details on how will this work when shipping apps but hopefully will have some more details about this soon
@nicklockwood

This comment has been minimized.

Show comment
Hide comment
@nicklockwood

nicklockwood Apr 28, 2015

Contributor

Will this apply to sounds, html files, json, etc. as well?

Contributor

nicklockwood commented Apr 28, 2015

Will this apply to sounds, html files, json, etc. as well?

@jaygarcia

This comment has been minimized.

Show comment
Hide comment
@jaygarcia

jaygarcia Apr 28, 2015

Contributor

I"m very much looking forward to this dude. I have asset requirements also, such as JSON files and potentially images that i'd like to pack in my app.

Contributor

jaygarcia commented Apr 28, 2015

I"m very much looking forward to this dude. I have asset requirements also, such as JSON files and potentially images that i'd like to pack in my app.

@wwwsevolod

This comment has been minimized.

Show comment
Hide comment
@wwwsevolod

wwwsevolod Apr 28, 2015

Contributor

it sounds so webpacky. may be just use webpack and use it's dependency graph to automatically inject it in xcode project (or where it need's to be injected). May be i don't understand something? webpack is so modular and flexible, so it could be possible to create loader that loads from local, or load it dynamically (to reduce result app size, to be allowed to download over cellular network).

Contributor

wwwsevolod commented Apr 28, 2015

it sounds so webpacky. may be just use webpack and use it's dependency graph to automatically inject it in xcode project (or where it need's to be injected). May be i don't understand something? webpack is so modular and flexible, so it could be possible to create loader that loads from local, or load it dynamically (to reduce result app size, to be allowed to download over cellular network).

@amasad

This comment has been minimized.

Show comment
Hide comment
@amasad

amasad Apr 28, 2015

Contributor

@nicklockwood yes, I just mentioned images becuase of the special consideration to scales, but otherwise all other assets should just work.

@wwwsevolod there's been a few discussion about why we didn't go with webpack from the start but it boils down to performance (webpack is orders of magnitude slower), the problem that we have a proprietary module system that needs to interop with, and finally non of the current asset loader does what we want it to do. However, this shouldn't stop you from using webpack. Take a look at @mjohnston webpack integration project which works really well, I'm open to patches or whatever is needed to support that project.

@jaygarcia I already adressed the json loading thing, it now returns a parsed JS object, just like in node.

Contributor

amasad commented Apr 28, 2015

@nicklockwood yes, I just mentioned images becuase of the special consideration to scales, but otherwise all other assets should just work.

@wwwsevolod there's been a few discussion about why we didn't go with webpack from the start but it boils down to performance (webpack is orders of magnitude slower), the problem that we have a proprietary module system that needs to interop with, and finally non of the current asset loader does what we want it to do. However, this shouldn't stop you from using webpack. Take a look at @mjohnston webpack integration project which works really well, I'm open to patches or whatever is needed to support that project.

@jaygarcia I already adressed the json loading thing, it now returns a parsed JS object, just like in node.

@ptmt

This comment has been minimized.

Show comment
Hide comment
@ptmt

ptmt May 2, 2015

Contributor

@amasad Would be awesome to have this kind of dependency system!

Is there any best practices to handle a lot of images right now? I'm not a fan of drag'n'droping something to XCode, so I wrote a little gist https://gist.github.com/unknownexception/91689d0f2bb8506cc9d9 which generate assets folders like imagename.imageset with Contents.json inside and three scaled images using GraphicsMagick.

https://facebook.github.io/react-native/docs/image.html says:

A React component for displaying different types of images, including network images, static resources, temporary local images, and images from local disk, such as the camera roll.

How could I insert "temporary local images" and "images from local disk" via <Image> component? What if I want to download and cache network images?

Contributor

ptmt commented May 2, 2015

@amasad Would be awesome to have this kind of dependency system!

Is there any best practices to handle a lot of images right now? I'm not a fan of drag'n'droping something to XCode, so I wrote a little gist https://gist.github.com/unknownexception/91689d0f2bb8506cc9d9 which generate assets folders like imagename.imageset with Contents.json inside and three scaled images using GraphicsMagick.

https://facebook.github.io/react-native/docs/image.html says:

A React component for displaying different types of images, including network images, static resources, temporary local images, and images from local disk, such as the camera roll.

How could I insert "temporary local images" and "images from local disk" via <Image> component? What if I want to download and cache network images?

@yifeic

This comment has been minimized.

Show comment
Hide comment
@yifeic

yifeic May 10, 2015

@amasad The new asset system sounds great. +1 for not touching Xcode project file.
Currently Image component only supports static and network image. In my iOS project, I use font file to generate icons. Is there any way for me to plug in my own image provider for Image? For example, if I have some native code that generate image from font file and then in JS I want to have something like:

<Image source={{uri: 'font://people-icon?size=10&color=white'}}/>

In summary, I want to provide a custom uri(or any other way) to tell Image to use a custom image provider. Not sure if this is related to the new asset system.

yifeic commented May 10, 2015

@amasad The new asset system sounds great. +1 for not touching Xcode project file.
Currently Image component only supports static and network image. In my iOS project, I use font file to generate icons. Is there any way for me to plug in my own image provider for Image? For example, if I have some native code that generate image from font file and then in JS I want to have something like:

<Image source={{uri: 'font://people-icon?size=10&color=white'}}/>

In summary, I want to provide a custom uri(or any other way) to tell Image to use a custom image provider. Not sure if this is related to the new asset system.

@frantic

This comment has been minimized.

Show comment
Hide comment
@frantic

frantic May 11, 2015

Contributor

Is there any way for me to plug in my own image provider for Image?

No, not yet. But I think it's a good idea. Note that currently we have 2 image component providers backing single <Image> - StaticImage and NetworkImage. Would be great to merge them on native side and add plugins support with custom URI schemas.

Contributor

frantic commented May 11, 2015

Is there any way for me to plug in my own image provider for Image?

No, not yet. But I think it's a good idea. Note that currently we have 2 image component providers backing single <Image> - StaticImage and NetworkImage. Would be great to merge them on native side and add plugins support with custom URI schemas.

@nicklockwood

This comment has been minimized.

Show comment
Hide comment
@nicklockwood

nicklockwood May 11, 2015

Contributor

@yifeic I'm working on an improvement to the image loading pipeline that will make it possible to plug in new image loaders. For now though, the only option is to clone RCTStaticImageManager or RCTNetworkImageManager, modify it to use your own loader, then export it with the same name as the built-in module and inject it using the RCTBridge moduleProvider block so that it is used instead of the built-in version.

Contributor

nicklockwood commented May 11, 2015

@yifeic I'm working on an improvement to the image loading pipeline that will make it possible to plug in new image loaders. For now though, the only option is to clone RCTStaticImageManager or RCTNetworkImageManager, modify it to use your own loader, then export it with the same name as the built-in module and inject it using the RCTBridge moduleProvider block so that it is used instead of the built-in version.

@felixkaiser

This comment has been minimized.

Show comment
Hide comment
@felixkaiser

felixkaiser May 12, 2015

Thanks. Can someone with access to the documentation please update it? Just spent an hour finding this. https://facebook.github.io/react-native/docs/image.html#static-assets

Thanks. Can someone with access to the documentation please update it? Just spent an hour finding this. https://facebook.github.io/react-native/docs/image.html#static-assets

@lukasreichart

This comment has been minimized.

Show comment
Hide comment
@lukasreichart

lukasreichart May 12, 2015

@nicklockwood What's your timeframe for the updates? If you pushed something I'd be happy to assist you, because image loading is crucial for my application ....

@nicklockwood What's your timeframe for the updates? If you pushed something I'd be happy to assist you, because image loading is crucial for my application ....

@nicklockwood

This comment has been minimized.

Show comment
Hide comment
@nicklockwood

nicklockwood May 12, 2015

Contributor

@lukasreichart if you need it urgently, I suggest forking the RCTImageManager module.

Contributor

nicklockwood commented May 12, 2015

@lukasreichart if you need it urgently, I suggest forking the RCTImageManager module.

@brentvatne brentvatne changed the title from New asset dependency system in the works to [Packager] New asset dependency system in the works May 30, 2015

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost Aug 4, 2015

Thank you for reporting this issue and appreciate your patience. We've notified the core team for an update on this issue. We're looking for a response within the next 30 days or the issue may be closed.

ghost commented Aug 4, 2015

Thank you for reporting this issue and appreciate your patience. We've notified the core team for an update on this issue. We're looking for a response within the next 30 days or the issue may be closed.

@tadeuzagallo

This comment has been minimized.

Show comment
Hide comment
@tadeuzagallo

tadeuzagallo Aug 4, 2015

Contributor

@amasad can we close it?

Contributor

tadeuzagallo commented Aug 4, 2015

@amasad can we close it?

@amasad

This comment has been minimized.

Show comment
Hide comment
@amasad

amasad Sep 8, 2015

Contributor

Sure. Although, we'll probably ship and document this soon (we started using it internally) I don't want to make promises 😛

Contributor

amasad commented Sep 8, 2015

Sure. Although, we'll probably ship and document this soon (we started using it internally) I don't want to make promises 😛

@amasad amasad closed this Sep 8, 2015

@gnestor

This comment has been minimized.

Show comment
Hide comment
@gnestor

gnestor Nov 9, 2015

Contributor

I upgraded to React Native 0.14.1 and like in 0.13, when I create an offline bundle, the static images that I require using the new system are not resolved (blank). The images show up fine when using the dev server bundle. Do I need to set an "assets destination folder" when bundling? Is this documented anywhere?

Contributor

gnestor commented Nov 9, 2015

I upgraded to React Native 0.14.1 and like in 0.13, when I create an offline bundle, the static images that I require using the new system are not resolved (blank). The images show up fine when using the dev server bundle. Do I need to set an "assets destination folder" when bundling? Is this documented anywhere?

@frantic

This comment has been minimized.

Show comment
Hide comment
@frantic

frantic Nov 9, 2015

Contributor

@gnestor 0.14 introduced a new script which now runs as part of your Xcode build. This means you no longer need to run react-native bundle from the terminal before building your app. To upgrade, use http://facebook.github.io/react-native/docs/upgrading.html or follow this instructions:

screen shot 2015-11-09 at 3 32 37 pm

screen shot 2015-11-09 at 3 31 59 pm

Note that if you have main.jsbundle you'll have to remove it, since it now is generated automatically for you:

screen shot 2015-11-09 at 3 37 34 pm

Let us know if it works for you and we can add this to upgrading documentation.

Contributor

frantic commented Nov 9, 2015

@gnestor 0.14 introduced a new script which now runs as part of your Xcode build. This means you no longer need to run react-native bundle from the terminal before building your app. To upgrade, use http://facebook.github.io/react-native/docs/upgrading.html or follow this instructions:

screen shot 2015-11-09 at 3 32 37 pm

screen shot 2015-11-09 at 3 31 59 pm

Note that if you have main.jsbundle you'll have to remove it, since it now is generated automatically for you:

screen shot 2015-11-09 at 3 37 34 pm

Let us know if it works for you and we can add this to upgrading documentation.

@gnestor

This comment has been minimized.

Show comment
Hide comment
@gnestor

gnestor Nov 10, 2015

Contributor

@frantic Thanks for the instructions (I missed that part of the upgrading process because I migrated my existing Xcode project and didn't see that in the diffs). I added the new script to my Build Phases and it works fine as long as the packager is running and serving the assets. As soon as I stop the packager, the images don't show. How can I build an offline bundle?

Contributor

gnestor commented Nov 10, 2015

@frantic Thanks for the instructions (I missed that part of the upgrading process because I migrated my existing Xcode project and didn't see that in the diffs). I added the new script to my Build Phases and it works fine as long as the packager is running and serving the assets. As soon as I stop the packager, the images don't show. How can I build an offline bundle?

@frantic

This comment has been minimized.

Show comment
Hide comment
@frantic

frantic Nov 11, 2015

Contributor

@gnestor note that your need to change the URL you load your bundle from. Instead of devserver it should be local file. Let me know if this helps. Otherwise reach out to @frantic via #reactiflux on Discord.

Contributor

frantic commented Nov 11, 2015

@gnestor note that your need to change the URL you load your bundle from. Instead of devserver it should be local file. Let me know if this helps. Otherwise reach out to @frantic via #reactiflux on Discord.

@gnestor

This comment has been minimized.

Show comment
Hide comment
@gnestor

gnestor Nov 11, 2015

Contributor

@frantic Adding the main.jsbundle generated by the Xcode script solved the problem, images are showing up fine for offline bundles (duh!). I'm noticing that the Xcode script is being run every time I build in Xcode, whether for development or production, which adds another 10-20s to build time. However, the bundle isn't used when running the app against the dev server, correct? If so, what's the value in using this new bundle script from Xcode vs. the former method of running react-native bundle when an offline bundle is needed?

Contributor

gnestor commented Nov 11, 2015

@frantic Adding the main.jsbundle generated by the Xcode script solved the problem, images are showing up fine for offline bundles (duh!). I'm noticing that the Xcode script is being run every time I build in Xcode, whether for development or production, which adds another 10-20s to build time. However, the bundle isn't used when running the app against the dev server, correct? If so, what's the value in using this new bundle script from Xcode vs. the former method of running react-native bundle when an offline bundle is needed?

@frantic

This comment has been minimized.

Show comment
Hide comment
@frantic

frantic Nov 19, 2015

Contributor

@gnestor (sorry for late reply) the value is in getting unified workflow and less room for errors by paying additional build cost. With react-native bundle you always have to think about offline vs dev, keep in mind that you need --dev false when before submitting your app, etc.

Now imagine you have Android app that also has it's own way of bundling, and a different bundle path, different set of options (e.g. images work differently in Xcode and Android SDK), etc.

You still have power and freedom of using the react-native bundle yourself. You can remove the Xcode integration script and Gradle build step, and have a different place where you'd put all these commands.

Contributor

frantic commented Nov 19, 2015

@gnestor (sorry for late reply) the value is in getting unified workflow and less room for errors by paying additional build cost. With react-native bundle you always have to think about offline vs dev, keep in mind that you need --dev false when before submitting your app, etc.

Now imagine you have Android app that also has it's own way of bundling, and a different bundle path, different set of options (e.g. images work differently in Xcode and Android SDK), etc.

You still have power and freedom of using the react-native bundle yourself. You can remove the Xcode integration script and Gradle build step, and have a different place where you'd put all these commands.

@gnestor

This comment has been minimized.

Show comment
Hide comment
@gnestor

gnestor Nov 19, 2015

Contributor

@frantic This makes sense to ease the pain of working across platforms. I have an idea to potentially improve the experience on iOS:

The new Xcode build script can inspect AppDelegate.m and if jsCodeLocation is of NSURL type, exit.

Good idea? If so I can bang it out and submit a pull request...

Contributor

gnestor commented Nov 19, 2015

@frantic This makes sense to ease the pain of working across platforms. I have an idea to potentially improve the experience on iOS:

The new Xcode build script can inspect AppDelegate.m and if jsCodeLocation is of NSURL type, exit.

Good idea? If so I can bang it out and submit a pull request...

@frantic

This comment has been minimized.

Show comment
Hide comment
@frantic

frantic Nov 23, 2015

Contributor

Very, very clever idea, but I'm afraid it can be too magical. Also how will it work on Android?

Contributor

frantic commented Nov 23, 2015

Very, very clever idea, but I'm afraid it can be too magical. Also how will it work on Android?

@gnestor

This comment has been minimized.

Show comment
Hide comment
@gnestor

gnestor Nov 23, 2015

Contributor

I haven't dug into React Native for Android yet, so this would just be an optimization for the react-native-xcode.sh. Here's what I'm thinking: gnestor@68116d5

# Find the app's AppDelegate.m file
app_delegate=$(find ./../../../iOS -name 'AppDelegate.m')

# Find the last jsCodeLocation assignment (that is not a comment)
type=$(awk '/jsCodeLocation =/ && !/\/\/ /' $app_delegate | tail -1)

# If the jsCodeLocation type is 'NSURL', then skip the bundle script
if [[ "$type" =~ "NSURL" ]]; then
  exit 1
fi
Contributor

gnestor commented Nov 23, 2015

I haven't dug into React Native for Android yet, so this would just be an optimization for the react-native-xcode.sh. Here's what I'm thinking: gnestor@68116d5

# Find the app's AppDelegate.m file
app_delegate=$(find ./../../../iOS -name 'AppDelegate.m')

# Find the last jsCodeLocation assignment (that is not a comment)
type=$(awk '/jsCodeLocation =/ && !/\/\/ /' $app_delegate | tail -1)

# If the jsCodeLocation type is 'NSURL', then skip the bundle script
if [[ "$type" =~ "NSURL" ]]; then
  exit 1
fi
@ide

This comment has been minimized.

Show comment
Hide comment
@ide

ide Nov 23, 2015

Collaborator

Xcode passes in CONFIGURATION as an environment variable to the script: https://developer.apple.com/library/mac/documentation/DeveloperTools/Reference/XcodeBuildSettingRef/1-Build_Setting_Reference/build_setting_ref.html#//apple_ref/doc/uid/TP40003931-CH3-SW49

Assuming that Debug implies using the packager server and Release implies using the embedded bundle, this seems like a robust approach.

Collaborator

ide commented Nov 23, 2015

Xcode passes in CONFIGURATION as an environment variable to the script: https://developer.apple.com/library/mac/documentation/DeveloperTools/Reference/XcodeBuildSettingRef/1-Build_Setting_Reference/build_setting_ref.html#//apple_ref/doc/uid/TP40003931-CH3-SW49

Assuming that Debug implies using the packager server and Release implies using the embedded bundle, this seems like a robust approach.

@gnestor

This comment has been minimized.

Show comment
Hide comment
@gnestor

gnestor Nov 23, 2015

Contributor

It could be as simple as that: if [[ $DEV == true ]] exit 1

The only use case that this ignores is testing on a device in dev mode (which would still be possible using react native bundle)...

Contributor

gnestor commented Nov 23, 2015

It could be as simple as that: if [[ $DEV == true ]] exit 1

The only use case that this ignores is testing on a device in dev mode (which would still be possible using react native bundle)...

@frantic

This comment has been minimized.

Show comment
Hide comment
@frantic

frantic Nov 23, 2015

Contributor

Assuming that Debug implies using the packager server and Release implies using the embedded bundle, this seems like a robust approach.

Yeah that's what we used with at FB initially, however this special casing rules (skip bundle if Debug, unless building for device or if building on CI, etc.) only grew bigger and proved to be a nightmare to support. The other problem is that this bunch of ifs had to be duplicated in both building scripts and Objective-C code that specifies where to load the bundle from.

which would still be possible using react native bundle

This is true only for JS changes. If you are using the new asset system the images also have to be copied into the resulting app bundle, the location of which is harder to guess and a bit too much to manually type in the command line.

Contributor

frantic commented Nov 23, 2015

Assuming that Debug implies using the packager server and Release implies using the embedded bundle, this seems like a robust approach.

Yeah that's what we used with at FB initially, however this special casing rules (skip bundle if Debug, unless building for device or if building on CI, etc.) only grew bigger and proved to be a nightmare to support. The other problem is that this bunch of ifs had to be duplicated in both building scripts and Objective-C code that specifies where to load the bundle from.

which would still be possible using react native bundle

This is true only for JS changes. If you are using the new asset system the images also have to be copied into the resulting app bundle, the location of which is harder to guess and a bit too much to manually type in the command line.

@gnestor gnestor referenced this issue in zmxv/react-native-sound Jan 22, 2016

Open

Play sound from JS bundle (main.jsbundle) #14

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