Open KevinHetzelGFL opened 1 year ago
@KevinHetzelGFL this works for me: https://github.com/nxext/nx-extensions/issues/773
Just tested with a fresh NX version and fresh app using ios and android with capacitor 5 on a mac. Works for both ios and android.
@mbeckenbach please tell me which command you use. If i execute ionic capacitor run android -l --external
I get this error:
[ERROR] Sorry! ionic capacitor run can only be run in an Ionic project directory.
@KevinHetzelGFL just dont run it directly using capacitor.
Here is how i do it step by step:
import { CapacitorConfig } from '@capacitor/cli';
import ip from 'ip';
const config: CapacitorConfig = {
...
server: {
androidScheme: 'https',
url: `http://${ip.address()}:4200`, // If you run on another port, adjust it
cleartext: true,
},
};
export default config;
SERVE Target: The port and host are important here.
"serve": {
"executor": "@angular-devkit/build-angular:dev-server",
"options": {
"port": 4200,
"host": "0.0.0.0"
},
"configurations": {
"production": {
"browserTarget": "aticash:build:production"
},
"development": {
"browserTarget": "aticash:build:development"
}
},
"defaultConfiguration": "development"
},
RUN Targets:
"run": {
"executor": "@nxext/capacitor:cap",
"options": {
"cmd": "run"
},
"configurations": {
"ios": {
"cmd": "run ios -l --external"
},
"android": {
"cmd": "run android -l --external"
}
}
},
nx serve yourapp
nx run yourapp:run:android
or nx run yourapp:run:ios
@KevinHetzelGFL Please note that i tested this on MacOS. I tried to get it running on windows. But windows s*cks as always.
@KevinHetzelGFL I am not sure if this capacitor config change affects the real device builds too. I guess so. So be careful before publishing the app to stores!!!
If this really works when deployed to real devices, it opens new options for development & testing. :-D
Btw: Works on windows with android too. I just had some issues with Android Studio...
just to clarify a few things here...
- Adjust the capacitor.config.ts, to point to your nx serve instance.
import { CapacitorConfig } from '@capacitor/cli'; import ip from 'ip'; const config: CapacitorConfig = { ... server: { androidScheme: 'https', url: `http://${ip.address()}:4200`, // If you run on another port, adjust it cleartext: true, }, }; export default config;
doing it this way will break anything other than a "live reload development run".. the capacitor.config.ts
file in your project's web src
directory is only used to generate the capacitor.config.json
in your project's native app project directories, and is only updated again the next time you run a cap sync
(I think it's part of update
, but I never run copy
and update
separately).
to get around this, you will need to conditionally modify your CapacitorConfig
's server
object. in my projects, I do this as follows:
import { CapacitorConfig } from '@capacitor/cli';
import { address } from 'ip';
const config: CapacitorConfig = {
...
server: {
androidScheme: 'https',
},
};
// this dynamic configuration allows the Capacitor app to source its UI from the Angular webserver,
// which will then serve the UI dynamically with live reloading for development purposes.
if (process.env['NX_CLI_SET'] === 'true' && process.env['NX_TASK_TARGET_TARGET'] === 'run') {
// NOTE: virtual adapters from the likes of VMWare or VirtualBox do tend to mess with this a little.. YMMV.
// furthermore, the "ip" package's "address" method fails loudly..
let wirelessIp: string | undefined;
try {
wirelessIp = address('Wi-Fi');
} catch (error) {}
let ethernetIp: string | undefined;
try {
ethernetIp = address('Ethernet');
} catch (error) {}
const serverIp = wirelessIp || ethernetIp;
if (typeof serverIp !== 'string') {
throw Error('could not find a valid server IP address to use for Live Reload..');
}
const serverPort = process.env['PORT'] || '4200';
config.server = {
url: `http://${serverIp}:${serverPort}`,
cleartext: true,
};
console.log('custom Capacitor config.server:', config.server);
console.warn('be sure to start the Angular dev server as well, this step is not automated (yet)..');
}
export default config;
in this case, process.env['NX_CLI_SET']
will be true simply because we're running commands with Nx CLI. whereas process.env['NX_TASK_TARGET_TARGET']
will be the actual Nx command being run, in this case being triggered by nx run <app>:run
.
for context, running nx run <app>:build
will not activate the config tweaks and the "live reload" configuration will not be injected into your application's capacitor.config.json
file, in turn not breaking your production builds or your distributable dev builds (for Android anyway, since iOS doesn't allow direct installs from ipa
files).
secondly, the --livereload
and --external
CLI args are not part of the Capacitor CLI, they're part of the Ionic CLI, which includes a subcommand that wraps Capacitor.
so, ionic cap run android --livereload
will cause the internal web server to start up with "live reload" enabled, whereas cap run android --livereload
will simply build the app and launch it on Android, exactly the same as cap run android
would.
at the same time ionic cap run --external
will cause the internal Angular web server to bind itself to the 0.0.0.0
IP address, allowing it to "serve" HTTP requests from something other than localhost
, whereas cap run
(without ionic
) will not launch the Angular web server.
in your application's project.json
, I would recommend modifying your serve
target to include the following changes to your development
config (or even create a new config), similar to the following:
"serve": {
"executor": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "live:build:production"
},
"development": {
"browserTarget": "live:build:development",
"host": "0.0.0.0",
"liveReload": true
}
},
"defaultConfiguration": "development"
},
you can leave the run
target as it was, adding --livereload
and --external
have no effect there.
but then to get the app running with "live reload", you need to:
nx run <app>:serve:development
- this will continue to run until your kill the process.nx run <app>:run
- this process will end once the native app has been built and deployed to your device or emulator.keep in mind that the app deployed to your device or emulator by this will always look at the configured server.url
address when it starts up, so once the native app has been built and deployed, all you need to do to continue your "live reload development" process is launch your Angular web server with live reload, and open your already deployed native mobile app.
hope that helps a bit..
@andregreeff thank you very much!
@andregreeff Great! Thank you so much. I just had to modify one line on my mac, adding a fallback if wireless or ethernet up dont work.
const serverIp = wirelessIp || ethernetIp || address();
@andregreeff Did you ever try to use this approach in combination with ngrok?
@andregreeff Did you ever try to use this approach in combination with ngrok?
nope, sorry.. I've never used ngrok.
@andregreeff Great! Thank you so much. I just had to modify one line on my mac, adding a fallback if wireless or ethernet up dont work.
const serverIp = wirelessIp || ethernetIp || address();
the process of picking which interface to use is a PITA, especially when trying to do so with as little human input as possible.
just for reference, my Windows 11 work laptop has both a wireless and wired interface, and I have both VMWare and Fortinet VPN installed, each of which add virtual adapters..
disclaimer: this is digressing from the initial "support live reload" topic slightly, but it is relevant in the sense that this is something that needs to be catered for in order to automate the process of running a "serve with live reload" workflow...
so, queryingrequire('os').networkInterfaces()
on my PC lists the following interfaces:
'VMware Network Adapter VMnet1'
with IPv4 192.168.48.1
'VMware Network Adapter VMnet8'
with IPv4 192.168.21.1
'Wi-Fi'
with IPv4 192.168.3.4
'Loopback Pseudo-Interface 1'
with IPv4 127.0.0.1
'vEthernet (Default Switch)'
with IPv4 172.24.128.1
'vEthernet (WSL)'
with IPv4 172.25.128.1
but these names can and do vary wildly between different computers, being affected by everything from hardware, host OS, even any additional software that may or may not be installed.
so keeping the "desired interface names" as simple and generic as possible, I opted to test the following:
ip.address("Wi-Fi")
gave result: 192.168.3.4
ip.address("Ethernet")
gave error: Cannot read properties of undefined (reading 'filter')
ip.address("public")
gave result: 192.168.48.1
ip.address("private")
gave result: 127.0.0.1
ip.address()
gave result: 192.168.48.1
since my PC is connected to my network by Wi-Fi, my network accessible address, and therefore the address I need to use for capacitor.config.ts
, is 192.168.3.4
, whereas either specifying the interface public
or leaving the function to use it's default (which is also public
) returns the address assigned to my "VMware Network Adapter VMnet1"
, simply because it is the first public network interface with an active connection.
personally, I often switch between both wired and wireless networks, both at home and at work, so in all of my environments checking the Wi-Fi
interface first and Ethernet
interface second "Just Works" 99% of the time.
tl;dr: the only truly "safe" way to handle this, is to prompt the user to select an interface to use at runtime..
with all that said, I'm not familiar enough with Nx executors to set up a flow like this.. which I imagine would go along the lines of:
nx run <app>:run-target-with-live-reload
npx
wants to install a new package..android
or ios
) and the specific emulator/simulator/device ID is provided..the reason I highlight the extra points with the Angular and Capacitor commands here, is that although they can be executed in parallel with nx run-many
or the commands
array in project.json
or even a nx run app:ng-serve && nx run app:cap-run
script in your package.json
, you may end up with both of these processes running in a terminal window that does not listen to user input.
furthermore, even if it does, their output is dumped into the same window, meaning you probably won't even see that npx
is asking for permission to install nx
in order to run the required command, simply because that one line is now 4 screens-worth back in your terminal output.
anyways, this is just a highlight of the issues I've run into over the past 2 weeks while trying to get this working in my Nx workspace. I'm very far from being an Nx-ninja, but the most reliable way I've found to do this (so far), is the two-terminal approach I described at the end of my previous comment.
I use the command:
cd your app directory
run npx ionic capacitor run android --livereload --external --project=your app name
I use the command:
cd your app directory run
npx ionic capacitor run android --livereload --external --project=your app name
ok sure, but take note that the --project
argument is part of Ionic's own "multi-app projects" compatible configuration, as described here: https://ionicframework.com/docs/cli/configuration#multi-app-projects
but this here is where we run into a few potentially sticky points:
most importantly, the apps generated by @nxext/ionic-angular
still contain their own individual ionic.config.json
files, so getting this to work in our repos (considering this is the @nxext
scoped packages workspace) will require a manual configuration change for each generated app.
furthermore, this assumes that you either have NPM workspaces configured in order to take advantage of the root node_modules
folder, or that each application project has it's own node_modules
folder.
personally I have not had much luck with using NPM's new "workspaces" concept in a Nx workspace, not sure why.. but also this is quite probably why the @nxext/capacitor
executors call an npm install
inside the workspace app project at the start of virtually every cap
command, and then remove the node_modules
folder at the end.
tl;dr: trying to use Ionic's own "multi-app workspace" feature for this feels like one giant hack. or at the very least, like it would require a crazy number of smaller hacks in order to "just work".
Is your feature request related to a problem? Please describe. For ionic there are this commands available to run the app with live reload on android or ios:
$ ionic capacitor run ios -l --external $ ionic capacitor run android -l --external
I realy need it for my ionic app inside nxext Workspace. At the moment I always need to run "nx run [app-name]:sync:android" and "nx run [app-name]:open:android" after changes. It takes a lot of time