Jump to content

AlfredWorkflowScriptFilter (Swift)


Recommended Posts

to create Alfred ScriptFilter results in Swift.

 

the code: https://github.com/godbout/AlfredWorkflowScriptFilter

 

if you care about the why, a bad blog post: https://sleeplessmind.com.mo/news/alfred-workflow-scriptfilter-another-one/

 

it's probably missing some stuff as it's built mainly for my current needs, so don't hesitate to bitch and i'll see what i can do!

Link to post

FWIW, if/when Apple remove the interpreters from macOS, Andrew will probably add the ability to install them via Alfred.

 

Quote

on any supported macOS (Yosemite and above)

 

I'm not sure exactly what you mean by this, but you can't build a Swift workflow that will run on Yosemite with a recent version of macOS.

 

The problem is that older versions of macOS don't have the Swift standard libraries installed and the recent ones that do no longer support statically compiling the stdlib into the binary. So if you want to build a Swift workflow that will run on Yosemite, you'll have to build it with an older version of macOS.

 

IIRC, Mojave/Swift 5 is the cutoff. To build a Swift binary that will run on older systems, you need to build it with Swift 4 using -static-stdlib. That flag is no longer supported in Swift 5.

Edited by deanishe
Link to post

hey deanishe, thanks for taking the time!

 

2 hours ago, deanishe said:

FWIW, if/when Apple remove the interpreters from macOS, Andrew will probably add the ability to install them via Alfred.

 

this is what i got from Andrew end of January this year (i hope he doesn't mind me sharing our secret love affair): "Alfred won't ever be bundling binaries for languages such as php, so Alfred users will be using the built-in version of php in macOS sourced from /usr/bin/php, or will need to install custom runtimes." i took this as users will have to install their own interpreters if macOS doesn't provide. i might be wrong though? (anyways, not the primary reason why i went for Swift.)

 

2 hours ago, deanishe said:

I'm not sure exactly what you mean by this, but you can't build a Swift workflow that will run on Yosemite with a recent version of macOS.

 

The problem is that older versions of macOS don't have the Swift standard libraries installed and the recent ones that do no longer support statically compiling the stdlib into the binary. So if you want to build a Swift workflow that will run on Yosemite, you'll have to build it with an older version of macOS.

 

IIRC, Mojave/Swift 5 is the cutoff. To build a Swift binary that will run on older systems, you need to build it with Swift 4 using -static-stdlib. That flag is no longer supported in Swift 5.

 

ah. thanks for catching this. i think both my phrasing and my understanding are wrong. my line of thought is: i started the library with no minimum requirement, then along the way i've used some APIs that were only available starting with Yosemite. so Yosemite become the lowest requirement. i expect (still) people to be able to use the library under Yosemite, and build a Workflow with it. now where my phrasing is misleading is using the library/building the Workflow vs using the Workflow. 

 

am i getting this correctly?

hadn't touched compiled stuff for 20 years. way harder than interpreted. too much to my taste 😅

Link to post

if my above understanding is correct, the Alfred Workflow ScriptFilter library supporting up to some OS and the Workflow built with it supporting up to some OS are two different things. the library supports up (down?) to Yosemite, but for the Workflow itself it will depend on what the developers want/do themselves.

Link to post
40 minutes ago, godbout said:

Alfred won't ever be bundling binaries for languages such as php

 

No, not bundling. But he has said in the past that Alfred might offer assistance installing (some of) them.

 

34 minutes ago, godbout said:

the Alfred Workflow ScriptFilter library supporting up to some OS and the Workflow built with it supporting up to some OS are two different things

 

They are. But your blog post says:

 

Quote

install the Workflows built with the Swift Alfred Workflow ScriptFilter on any supported macOS (Yosemite and above), and they will run instantly.

 

That sounds like you can build a workflow that will run on any version of macOS from Yosemite to Big Sur, but that's only true if you statically compile it on an old, unsupported version of macOS (High Sierra or older). That's a pretty important caveat that deserves mentioning, imo.

 

Link to post
1 hour ago, deanishe said:

That sounds like you can build a workflow that will run on any version of macOS from Yosemite to Big Sur, but that's only true if you statically compile it on an old, unsupported version of macOS (High Sierra or older). That's a pretty important caveat that deserves mentioning, imo.

 

yes, definitely fair. will remove for now and hopefully find a better phrasing tomorrow now that you've helped clarify my understanding. another day when i'll go to sleep less stupid than when i woke up. wondering when this is gonna stop though...

Link to post
1 minute ago, godbout said:

wondering when this is gonna stop though...

 

When you get Alzheimer's :)

 

BTW, what's your experience been using Swift for workflows? I tried to write a workflow in Swift a few weeks ago, but I think I picked something Swift is totally unsuited for because the performance was appalling (10x slower than Go).

Link to post

ah, Alzheimer's. not remembering that you can't remember. sounds like the perfect recipe for happiness to me 😂

 

well, not sure my "experience" is currently valuable to you. i was impressed by your SSH Workflow and the speed of what shows in the items subtitle while you're typing, so i knew sooner or later i'd have to switch to some compiled language, at least for some Workflows. personally i wanted to learn Swift for some other macOS software so that's a win. not having to force the user install PHP is another win. the speed compared to PHP is one more win, although in the case of Alfred KAT—the only one in Swift i have for now (next is gonna be same, TPB)—it doesn't reflect much as i need a delay and then it's just pulling pages from the web. so honestly for now it's all win, but if there's performances issues later that might be a problem, yes. i made some tests with the user input and i can still press enter too fast and crop the last letter. but it happens also on your SSH Workflow, although not as frequently i think.

 

i'm curious, why bothering with Swift if you're already building stuff in Go?

Link to post
16 minutes ago, deanishe said:

(10x slower than Go).

 

thinking about it, that's probably due to the fact that you were not using this fantastic new Alfred Workflow ScriptFilter library 🙄

Link to post
27 minutes ago, godbout said:

i'm curious, why bothering with Swift if you're already building stuff in Go?

 

I wanted more practice with the language. Go is awful for GUIs or interacting with macOS native APIs.

 

28 minutes ago, godbout said:

that's probably due to the fact that you were not using this fantastic new Alfred Workflow ScriptFilter library

 

:D I didn't even get as far as building the workflow bits. The workflow needs to parse a 1.5MB text file, and Swift is very poor at string processing. Swift’s String is a massive, slow object, while Go’s strings are just sequences of bytes.

 

Link to post
10 hours ago, deanishe said:

I wanted more practice with the language. Go is awful for GUIs or interacting with macOS native APIs.

 

i like the language. you know it's new. it's like they took good practices from software development and baked them in. like the guard statements for returning early/happy paths. i like the named parameters too. i like to write APIs like i talk, and that helps a lot. the only thing i've struggled with (but it's a compiled VS interpreted thing) is the lack of dynamic(?) flexibility. like in PHP i can do crazy stuff like calling dynamically properties or methods and it takes two lines of code. in Swift everything needs to be stated clearly. currently it makes me feel that the code is extra verbose, not clean. but i haven't checked yet `KeyPaths` and other stuff that should help with that.

 

10 hours ago, deanishe said:

:D I didn't even get as far as building the workflow bits. The workflow needs to parse a 1.5MB text file, and Swift is very poor at string processing. Swift’s String is a massive, slow object, while Go’s strings are just sequences of bytes.

 

man. you sure do know your stuff 😅️ i usually find the Swift APIs nice to work with, but i have no idea about performance. i was assuming it was good, actually.

Link to post
2 hours ago, godbout said:

like in PHP i can do crazy stuff like calling dynamically properties or methods

 

You can do that in ObjC/Swift, too (the object system is based on Smalltalk, after all). It’s just generally bad practice. When you start messing around with runtime introspection or using id to circumvent the type system, you destroy the performance and safety a statically-typed, compiled language gives you.

 

2 hours ago, godbout said:

in Swift everything needs to be stated clearly.

 

That's the nature of static typing, tbh. You have to spell everything out for the compiler, and in return, it eliminates an entire class of runtime errors for you. When you change the type of a variable, it's impossible to forget to update your code in one place or another (a super-common error in dynamically-typed languages) because your code won't compile. That's why it's considered poor practice to circumvent type checking with runtime shenanigans unless absolutely necessary.

 

3 hours ago, godbout said:

you sure do know your stuff 😅

 

I make so many mistakes I can learn from…

 

3 hours ago, godbout said:

i was assuming it was good, actually.

 

It is and it isn't. It's a fairly standard statically-typed, compiled language, but with Smalltalk's extremely dynamic object model and messaging system grafted on top. That adds a lot of overhead and idiomatic Swift makes pretty heavy use of it. Big classes, lots of copying, dynamic dispatch.

 

To get good performance, you need to do non-obvious things. To get really good performance, you have to basically treat Swift like C and use UnsafePointer and pals and primitive types (e.g. a  UInt8 instead of large objects like Character).

Link to post
1 hour ago, deanishe said:

You can do that in ObjC/Swift, too (the object system is based on Smalltalk, after all). It’s just generally bad practice. When you start messing around with runtime introspection or using id to circumvent the type system, you destroy the performance and safety a statically-typed, compiled language gives you.

 

makes sense.

 

1 hour ago, deanishe said:

When you change the type of a variable, it's impossible to forget to update your code in one place or another (a super-common error in dynamically-typed languages) because your code won't compile.

 

i do like this. it's just i think a matter of habit and of not knowing the language well enough yet. i like to have methods with only a few lines of code in them, but with Swift it's usually hard to do. might improve, or i might get used to it.

 

1 hour ago, deanishe said:

It is and it isn't. It's a fairly standard statically-typed, compiled language, but with Smalltalk's extremely dynamic object model and messaging system grafted on top. That adds a lot of overhead and idiomatic Swift makes pretty heavy use of it. Big classes, lots of copying, dynamic dispatch.

 

To get good performance, you need to do non-obvious things. To get really good performance, you have to basically treat Swift like C and use UnsafePointer and pals and primitive types (e.g. a  UInt8 instead of large objects like Character).

 

do you think it's still the case with Swift 5? i thought `struct`s were there especially to handle performance issues. also there's stuff like `Substring`s that are a special type rather than `String`s themselves so that they get copied only when necessary. 

 

well it's always the same compromise right? or you stick to low level and it's performant but the APIs are usually hardcore, or you get a nice abstraction where you almost just type English but it comes with performance issues. personally when i build something (for other developers) i like to put priority on a nice API. then, for the performance, i can always figure things out later and refactor the internal organs. (hence the need for me of a good test suite.)

Link to post
1 hour ago, godbout said:

i thought `struct`s were there especially to handle performance issues

 

They're immutable and not subclassable, so they aren't dragging all that machinery around with them, but on the other hand, structs are always passed by value, not reference, so a lot of copying goes on.

 

1 hour ago, godbout said:

stuff like `Substring`s that are a special type rather than `String`s themselves so that they get copied only when necessary.

 

I don't really understand how substrings, ranges, indexes work in Swift yet.

 

1 hour ago, godbout said:

well it's always the same compromise right?

 

Absolutely. Idiomatic Swift is pretty high level. That convenience always has a performance cost, AFAIK.

 

1 hour ago, godbout said:

i like to put priority on a nice API. then, for the performance, i can always figure things out later and refactor the internal organs.

 

This is the way.

 

1 hour ago, godbout said:

ok i'm depressed now. i'm gonna give up Swift and will start developing my Workflows with this library: https://github.com/deanishe/awgo

 

:D Swift is a far more interesting language than Go. I plan to use it more in future, just not for serious string processing.

 

Edited by deanishe
Link to post
5 hours ago, deanishe said:

They're immutable and not subclassable, so they aren't dragging all that machinery around with them, but on the other hand, structs are always passed by value, not reference, so a lot of copying goes on.

 

if i remember/understood correctly i think they're getting copied only when necessary. so it's supposed to be Ultra Performant™. but of course as usual i might be completely wrong.

 

5 hours ago, deanishe said:

I don't really understand how substrings, ranges, indexes work in Swift yet.

 

at first i was dafuq, i want some part of a string and i can't even use that shit as a string itself??? but you actually just need to init a String with the substring like String(yourSubtring). the idea is as long as you don't need the substring, it takes no space. it's just a reference to the lower and upper bounds. after the initial dafuq i was like, nice, pretty smart. (as you can see by now i don't need much to find something smart.)

 

5 hours ago, deanishe said:

:D Swift is a far more interesting language than Go. I plan to use it more in future, just not for serious string processing.

 

nice. looking forward to see where this is gonna lead you. should be quite interesting.

Link to post
9 minutes ago, godbout said:

i think they're getting copied only when necessary.

 

A quick googling says you're right. It looks like Swift uses copy-on-write semantics, so a struct would get only copied when you mutate it.

 

35 minutes ago, godbout said:

i was like, nice, pretty smart.

 

Makes a lot of sense with Strings being so expensive to create.

 

40 minutes ago, godbout said:

looking forward to see where this is gonna lead you.

 

Low-quality GUI apps, most likely. What have you got in mind?

 

Link to post
Posted (edited)
13 hours ago, deanishe said:

A quick googling says you're right.

 

sorry what does `googling` mean?

 

13 hours ago, deanishe said:

Makes a lot of sense with Strings being so expensive to create.

 

i'd be surprised if Apple and the bunch of people working on Swift would let it suck though? so i'd foresee more and more improvements. but maybe it's just wishful thinking. i've joined the bandwagon at a better time than you also. seems you were in when everything would be still slow and break at every update. i joined at 5.3, so things seem already quite stable and i don't see (yet) any performance bottlenecks.

 

13 hours ago, deanishe said:

Low-quality GUI apps, most likely. What have you got in mind?

 

for myself? in the long term it's just kindavim.app. want to get Vim binding for the whole macOS, and provide this as a nice little app for US$1 a month. that's why i went for Swift. i have something working already that i use daily, but as usual i've built it only for my own needs first. the idea was to release it, and if there's some traction and people request other moves, i'll add them. the issue is that currently it's not properly build, and no test suite, so adding new moves tend to break old ones. wanted to get more knowledge and practice and with the macOS interpreter thing + slow PHP Workflows, decided to learn by doing some stuff for Alfred. i guess in the short/middle term it's going to be building stuff for Alfred. maybe also some small, very specific stuff later for my own needs. i like to build small, useful stuff, that do only one thing but do it greatly. with being tested properly so that i can keep them updated without much hassle. let's see where this will land. utopically i hope to be the next Steve Bobs.

Edited by godbout
Link to post
17 minutes ago, godbout said:

sorry what does `googling` mean?

 

Searching on Google.

 

18 minutes ago, godbout said:

i'd be surprised if Apple and the bunch of people working on Swift would let it suck though?

 

It doesn't suck. It's just not designed or suited for what I was trying to do with it.

 

35 minutes ago, godbout said:

seems you were in when everything would be still slow and break at every update.

 

That's a big part of why I haven't used Swift more seriously. I've written a few things in Swift over the years, and it seems that every time I open an old project, the whole bloody language has changed and the small edit I wanted to make turns into a significant rewrite.

 

Do you think Swift 5 is going to be any more stable?

 

49 minutes ago, godbout said:

want to get Vim binding for the whole macOS

 

What does that do? (I only know word/line-navigation bindings, which I presume are not the bindings you're talking about.)

 

Link to post
54 minutes ago, deanishe said:

Searching on Google.

 

was wondering until where i could push my bad humor. i guess limit reached 😂😂😂

 

56 minutes ago, deanishe said:

That's a big part of why I haven't used Swift more seriously. I've written a few things in Swift over the years, and it seems that every time I open an old project, the whole bloody language has changed and the small edit I wanted to make turns into a significant rewrite.

 

Do you think Swift 5 is going to be any more stable?

 

yeah i remember that. well, as usual, take my understanding with a grain of salt, but previous every year they would release a new major version, that means they'd break APIs for sure. seems that for the last recent years they mainly add new features. 5.4 should bring some asynchronous shits. so i'd say yes, it should be more stable. (you can quote me next time they break your projects.)

 

58 minutes ago, deanishe said:

What does that do? (I only know word/line-navigation bindings, which I presume are not the bindings you're talking about.)

 

actually it is. for developers that are used to Vim motions and commands in their favorite text editors, they can use the same bindings in all input fields on macOS, i.e. when they use an app, when they type bullshit on an Alfred forum, etc. at least this is how it started. now rather than purely making the bindings available to the macOS input fields only, i'm kinda tweaking it more, i don't know, conceptually? abstractly? like you usually use `j` and `k` do go up and down in your text editor. now you can use `j` and `k` to go up and down dropdowns, "lists", etc... macOS comes with the Emacs binding, so you can use Ctrl+n/Ctrl+p to go up and down rather than the arrow keys, but it doesn't work in a lot of apps, including Apple's own (like Xcode choose project dialog). with kindavim.app you can just go up and down everywhere without ever using the arrow keys and mainly keep your fingers on the home key. there's more to it but mainly that's the idea. i know at least one person very interested in this (me).  

Link to post
2 hours ago, godbout said:

now rather than purely making the bindings available to the macOS input fields only

 

How does that work? I've used some Vim-bindings extensions in browsers, but I'm having a hard time imagining how a global "command mode" would work.

 

Link to post

Interesting discussion. Quite interested to try out the app @godboutfrom the description it reminded me of https://shortcatapp.com

 

I have global vim bindings setup myself but with karabiner. s + hjkl (arrow keys), s +w (select current word), s+v (visually select, it stacks so s+v+l will do essentially shift + right arrow key). And so on.

 

Here is config file for s key defined in Karabiner/Goku https://github.com/nikitavoloboev/dotfiles/blob/eb7e89f05633e37b07e149a08a2de542ddcabf1a/karabiner/karabiner.edn#L444

 

On Swift side, one thing that is so annoying to me is that Xcode still doesn't have proper vim mode :( And Swift LSP is immature to keep using VSCode for it. Hoping this WWDC will introduce proper plugin development for Xcode.

Edited by nikivi
Link to post
Posted (edited)
4 hours ago, deanishe said:

How does that work? I've used some Vim-bindings extensions in browsers, but I'm having a hard time imagining how a global "command mode" would work.

 

actually it's pretty straightforward. there's two ways to do it. one is by using the Accessibility API and gather inputs' contents. that should allow for a more precise experience, but i haven't explored this yet. the way i did it is by creating a tap event. basically you insert a tap in the event stream and grab and redirect some of them (keyboard ones in my case). it was not hard to do, but (at the time at least) hard to test. haven't gone back to it yet. to enter the global command mode, it's just a keyboard shortcut. (personally i tried to use something related to escape, like a long press for command mode and normal press for normal escape or the opposite, but nothing felt right. so i use cmd+escape now (although it's more cmd+capslock; capslock mapped to escape)). probably the final app will be a mix of both methods. for text, it'll use the Accessibility API, to move in dropdowns, menus, etc..., a remapping of the keyboard.

Edited by godbout
corrected a lot of... typos
Link to post
2 hours ago, nikivi said:

Interesting discussion. Quite interested to try out the app @godboutfrom the description it reminded me of https://shortcatapp.com

 

i could put a stuff online later, but it's pretty barbaric. like there's a useless window that you're gonna have to hide, and the shortcut is set to cmd+escape and that's it. but there's quite a few moves working. yeah i tried `shortcat`, the idea is very nice. but when i tried it it kept crashing. i haven't been able to use it at all. i think i read later that it was because you can't use it on itself. might have been the issue, but haven't tried again.

 

i use Vimac currently. different style, but still gets rid of the mouse: https://github.com/dexterleng/vimac

 

2 hours ago, nikivi said:

I have global vim bindings setup myself but with karabiner. s + hjkl (arrow keys), s +w (select current word), s+v (visually select, it stacks so s+v+l will do essentially shift + right arrow key). And so on.

 

yes i tried Karabiner also and used it for a while. but the main moves were never enough. and in some cases there was delay in the key presses. like in Karabiner i couldn't find an easy way to have stuff like "ciw" for example.

 

2 hours ago, nikivi said:

On Swift side, one thing that is so annoying to me is that Xcode still doesn't have proper vim mode :( And Swift LSP is immature to keep using VSCode for it. Hoping this WWDC will introduce proper plugin development for Xcode.

 

have you tried XVim2? https://github.com/XVimProject/XVim2

you need a little trick to make it work, but it works. although not all the moves are there. also you may need another clean Xcode (that you didn't resigned) to push to the App Store, or do some other stuff that require your Developer ID etc. if i could spend all my time in ST i would do it, but the autocompletion in Xcode for, well, that static Swift thing is pretty good and useful.

Link to post

you need a little trick to make it work

 

Yeah having to unsign Xcode and stuff through me off but happy to hear it works nicely. I read through GitHub issues that said otherwise. 

Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...