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

Improve a flow of loading devices and xcode detection #563

Merged
merged 12 commits into from
Sep 24, 2024

Conversation

filip131311
Copy link
Collaborator

@filip131311 filip131311 commented Sep 19, 2024

this PR addresses a few issues with the loading devices flow:

  • checkIfCLIInstalled no longer logs errors when returning false instead just logs with debug level, this change makes the logs more alined with expected results for setups without xcode
  • initial device load is performed with force, as xcode installation could change between session of the ide
  • initial loading screen no longer "jumps" between states
  • removed DYLD_FRAMEWORK_PATH as it is no longer needed
  • trySelectingInitialDevice() creates "devicesChanged" listeners before selectingInitial device is complete, so it catches cases in which lastDeviceId points to the device that no longer exists (e.g. because of xcode update)

Copy link

vercel bot commented Sep 19, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
react-native-ide ✅ Ready (Inspect) Visit Preview 💬 Add feedback Sep 24, 2024 9:47am

@filip131311 filip131311 changed the title Improve a flow of loading devices Improve a flow of loading devices and xcode detection Sep 19, 2024
@@ -428,9 +425,12 @@ export class DependencyManager implements Disposable {
export async function checkIfCLIInstalled(cmd: string, options: Record<string, unknown> = {}) {
try {
// We are not checking the stderr here, because some of the CLIs put the warnings there.
const { stdout } = await command(cmd, { encoding: "utf8", ...options });
return !!stdout.length;
const { stdout } = await command(cmd, { encoding: "utf8", quiet: true, ...options });
Copy link
Member

Choose a reason for hiding this comment

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

quiet name needs to change eventually to something like printStdErr, I'd assume if quiet is true we only get status code on exit.

const { stdout } = await command(cmd, { encoding: "utf8", ...options });
return !!stdout.length;
const { stdout } = await command(cmd, { encoding: "utf8", quiet: true, ...options });
const result = !!stdout.length;
Copy link
Member

Choose a reason for hiding this comment

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

Please be more explicit:

const hasOutput = stdout.lenght > 0;

return !!stdout.length;
const { stdout } = await command(cmd, { encoding: "utf8", quiet: true, ...options });
const result = !!stdout.length;
Logger.debug(`CLI: ${cmd} ${result ? "" : "not"} installed `);
Copy link
Member

Choose a reason for hiding this comment

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

Nit: "CLI: " prefix is unnecessary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think it is useful to have prefixes that allow to serach what part of our code generated the log. We have. a growing code base and most of that log is generated dynamically so searching for it would be hard without it.

@@ -125,11 +128,11 @@ export class DeviceManager implements DeviceManagerInterface {
return getAvailableIosRuntimes();
}

public async listAllDevices() {
public async listAllDevices(force?: boolean) {
Copy link
Member

Choose a reason for hiding this comment

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

Maybe forceReload?

{devicesNotFound ? <DevicesNotFoundView /> : <VSCodeProgressRing />}
</div>
<LoadingComponent
finishedInitialLoad={finishedInitialLoad}
Copy link
Member

Choose a reason for hiding this comment

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

Why we're checking for finishedInitialLoad twice in the ternary and in the component?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

there is more then one reason for rendering the loading component, but not all of them behave the same.

})
: Promise.resolve([]);

const shouldLoadSimulators = Platform.OS === "macos" && (await checkXcodeExists());
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we can add additional log here when checkXcodeExists fails. For example "Couldn't list iOS simulators as XCode installation wasn't found" – this will allow us to track issues in case those checks aren't accurate

@@ -26,6 +26,7 @@ const ProjectContext = createContext<ProjectContextProps>({
longitude: 19.965474,
isDisabled: false,
},
locale: "en_US",
Copy link
Member

Choose a reason for hiding this comment

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

why is this change here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It was missed before and ts was not happy, it does not effect the webview much at all as it never uses default context.

const devices = extensionContext.globalState.get(DEVICE_LIST_CACHE_KEY) as
| DeviceInfo[]
| undefined;
if (devices) {
if (devices && !forceReload) {
Copy link
Member

Choose a reason for hiding this comment

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

this isn't accurate as we always load the devices. The difference is that we return the updated list or let it update asynchronously. I'm a bit hesitant about this change as from what I remember, we added device caching as it was slowing down the initial startup significantly. As it isn't documented in PR description I wonder why you're changing this behavior?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

When user had an iPhone selected when they last used IDE, but something happend to xcode (e.g. user started using beta but not updated the xcode-select) application would select an iPhone anyway as it is the most recent device and would get stuck on loading screen.

What dou you think about forceLoad instead of forceReload

Copy link
Member

Choose a reason for hiding this comment

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

I think we should optimize for the most common case in which people don't change their xcode location. If there are cases when it happens, I understand they will get an error when trying to load the simulator, and by that time we will have the list refetched. If that's not the case we should adjust the logic such that the worst thing that can happen if you mess up the simjulator cache or xcode is that you get an error loading the simulator instead of slowing down all the time to interact for everyone else.

Copy link
Member

@kmagiera kmagiera left a comment

Choose a reason for hiding this comment

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

Looks mostly good, left a few comments inline


let shouldLoadSimulators = Platform.OS === "macos";

if (!(await checkXcodeExists())) {
Copy link
Member

Choose a reason for hiding this comment

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

do we even want to run those checks if we're not on a Mac?

Perhaps doing it this way would be cleaner:

const shouldLoadSimulators = Platform.OS === "macos" && (await checkXcodeExists());

Then you can move the log to be printed in checkXcodeExists

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I did this in this way to address your previous comment about adding logging in !checkXcodeExist() scennario

Comment on lines 203 to 217
const selectInitialDevicePromise = selectInitialDevice(devices);

const listener = async (newDevices: DeviceInfo[]) => {
if (await selectInitialDevicePromise) {
this.deviceManager.removeListener("devicesChanged", listener);
return;
}
if (await selectInitialDevice(newDevices)) {
this.deviceManager.removeListener("devicesChanged", listener);
}
};
this.deviceManager.addListener("devicesChanged", listener);

if (await selectInitialDevicePromise) {
this.deviceManager.removeListener("devicesChanged", listener);
Copy link
Member

Choose a reason for hiding this comment

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

This code doesn't do the same thing as the one on the left. In general I'm most afraid of the changes in awaiting that you're adding here. Not saying we shouldn't have them, but they require some explanation in the PR description, such that we can understand why we're changing the await flow.

This part is the most complex change and it introduces some unusual pattern (that we, albeit, use in other places but typically with some explanation) where we create promise and then await inside of an arrow function.

I think that given this method already awaits selectDevice (which wasn't the case before this change) , we can just add the listener before calling selectInitialDevice ?

Copy link
Member

@kmagiera kmagiera left a comment

Choose a reason for hiding this comment

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

lgtm 👍

@filip131311 filip131311 merged commit 028091e into main Sep 24, 2024
3 checks passed
@filip131311 filip131311 deleted the @Filip131311/ImproveAFlowOfLoadingDevices branch September 24, 2024 09:55
@filip131311 filip131311 mentioned this pull request Sep 24, 2024
filip131311 added a commit that referenced this pull request Sep 24, 2024
This PR fixes a mistake introduced in #563, the return value of
`selectDevice` should indicate if device was chosen successfully, not if
processes on it are successful otherwise if build fails and the selected
device was set by `trySelectingInitiallDevice` we are getting stuck in
infinite loop.
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.

3 participants