Early this year, I shared my setup to get tab numbers in the form of Unicode characters like “⓪”. This week, I reduced the font size in the tab bar to 80% of the main font and make the tab bar stand out less in general. The numbers in circled didn’t look so great then anymore, and the baseline looked off.
In the previous post I showed some settings to manage a tab dedicated to Org Agenda and its related org files. I also mentioned that I didn’t like how that tab was actually handled, e.g. when I wanted to break out into other tabs.
I recently went to CocoaHeads Aachen and was asked if I happened to have a topic to talk about. Of course I did :) Designing plugins for The Archive took up a lot of my time this year. So this presentation is about the basic design of the plugin system as registered inputs/outputs.
Me: How can you wrap NSTextView events to go through a ReSwift store? ChatGPT: It’s not clear what you’re asking. Could you please provide more information or context? I’m a large language model trained by OpenAI, so I don’t have the ability to browse the internet or access information outside of what I was trained on. I can provide general information and advice based on that training, but I may not be able to answer specific questions about events in NSTextView or ReSwift stores.
Emacs users sooner or later disable all the GUI stuff, and usually also get rid of system alerts and file pickers. But if you just once want to pick a file using your operating system’s file picker, these three temporary variable overrides will do: If you’re in this situation, you likely already know use-dialog-box and use-file-dialog because you’ve set both to nil globally.
I may be 7 years late or so – but until last week, I didn’t realize that didSet property observers would fire when the observed property hasn’t actually changed. All you need is a mutating func that doesn’t even need to mutate. This can be illustrated with a simple piece of code:
I just wanted to write one short post. Then I discovered that I didn’t yet have a wiki page for a central concept. Then I needed another one. And an hour later, here we are. New wiki pages: And a new library entry:
Emacs’s outline-mode only has font settings aka “faces” for 8 outline levels; then they wrap, so level 9 looks like level 1 and so on. org-mode inherits these faces, and thus also only defines 8 styles. That’s more than enough, I believe, to have some visual variety in your outlines and also sufficient distance between repeating styles.
I discovered this piece of news by accident, and I want to share this with you because I believe this is a very interesting case, providing insight into open source projects, even those that are wildly popular. Gitea is an open source “git forge” (think: GitHub; or rather GitLab, because you can self-host Gitea), itself a fork of Gogs, which also still exists, but with 1/3 of the contributors. Its Open Collective budget reveals that the project raised US$ 35k in total and has an annual estimated budget of 14k. That’s not nothing, but it’s also not financing a full-time maintainer.
NSTableView comes with a couple of default shortcuts in an override of its keyDown(with:) method. These aren’t mentioned anywhere, so Xcode’s documentation quick help will repeat the NSView docstring to you, saying: The receiver can interpret event itself, or pass it to the system input manager using interpretKeyEvents(_:). The default implementation simply passes this message to the next responder. [Emphasis mine]
In recent weeks, I’ve been feeling quite restless. There’s not enough time, too many things I just can’t seem to take care of. You probably know that feeling – or rather, the attitude towards things. There’s a couple of things that need to be sorted out. And there’s stuff that arrives on my desk and wants to be taken care of. Like taxes.
Variable row heights in your NSTableView might be broken in your apps on macOS Ventura 13.0 – it’s fixed with the upcoming 13.1, but that’s only available as a beta at the moment. When you replace table contents by calling aTableView.reloadItems(), this will ingest the new data as usual, but the old row heights won’t be forgotten. This can affect scrolling. The row height cache, it seems, isn’t properly invalidated or cleared.
Happy macOS release week, everyone! Earlier this week, macOS 13 Ventura was released. It’s a point-oh release, so you might want to sit this out a bit and wait for 13.1, as is common practice. (My main developer machine is running Big Sur, and I won’t be updating for a couple of weeks more, maybe not before December.)
Sketch shared the news today (on LinkedIn!) that 80 employeed are being laid off. This will mostly impact Operations and Marketing, who have done great work in the recent weeks and months. Our Product team remains well-equipped, with a core team continuing to drive things forward.
For a plain text (not rich text/RTF) NSTextView, I found that: Since NSTextView doesn’t understand the NSPasteboard.PasteboardType.string pasteboard type for reading or writing, I tried two approaches to handle plain text input (pasting) and output (cut/copy): The backport seems to work, but extending supported types sounds like more robust solution.
NSTextStorage provides attribute fixing of user-entered text, via NSMutableAttributedString.fixAttributes(in:). Attribute fixing… NSMutableAttributedString.fixAttributes(in:) always work on full paragraph ranges. It extends the passed-in range as needed “to cover the last paragraph partially contained”.
The metaphor of the “black box” is very common in programming. It is so general that it’s nearly meaningless; but it summarizes a lot of specialized principles nicely and shows a unifying principle. A black box is characterized by: It can be applied to “modules” on all levels of abstraction.
Here’s a short definition of the two terms: Cohesion is about how well elements within a module belong together and serve a common purpose. Coupling is about how much one module depends or interacts with other modules. Thus, cohesion is an intra-module concern whereas coupling cuts across modules [aka inter-module].
When you work with NSTextView and happen to use insertText(_:) to programmatically insert text, you get an undoable action for free. This might give the impression you get undo/redo functionality for free. Eventually, you’ll notice how other changes don’t have an affordance in the “Edit” menu. While it’s possible to get “Undo Typing” and “Undo Set Color” from some function calls, it’s not possible to get “Undo Change Text Attributes” when you use NSTextStorage.addAttributes(_:range:).
Reactive, declarative code is sequentially cohesive: you have a sequence of events and reactions to events, and it’s pieces are tied together real close. The processing chain itself is then a function or feature of the app. The chain of operators is a thing itself; the step-by-step transformation is then reified into a sequence in one place: it has a beginning, an end, a sequential order of steps.
Thanks everyone for joining the AMA yesterday! Was a fun experience :) We talked about a couple of technical and historic things. Here’s an outline of the pieces: The first, very successful two weeks of sale having passed, this adds up to a whopping $1.03 hourly rate! I sold the app 181 times […].
I realized that my official 10th “company” anniversary is just around the corner! To celebrate and hang out with people online, I’d like to party with all of you in a live chat and video. To round things up, I’m doing this Ask Me Anything style. No agenda from my part, but I’m open to any questions and inquiries.
How long does it take to become a true SwiftUI master? How far am I on my own way to mastery? 🤔 Let’s quantify and have a wild guess! I start this introspective journey by looking at another topic I feel like I could become quite the expert, even though I am painfully aware of how much I still don’t know – Text Kit.
This time I remembered to record our weekly sketch-together over Discord. Here’s a 4x speed version with narration of this evening’s sketches. The focus was on tonal values, so no colors, just pen and ink, then ink washes on watercolor paper. The paper I used here is the Moleskine watercolor pocket book (Affiliate link). I don’t like that paper for watercolor paintings, but it’s great for ink washes, because it absorbs the ink quite rapidly (unlike regular drawing paper).
Pop quiz! What is the output of this program, e.g. when run in a playground or an autoclosure block? The documentation indicates as much, but it can still be a surprise in your programs when you treat the @Published event as a mere signal to read multiple values from the observed object.
To get more structure into my day so I get work stuff done in time and have free time in the evenings to tackle other things, I’m now experimenting with notifications to end the work day. The following is a translated version of the current data. I am using the German term "Feierabend" (which you can shout well); “end of work” is a bit clumsy, and it doesn’t sound like an exclamation.
When you have nested and complex UI components with multiple sub-view controllers, passing an optional output port (event handler or delegate) down the tree is cumbersome. There are two straight-forward approaches: The second option is “cleaner”, but for a two or three object component might also feel a bit too much.
My blog posts here usually have a line like: For tags I don’t use a lot, I sometimes don’t remember how to write them. So I do the only sane thing – and go to my website’s list of tags and look for a match. Got annoyed by that now after a couple of years :)
I’ve recently added the code signing identities for a client to my dev Mac. Some old code signing scripts then failed to run: 🛑 Apple Development: ambiguous (matches “Apple Development: Christian Tietze (xxxxxxxxxx)” and “Apple Development: Christian Tietze (xxxxxxxxxx)” in /Users/myuser/Library/Keychains/login.keychain-db)
Writing unit tests first, in the manner of test-driven development, is some kind of “wishful programming”: you create a counter-factual situation in the test code, e.g. one that uses types and methods that don’t even exist. Then you add the implementation to make the counter-factual statements (and failing tests) the actual reality.
I used to rely on M-h to mark the structural element in Emacs; in text buffers, that’d be the paragraph, in org buffers, that was the whole outline item (when the cursor was in the heading line, at least). Ever since I installed Emacs for Mac OS X which now is also on Emacs 28.1, this shortcut wouldn’t work for me anymore, because my Meta key is my left Command key, and Cmd-H is the macOS shortcut to hide the frontmost app.
I’ve released an update to existing users of TableFlip last week. It works well so far, so now’s the time for the official announcement of the new version, available for direct download.
Most notable changes:
TableFlip now guesses the delimiter in CSV files, if needed. So you can use ; or \t just fine, which means export from Numbers works out of the box.
Non-comma-delimiters also “stick” when saving. No auto-replacement to comma anymore.
The CSV loading and saving is more robust, especially with quoted content. There’s been issued reported in the past, and these should now be fixed. Thanks for your emails and sample files, folks! ❤️
For my nerdy readers, behind the scenes I also upgraded Sparkle from the XPC branch to the actual Sparkle 2.0 stuff. I sincerely hope it will ingest the upcoming updates, too. (It’d not be the first time I broke the update mechanism of one of my apps, heh.)
What is TableFlip, you ask?
TableFlip is a fast tabular data editor – the ideal companion app for Markdown users who want beautiful tables in their documents. Open the file in both TableFlip and your text editor, and you’re set: TableFlip lets you edit all tables it finds inside your document and updates live as you edit your text.
Well, and you can edit CSV files, too, and export to LaTeX.
Found DevCleaner for Xcode yesterday and gave it a spin. It’s freeware on the Mac App Store with a tip jar.
Developers who ever nuked their Derived Data folder (I have an alias in my .profile for that) know how much stuff Xcode collects over the years. And then there’s outdated iOS Simulator versions nobody needs anymore. And log files, of course, by Xcode.
I’m pleased to say that DevCleaner does a good job at enabling me to select what I want to remove from the set of things that could be obsolete.
I have Xcode 14 beta installed (to my dismay), so DevCleaner suggested all Xcode 13.x related things could go away. It tries to be smart and helpful here, but I kept the Xcode 13.4.1 logs just in case. The option is there.
Where in the past I would occasionally nuke all Derived Data, DevCleaner shows these folders in a sensible way, with a reference to the project location. I usually create throwaway projects to test and debug something in my Downloads folder. The derived data of these projects is absolutely useless after a month, week, or sometimes even a day. Since DevCleaner shows where the original project was located, I’ve been able to delete data for every long-gone project that was in my Downloads folder.
I cannot stress enough how cool I find this.
Deleting derived data folders is often hard to do by name alone: when I download a new version of a 3rd part dependency that I am using in my apps, and open the project in my Downloads folder, and play around with it, then I have two derived data folders for this dependency, but only one is relevant for the actual project. Either I don’t care and delete both when I know building them is trivial, or I don’t bother at all and they stay around forever.
I really, really like this feature.
Would be even better if I could sort or filter by origin path, but it’s ok the way it is.
The Notarization API changed a bit, so my old scripts broke. Revisiting the approach, it’s delightfully simple to notarize an app nowadays if you use the Apple toolchain on macOS. I haven’t watched the WWDC 2022 video on Notarization but did take the documentation link there into account.
Sometimes I actually do wonder why the Emacs defaults are whatever they are. Someone somewhere decided that clicking with the left mouse button while holding shift should pop up an appearance menu where you can change font size and things like that. The behavior I expected from my personal history of 25 years or so of using computers is to extend the selection up to the clicked point.
Magit binds M-w to magit-copy-buffer-revision. On my Mac, I use the left Command (⌘) key as the Meta key, though, and wired M-x, M-c, M-v to cut/copy/paste, leaving M-w aka ⌘+W to close the current buffer.
I’m using the Emacs git frontend (‘porcelain’) Magit for all my projects nowadays. I fire up GitUp (which is great) only to traverse the commit history visually. Here’s one of the reasons: With auto-completion framework company, I get completion suggestions when I type my commit message. These are based on the actual code diff of the commit.
In SwiftUI, @EnvironmentObject is used to loosely couple any ObservableObject without directly passing it down the view hierarchy, e.g. via parameter injection. Unlike Singletons and global variables, Environment Objects are local to view hierarchies: different branches in the hierarchy can maintain different object references.
I talked about my “job” the other day and, again, pointed out that I’m making apps, but for Mac, not iPhone. No, no, I also do iPhone. I just don’t like to, and avoid it if I can. But using a Mac, that is fun, and working on a Mac is great. The machines are good, the OS is still good. That’s what I believe the most. For the things I’m interested in making, the Mac is a good platform. It’s a platform to get serious stuff done. And I can be a part in making the experience enjoyable and make “work” fell less like a chore. That’s what I’m interested in.
Since hackers and normies alike check out Mastodon nowadays, here’s an undocumented (as far as I can tell) variant to connect your website link to your Mastodon profile. Update 2022-05-16: Dave Barr made a much nicer illustrated guide for this!
On Twitter, Manuel Schulze (@zet_manu shared the Swift package Resolver that does dependency injection in a very convenient way with little boilerplate thanks to property wrappers: Mr Dr Dominik Hauser replied, and that’s how it entered my Twitter timeline. I was curious why people use packages like this – I know the concept from Java, but have always found constructor injection and maybe a Service Locator here and there to suffice.
I have a confession to make. I eased into this by sharing the same info on Twitter/Mastodon already. My ~/Downloads folder is a mess. It’s much less messy than your Downloads folder, most likely, but still. I achieved relative de-messification by automatically filing old downloads into sub-folders, one per month, like ~/Downloads/2022-05 Downloaded. That was amazing because the actual Downloads folder was clean, and old stuff was somewhat highlighted.
In Swift, you can weak-ify references to self in escaping closures, and then you need to deal with the case that the reference is gone when the block is called. Last month, Benoit Pasquier and Chris Downie presented different takes on the problem. That discussion was excellent. It prompted me to take some more time to revisit this problem systematically, and I took away a couple of notes for future-me.
I’m using Sendy for our newsletters. Recently, verison 6 was released. Here’s how I updated like a pro on my VPS via SSH. The beginner-friendly but ultimately drag-and-drop/FTP-based online instructions assume that you will be moving files over from “new location” to “existing location”. That’s quite cumbersome when all you have is SSH.
I just wanted to take a short minute to shout out to two transient.el based packages I discovered this week. One is org-menu, and bound to C-c m, it shows a transient menu of, well, transient keybindings to navigate around, move outline items, and do a lot of other things.
So for quite some time, I had the Swift Build System Integration flag enabled to speed up my builds. It was good. I set it, it worked, I forgot about it. With Xcode 13.3 and 13.3.1, now, we got a lot of dependency cycle warnings out of nowhere. Clean build works, but any build afterwards would occasionally fail.
To make menu bar app screenshots for the Mac App Store, you need to fill 2800 by 1880 pixels with something that highlights a widget in the corner of the screen. Geoff Hackworth did this: Since today, you can look at the result of this on the App Store: check out Geoff’s app “SF Menu Bar”.
I bought a hand grinder that would replace our 40+ years old, dull antique. Of course I looked for advice on James Hoffmann’s YT channel and figured the Aergrind by Knock would be a great fit. As the man coffee scientist himself says, the grinder is so good it would’ve blown away any cheaper competition from his other video, so he included it in the premium segment comparison. That was one reason I considered this.
I am using the built-in project.el in Emacs to find files in various git-backed projects. But I also have a shared folder of blog post drafts with Sascha at zettelkasten.de that does not have any version control backing. I couldn’t get that folder to show up in project.el’s list because of that.
Dominik Hauser maintains the illustrated xcode.tips page I’ve mentioned on Twitter here and there because the graphics are just lovely and the tips are really useful to become a proficient user of Xcode. It’s a complex piece of software, and the illustrations capture a tip in a single graphic, with shortcuts and UI hints and all.
In hindsight, it looks like my quest to tweak how Emacs looks and feels is to make it more Mac-like. No wonder, because I really like the OS X-era UI. So I’m still using the system default selection highlight colors and want these everywhere.
NSAttributedStringAPI takes an NSRange by-reference. That’s cumbersome to use, though, because you need to initialize a non-nil range, and there you should initialize it with NSNotFound to indicate an illegal state. Afterwards, you need to check if the range changed to a legal value.
I’m a very happy user of Magit, the amazing git frontend for Emacs. Today I noticed again that I miss one thing from GitUp, a GUI frontend for macOS, that I use when I’m selecting changes for a commit, discarding experimental file and line changes here and there in the process:
Tyler Hall wrote about “Half-Assed Mac Apps” the other day. His argument goes like this: Catalyst made it easy to deploy iPad apps to the Mac. But iPad apps don’t make the best experience on Mac. Users end up with more apps, but a lot of them being “half-assed” Catalyst ports.
I got an email by reader Andrej this week, asking about ways to (ab)use frame recentering in Emacs so that when a split window is created, the frame would grow to make room. So if you have a frame with 1000px width, and split the window to the right, it should enlargen to 2000px width. And then recenter.
For about two months now, I’ve been using tab-bar-mode in Emacs to have multiple “workspaces” open. I was fine with buffer switching, too. But tab-bar-mode not only allows you to switch between buffers, but also window configurations – that means one tab will have 1 window with 1 buffer visiting a file, while another might have a gazillion split panes. So the main upside for me is to arrange stuff into panes aka windows, and then open a new tab to do something in a single-pane view.
Today I have uninstalled the Dropbox app from my Mac. I’ve been an early Dropbox user and invited friends until I got up to 10 GiB, which was a ton of storage back in the day. But the native apps gets more and more annoying; they have a history of faking system standard dialogs to “trick” you into installing the kernel extension with root privileges; and resource consumption was at times confusingly high. Stuff like that made me tired of the app.
It’s a hack but it works: To stay in a room (or rather: let the bouncer stay there) and still close the buffer locally, it helps to change the major mode of the circe buffer: M-x fundamental-mode RET. If you change the buffer’s mode to any text mode, the circe-mode specific teardown hooks won’t be triggered and you can close the buffer without leaving the room.