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

Capacitor build script fixes #2516

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

fbmcipher
Copy link

@fbmcipher fbmcipher commented Nov 1, 2024

There were some issues with the Capacitor config file (capacitor.config.ts) and the build scripts for Capacitor in package.json that were stopping me from getting a working build of Capacitor on Android. Opening the app would just yield a black screen.

This was happening because of a line in the Capacitor config file that says to use a development server defined in .env instead of loading from local built assets (if NODE_ENV=development)

I have resolved this with some changes in capacitor.config.ts and some new scripts in package.json:

  • yarn cap:android:dev – dev build that uses live server
  • yarn cap:android:static – dev build that uses static assets
  • yarn cap:android – dev build that uses live server (default)

And all the same for iOS.

(As a bonus – this also means we have proper build scripts ready for CI/CD!)

Faiz Mustafa added 3 commits November 1, 2024 00:44
Changes in `.env.development.local` should override those in `.env`, but this was not working as intended.
Prior to this fix, `yarn cap:android`  built a static development version of the app. It builds correctly, but upon opening the app the user just gets a black screen.

This was happening because of a line in the Capacitor config file that says to use a development server defined in `.env` instead of loading from local built assets (if `NODE_ENV=development`)

So there was an incongruency – `cap:android` is creating a static build, but the Capacitor config is set up to use live server.

I have resolved this with some changes in `capacitor.config.ts` and some new scripts in `package.json`:

* yarn cap:android:dev – dev build that uses live server
* yarn cap:android:static – dev build that uses static assets
* yarn cap:android – dev build that uses live server (default)

and the same for yarn cap:ios, yarn cap:ios:static, yarn cap:ios:dev.

Now all build scripts work as intended.
Set CAPACITOR_SERVER_URL to the default address the dev server runs at (localhost:3000), and guide users to change in .env.development.local if necessary.

This enables usage of the dev server directly after cloning without any config.
Copy link
Contributor

@raineorshine raineorshine left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

Unfortunately, I now see the blank screen on iOS :). When running yarn cap:ios on main the Xcode console says capacitor://localhost (correct) and on this branch it says http://192.168.1.8:3000/ (incorrect, at least when in development mode).

Aside from that, maybe you can explain more about BUILD_MODE. Our original goal for configuring the build environment was to have everything branch off NODE_ENV, which can be development (default), production, or test.

It's been a while since another developer set up Capacitor on this project, but it looks like the intention is to serve the app from the vite dev server in development mode and serve the app from the internal Capacitor sever in production mode. I suspect the Android issue is actually a problem connecting to localhost rather than a misconfiguration of the environment per se.

@fbmcipher
Copy link
Author

fbmcipher commented Nov 1, 2024

Apologies for the regression! Let me explain the rationale behind these changes. I'll work with a fresh clone of both main and capacitor-fixes to ensure no environment settings cause inconsistent behaviour.

Evaluating the behaviour in main

yarn cap:ios on main does two things:

  • yarn cap:sync – this copies built web assets from build directory to the iOS project.
  • cap open ios – opens the project in Xcode to build.

When you Build and Run the project in Xcode, the console shows the internal Capacitor server capacitor://localhost, which loads the built web assets – and the app works as intended. So yarn cap:ios is copying the web build artifacts, and then building a static version of the app – not load from vite!

yarn cap:android on main does the same two things:

  • yarn cap:sync – copies built web assets from build directory to the Android project
  • cap open android – opens the project in AndroidStudio to build

However, when you build and run this project on Android, you'll see this errors in the console:

  • E: Provided server url is invalid: no protocol:
  • W: Loading app at null

That the Capacitor URL is being logged as null indicated to me that the server URL wasn't being properly set. This makes sense, as there is a line in capacitor.config.ts that redefines the server URL based on the NODE_ENV:

const serverConfig =
  nodeEnv === 'development'
    ? {
        server: {
          url: process.env.CAPACITOR_SERVER_URL,
          cleartext: true,
        },
      }
    : {}

Of course, in a fresh clone, CAPACITOR_SERVER_URL is blank. So that causes the null server URL, and causes the blank screen to show on Android. This indicates to me that the problem is at least in part a configuration issue. Even if I set CAPACITOR_SERVER_URL in .env.development.local, my changes would not cascade correctly – which was the rationale behind ab97bbb.

(I have no idea why this only happens on Android and not iOS, as really the behaviour should be identical across both platforms!).

Revisiting the initial intention: There's an ambiguity. yarn cap:android and yarn cap:ios in main are set up to generate static builds (given the fact that they sync the built web assets to the native project, and that the iOS build points to the internal Capacitor server capacitor://localhost which correctly loads the built web assets).

But the code in capacitor.config.ts, and the behaviour of the Android build, tell us that when we make a development build we should be loading from a vite dev server (as why else would we be redefining CAPACITOR_CONFIG_URL from its default?)

So my conclusions were that either that:

  • The iOS build is improperly configured (because it should be loading from vite), and/or
  • The Android build is improperly configured (because it should be loading from in-built assets as the iOS build does)

Either way, we don't have the ability to build both a static dev build and a live dev build – and either way there does seem to be a misconfiguration somewhere.

BUILD_MODE

So we have two kinds of development build:

  • static – containing built web assets from cap:sync. This is the current behaviour of yarn cap:ios in main.
  • server – running off of the vite dev server. As you say, this does seem to be the intention given the explicit code in capacitor.config.ts.

That's why I introduced BUILD_MODE and the new build scripts – to resolve the ambiguity and ensure dev builds work as explicitly intended on both iOS and Android.

In capacitor-fixes yarn cap:android and yarn cap:ios are aliased to yarn cap:android:dev and yarn cap:ios:dev – so a dev build running of the vite server is the default. The user can opt to yarn cap:android:static if they want a static dev build for testing purposes.

If you wanted to work solely off of NODE_ENV, we could work it out as follows:

  • development builds run off of the Vite dev server => yarn cap:ios
  • test builds run off of static web assets in development/debug mode => yarn cap:ios:test
  • production builds run off of production web assets => yarn cap:ios:prod

Testing capacitor-fixes on a fresh clone:

  • yarn cap:ios:dev (now aliased to yarn cap:ios): Loads app from Vite dev server at localhost:3000 (the new default CAPACITOR_SERVER_URL), and works as intended. It's interesting that you see http://192.168.1.8:3000. I guess this is set in your env file? It should work if you have a vite server running there.
  • yarn cap:ios:static: Generates a static build using built web assets (no vite). This is the current behaviour of cap:ios in main. Works as intended, loads from capacitor://localhost.
  • yarn cap:android:dev (now aliased to yarn cap:android): Loads from Vite dev server. I did need to reconfigure CAPACITOR_SERVER_URL with an absolute IP address, but then it works as intended.
  • yarn cap:android:static: Generates a static build using built web assets (no vite). Works as intended.

I hope this explains the situation in more detail, and I also hope I understood your concerns correctly! Please let me know how you want to proceed.

@raineorshine
Copy link
Contributor

raineorshine commented Nov 1, 2024

When you Build and Run the project in Xcode, the console shows the internal Capacitor server capacitor://localhost, which loads the built web assets – and the app works as intended. So yarn cap:ios is copying the web build artifacts, and then building a static version of the app – not load from vite!

Ah, I see! I misunderstood the localhost part.

Of course, in a fresh clone, CAPACITOR_SERVER_URL is blank. So that causes the null server URL, and causes the blank screen to show on Android. This indicates to me that the problem is at least in part a configuration issue. Even if I set CAPACITOR_SERVER_URL in .env.development.local, my changes would not cascade correctly – which was the rationale behind ab97bbb.

Yes, that makes sense.

  • static – containing built web assets from cap:sync. This is the current behaviour of yarn cap:ios in main.
  • server – running off of the vite dev server. As you say, this does seem to be the intention given the explicit code in capacitor.config.ts.

The static assets that are generated with yarn build and added to /build is the production build. The live dev server is what I refer to as development. So I think of it like this:

staticproduction
serverdevelopment

I don't think we need to worry about test at the moment, since we don't ever check NODE_ENV === 'test' and we don't have any tests that run against the capacitor builds.

Do you think this would work? Or do you think static/server + production/development are orthogonal? I'm confused about what a dev build of static assets would mean... 🤔

  • yarn cap:ios:dev (now aliased to yarn cap:ios): Loads app from Vite dev server at localhost:3000 (the new default CAPACITOR_SERVER_URL), and works as intended. It's interesting that you see http://192.168.1.8:3000. I guess this is set in your env file? It should work if you have a vite server running there.

This does not work on my side, and 192.168.1.8 is not in any of my env files. I just get a blank screen.

  • yarn cap:ios:static: Generates a static build using built web assets (no vite). This is the current behaviour of cap:ios in main. Works as intended, loads from capacitor://localhost.

This works for me! I hadn't tried it before.

Are you able to run the Capacitor app against the live dev server? If so, it must be an issue on my computer and I can work on that separately. I'll be interested to see if it works for the developer that's coming in to work on the Capacitor build.

@fbmcipher
Copy link
Author

fbmcipher commented Nov 5, 2024

Do you think this would work? Or do you think static/server + production/development are orthogonal? I'm confused about what a dev build of static assets would mean... 🤔

Okay, I think I can see the confusion!

Here's how the Capacitor starter project does it by default.

    "build:native": "ionic build && npx cap copy",
    "build:native:prod": "ionic build --prod && npx cap copy",
    "install:android": "npm run build:native && cd android && gradlew installDebug",
    "install:android:prod": "npm run build:native:prod && cd android && gradlew installDebug",

In the stock config, there isn't even a live server. You get two builds:

  • development/debug native app build, with development/unminified web code
  • production native app build, with production/minified web code

Like you pointed out, our static asset development builds contain a production build from vite + development build of the native app. This is an oversight, the builds should be as follows:

  • cap:android:static – development/unminified web code with debug native app build
  • cap:android:dev – live server with debug native app build

I do hear what you're saying – the vite server is more appropriate for actual development.

The main advantage behind the static development build is that they'd enable development tools (like web inspect) to attach to the app. That could come in if a dev/tester's using the app away from the computer, they run into a bug, and need the ability to connect up to a computer to investigate more deeply. Of course, we couldn't do this with a production build.

I can either:

  • update our static dev build to use development/unminified web code
  • remove the static dev build if you feel we don't need it

Hope that makes sense 😅 Aiming to get this closed & merged ASAP so you can hand over to your Capacitor dev.

Are you able to run the Capacitor app against the live dev server? If so, it must be an issue on my computer and I can work on that separately. I'll be interested to see if it works for the developer that's coming in to work on the Capacitor build.

Yes – this works for me on a fresh clone if I start the vite server in one tab, then run cap:ios:dev in another. Have you tried in a fresh clone?

@raineorshine
Copy link
Contributor

Thanks for your response.

The main advantage behind the static development build is that they'd enable development tools (like web inspect) to attach to the app. That could come in if a dev/tester's using the app away from the computer, they run into a bug, and need the ability to connect up to a computer to investigate more deeply. Of course, we couldn't do this with a production build.

My takeaway from this is that the live dev server has different benefits from a static development build, and that these are mutually exclusive:

  • Live dev server - Hot reload allows developer to see UI changes immediately when developing on local machine.
  • Static dev build - Allows development tools (like web inspect) to attach to the app and view the console, set breakpoints, etc.

Is that accurate?

I'm a little out of my element here, and I'm not sure what to do.

@msdewitt Would you mind taking a look at this discussion and offering your opinion since you're our new Capacitor guy? :)

Are you able to run the Capacitor app against the live dev server? If so, it must be an issue on my computer and I can work on that separately. I'll be interested to see if it works for the developer that's coming in to work on the Capacitor build.

Yes – this works for me on a fresh clone if I start the vite server in one tab, then run cap:ios:dev in another. Have you tried in a fresh clone?

I'm reluctant to try a fresh clone, as it's a time-consuming process. It's also important to me that I can rely on git for version control and trust that checking out a different version is consistent across platforms. So a fresh clone is not something I typically reach for. But I can try it this time since this appears to only be an issue on my machine.

@fbmcipher
Copy link
Author

It's more like this:

  • Live dev server - Hot reload allows developer to see UI changes immediately when developing on local machine and allows development tools.
  • Static dev build - Development build of the app – allows development tools
  • Production build - production build of the app – development tools not allowed.

I hope this makes sense.

We can just go with a simpler live dev + production build setup, and take static dev build out of the equation if it's making things confusing.

Really, the behaviour of the iOS Capacitor build being a "static dev build" prior to my fixes was why I kept it in!

Looking forward to yours + @msdewitt's input.

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

Successfully merging this pull request may close these issues.

2 participants