raycast / extensions

Everything you need to extend Raycast.
https://developers.raycast.com
MIT License
5.23k stars 2.93k forks source link

[Apple Reminders] Getting SwiftError (Could not get any data) when opening My Reminders command #9172

Closed rolandleth closed 7 months ago

rolandleth commented 10 months ago

Extension

https://raycast.com/raycast/apple-reminders

Description

Error:

SwiftError
    at ChildProcess.<anonymous> (/Users/<user>/.config/raycast/extensions/112ad714-bf10-4ccc-a70f-4232bb2241a5/my-reminders.js:50:13280)
    at ChildProcess.emit (node:events:517:28)
    at ChildProcess.emit (node:domain:489:12)
    at maybeClose (node:internal/child_process:1098:16)
    at Socket.<anonymous> (node:internal/child_process:450:11)
    at Socket.emit (node:events:517:28)
    at Socket.emit (node:domain:489:12)
    at Pipe.<anonymous> (node:net:350:12)

Steps To Reproduce

Open My Reminders (privacy permissions granted).

Current Behaviour

Nothing loads, error.

Expected Behaviour

To load.

Extra info

Creating reminders works (although lists do not load in that view).

raycastbot commented 10 months ago

Thank you for opening this issue!

🔔 @tmwrnr @irangarcia you might want to have a look.

💡 Author and Contributors commands The author and contributors of `raycast/apple-reminders` can trigger bot actions by commenting: - `@raycastbot close this issue` Closes the issue. - `@raycastbot rename this issue to "Awesome new title"` Renames the issue. - `@raycastbot reopen this issue` Reopen the issue.
rolandleth commented 9 months ago

I have an extension published too, and I don't always reply in time, but the built-in (old) Reminders function worked and got deprecated in favour of this one and can not be installed anymore (or at least I can't find out how), a decent time to reply would be immensely appreciated, especially for an extension so core like this...

thomaslombart commented 9 months ago

Hey @rolandleth, sorry for the late reply. We'll investigate this bug. Thanks for reporting the issue and for your patience! 😊

rolandleth commented 9 months ago

Hello! Is there anything I could help with? I really miss this extension, I used to use it multiple times per day 🙁

thomaslombart commented 9 months ago

Hey @rolandleth, we've added an update that should make the error more descriptive. Could you test it out again and let me know if you see more than SwiftError?

If that's not the case, could you follow these instructions:

rolandleth commented 8 months ago

Hey, @thomaslombart! Sorry for the late reply.

I just uninstalled and reinstalled it, to make sure it's the latest version, but the error is almost the same. I see no "Show Build in Finder" action command.

I did find the extension on disk and create-reminder.js still contains Could not get any data, even though I just reinstalled it. Uninstalling does delete the folder, so installing again doesn't reuse anything old.

SwiftError: Could not get any data
    at ChildProcess.<anonymous> (/Users/<user>/.config/raycast/extensions/112ad714-bf10-4ccc-a70f-4232bb2241a5/my-reminders.js:50:13560)
    at ChildProcess.emit (node:events:514:28)
    at ChildProcess.emit (node:domain:488:12)
    at maybeClose (node:internal/child_process:1105:16)
    at ChildProcess._handle.onexit (node:internal/child_process:305:5)

Not sure if a screenshot with create-reminder.js helps in any way, but here it is:

2024-01-17 at 11 27 48 AM
ddotlic commented 7 months ago

@thomaslombart Seeing how the "legacy" extension worked just fine and this issue isn't getting any love, would it at least be possible to get the legacy extension back?

PSalant726 commented 7 months ago

Agreed with @ddotlic. This extension is completely broken and there seems to be no timeline for a fix. Quickly adding reminders is one of my primary use cases for Raycast, so re-enabling the legacy extension should be done as a workaround until this extension can be given the attention needed to resolve this issue.

If it helps the debugging process, the error seems to be coming from this component. I don't have enough familiarity with these APIs to suggest a fix, but the least we can do is review the included fields for availability, and wrap that return in an error-catching block.

mathieudutour commented 7 months ago

The extension isn't "completely broken" - it seems that, for a handful of users, the extension can't execute a binary to get the data from Apple's APIs. We haven't been able to reproduce nor find any pattern as to when and why it would fail.

Let's see if we can find some common points between the failing setups:

ddotlic commented 7 months ago

@mathieudutour I agree that framing "completely broken" is likely a personal feeling and not based in hard data (I did not make this statement BTW) but I also suspect that "handful of users" is similarly misguided - as a (professional, making a living of it) developer myself, I occasionally get user-supplied bug reports (large web site with lots of users) which when found turn out to be years old. Why didn't we get any other bug reports about it (again, lots of users, public site, financial industry, not something silly)? I assume because it doesn't hurt the users enough or they don't know how to file a bug report or are too busy or...

But this philosophical point aside, I am extremely happy that you've given us things to try. Here's my report:

  1. I am running latest possible macOS, 14.3.1 on Apple M2 Pro (mac mini)
  2. I am not running any kind of antivirus or such, but this is an excellent point, I've seen issues with overzelous antivirus many times in my career (and have just double checked that on my machine, Acronis Backup's unwanted Protection is completely disabled)
  3. Running ls command produces .rw-r--r--@ 904k draza 12 Feb 14:42 AppleReminders
  4. Cannot run the AppleReminders since it's obviously not an executable

Based on 3 and 4, I manually made the file executable running (while in the plugin folder) chmod 755 AppleReminders which did not help, followed by a more dangerous chmod 777 AppleReminders which also did not help. In both cases, I did quit and reopen Raycast.

If there's anything else I could try, please LMK and I'll gladly do it. Thanks!

ddotlic commented 7 months ago

Forgot to say: now that AppleReminders can be executed, when I do so I get back Not enough Command-Line arguments.

I tried figuring out the arguments from the my-reminders.js but as it's minified, it's very hard to do so.

mathieudutour commented 7 months ago

Running ls command produces .rw-r--r--@ 904k draza 12 Feb 14:42 AppleReminders Cannot run the AppleReminders since it's obviously not an executable

That's interesting! Now that the file is executable, the extension still shows the same error?

The code we use to run the binary is fairly simple:

import { join } from "path";
import { chmod } from "fs/promises";
import { spawn } from "child_process";

async function runSwiftFunction(command, ...args) {
  // the path below is dynamically generated
  const swiftPath = "~/.config/raycast/extensions/112ad714-bf10-4ccc-a70f-4232bb2241a5/assets/compiled_raycast_swift/AppleReminders";
  await chmod(swiftPath, "755");

  return new Promise((resolve, reject) => {
    const commandArgs = [command];
    for (const arg of args) {
      try {
        commandArgs.push(JSON.stringify(arg, (k, v) => v === undefined ? null : v));
      } catch (err) {
        reject(new SwiftError("Failed to serialize input to JSON: " + err.message));
        return;
      }
    }
    const child = spawn(swiftPath, commandArgs);
    const stdout = [];
    const stderr = [];

    child.stdout?.on("data", (data) => {
      stdout.push(data.toString());
    });
    child.stderr?.on("data", (data) => {
      stderr.push(data.toString());
    });

    child.on("exit", (code) => {
      if (code === 0) {
        try {
          const result = stdout.join("").trim();
          if (result.length != 0) {
            resolve(JSON.parse(result));
          } else {
            resolve(null);
          }
        } catch (err) {
          const error = new SwiftError("Failed to deserialize result from JSON: " + err.message);
          error.stdout = stdout.join("").trim();
          error.stderr = stderr.join("").trim();
          reject(error);
        }
      } else {
        const error = new SwiftError(stderr.join("").trim() || stdout.join("").trim() || "Could not get any data");
        error.stdout = stdout.join("").trim();
        error.stderr = stderr.join("").trim();
        reject(error);
      }
    });

    child.on("error", (error) => {
      reject(error);
    });
  });
}

export class SwiftError extends Error {
  constructor(message) {
    super(message);
    this.name = "SwiftError";
  }
}
ddotlic commented 7 months ago

That's interesting! Now that the file is executable, the extension still shows the same error?

@mathieudutour Yes, still the same error. I noticed the chmod in the script, this obviously did nothing prior.

What are the arguments sent to this, when one wants to list reminders? I'd like to try executing this manually.

mathieudutour commented 7 months ago

I noticed the chmod in the script, this obviously did nothing prior.

Yes this is weird indeed

~/.config/raycast/extensions/112ad714-bf10-4ccc-a70f-4232bb2241a5/assets/compiled_raycast_swift/AppleReminders getReminders should return a big JSON

ddotlic commented 7 months ago

should return a big JSON

It returns zsh: trace trap ./AppleReminders getReminders

I ran this from iTerm (latest) where it also has access to Reminders. Tried again from Apple's own Terminal same thing.

mathieudutour commented 7 months ago

Oh 👀

Could you try running the Color Picker extension? Do you get the same issue?

ddotlic commented 7 months ago

Could you try running the Color Picker extension? Do you get the same issue?

Not only does this extension work just fine, but I was not aware of it yet I need that kind of functionality fairly often, thanks for pointing it out! 🎉

I guess this is technically not good news WRT Reminders extension, is there anything else I could try?

mathieudutour commented 7 months ago

So it means that it's not something related to the swift/js bridge, but something related to the swift code itself. I'll see what we can do - but that was already a big help, we made some progress, thanks!

ddotlic commented 7 months ago

@mathieudutour I am not familiar enough with Swift nor native development on a mac, though I've done decades of native development on Windows - I presume you can "catch an exception" (seems that this isn't strictly the correct term, but close enough) during the execution and then trace the details to the console? You can send me the executable without the need to publish the new version of the plugin.

Alternatively, if the source is accessible, I can try to run this in debug mode from XCode? Again, haven't done much in this regard, but "how hard could it be" 😉

mathieudutour commented 7 months ago

The source is indeed accessible here: https://github.com/raycast/extensions/tree/main/extensions/apple-reminders/swift/AppleReminders

thomaslombart commented 7 months ago

@ddotlic, do other commands fail for you, like Create Reminder? @rolandleth reported that lists couldn't be loaded in this command.

ddotlic commented 7 months ago

@thomaslombart So, the Create Reminder technically works, but the lists picker is empty, where I actually have one list and put all my reminders to that list.

ddotlic commented 7 months ago

OK, so I was bored and tried the good old print debugging - the Swift code works fine up to the line

remindersData = reminders.map { $0.toStruct() }

where it crashes. I tried using try/catch but my Swift skills are pathetic, so I got nothing.

The code is able to get necessary permission and the app does see all of my (mostly already completed) reminders (1563 of them to be exact).

LMK if I can try something else.

Since the build is "obfuscated" by the use of wrappers over wrappers etc (not judging), I cannot figure out (yet) how to build the Swift part in debug mode.

thomaslombart commented 7 months ago

You can find the toStruct function of the EKReminder extension in the EventKit+Additions.swift file. I'm wondering if it's not the toStruct function of the EKCalendar extension that's bugging. Could you comment this line and let me know if:

remindersData = reminders.map { $0.toStruct() }

still crashes?

Otherwise, you could try to set up some breakpoints in the code to see if it's ever reached and see the content of the different variables:

https://github.com/raycast/extensions/assets/16003285/a25e70ac-006f-4b55-8950-2ab8c0e9c043

ddotlic commented 7 months ago

@thomaslombart Thanks for your patience. I am actually debugging like an idiot, by sprinkling print statements over the codebase.

Unfortunately, when I comment out the line you pointed at, the build fails. Since I'm running the build from the command line using npm run build what I get back is fairly useless (no details about the error).

How did you open just AppleReminders in XCode?

thomaslombart commented 7 months ago

No worries! 😄

Opening extensions/apple-reminders/swift/AppleReminders from Xcode should be enough.

https://github.com/raycast/extensions/assets/16003285/e0976043-d659-4bdb-9bcb-ffbb1da26eb1

ddotlic commented 7 months ago

@thomaslombart Duh! OK, now we're "cooking with gas" or as we Europeans should probably say "cooking with induction" 😛

I can now debug normally - the error I had previously was when commenting out the line you indicated, I commented out the closing parenthesis 🤦‍♂️. Btw, that by itself does nothing.

The issue is index out of bounds at the most unexpected place, month index is 12 at some point, I need to debug this more to tell you more, but we're getting somewhere!

ddotlic commented 7 months ago

@thomaslombart This appears to be a classic off-by-one: the Apple docs for the monthsOfTheYear state that values range from 1 to 12, where the code assumes array index, 0 to 11.

The offending line is this one.

In theory, using monthSymbols?[$0.intValue - 1] should do the trick: indeed it does, after this change all my reminders are printed as a large JSON string.

ddotlic commented 7 months ago

I patched my local copy and everything works fine now, including the fetching of the default list in the Create Reminder UI.

If possible, it would likely speed things up to filter out completed tasks, since they don't seem to be shown anywhere in Raycast's UI (nor would I ever expect them to, personally).

thomaslombart commented 7 months ago

Oh, that's interesting. So, it means it's crashing for some recurring reminders? Could you share some of your reminders so that I can reproduce it as well?

As for the completed reminders, they're loaded upfront so that you can display them if you want (as shown below). Does it have a noticeable performance impact if you filter them out?

apple-reminders 2024-02-14 at 14 34 35

Anyway, thanks a lot for investigating this. It's really helpful! 🙌

ddotlic commented 7 months ago

Could you share some of your reminders so that I can reproduce it as well?

@thomaslombart I am not sure how I would go about "sharing my own reminders" but this particular one is just a yearly reminder, every 4th Friday of December (details unimportant).

I thought my comments were clear - the code assumes 0 based month indices, where the Apple uses 1 based month indices, you must have seen this kind of misunderstanding a million times, no?

My reminder being for December, its "index" is 12, out of bounds of a typical month array with 12 entries, last one having an index of 11. I pointed at the line exactly in my comment above and provided the fix - just subtract one.

thomaslombart commented 7 months ago

I took a deeper look at that and if you run it locally, you get a much more helpful error:

SwiftError: Swift/ContiguousArrayBuffer.swift:600: Fatal error: Index out of range

Instead of just:

SwiftError: Could not get any data

So there's two issues here. The first one is the Apple Reminders one that I'll fix in the following hours thanks to you. The second one has to do with how we're sending the errors in production. We'll follow up on that so that if we have other errors in the future, it'll be way easier to identify the cause.

Anyway, thanks again for your help. I'll ship the PR soon 🙌

rolandleth commented 6 months ago

Awesome, can confirm it now works, thank you!