Long story short, music.apple.com will look very good in a web browser on Android, but (with German location anyway) it will only ever play song previews, never full songs, no matter how hard I log out and back in again. The only download advertised on the website is the Play Store, though.
Flying Meat’s Gus Mueller on the life of an independent developer in reaction to Apple acquiring Pixelmator: But today I woke up, and got to work on Acorn. And I’ll do the same tomorrow and the day after that. I enjoy what I work on and I plan on doing it for many years to come. And I truly value my independence. I love being able to work on what I want, and when I want to.
In Emacs Org-Mode, the default key binding to “highlight” or “format” the selection is C-c C-x C-f. This will then ask for the org-mode syntax thingie to use, i.e. */_~=+. Literally, that’s the prompt. So you have to know which character to use to embolden text. That’s *. But which one is code? It’s ~. And = is for verbatim text, while + is for strike-through.
[W]e can just play around with our types, their APIs and structure, and the compiler will give us feedback about their concurrent behaviors. You can often get considerable amounts of work done without even needing to run the code! That gives you a really fast feedback loop.
The ability to iterate on a design’s runtime behavior this way is cool.
I believe I, too, felt this effect when playing around with actor isolation. But Matt really nailed expressing what this means.
It’s compiler feedback for implicit assumptions we made in the past. We don’t need to test parts of concurrent behavior if we learn to wield this tool.
Well, this is the vision.
The reality in the past year(s) and the current discussion is dominated by expressing frustrations because it’s hard to understand. It would’ve been hard before, too, but now we can’t gloss over things like NSImage being inherently async when passing actor boundaries anymore because the compiler won’t let us. And some first party API’s aren’t even properly or fully ported to Swift concurrency support, yet. And documentation is sparse.
But I do share Matt’s excitement that vision and reality will eventually conflate, and we’ll be better off then.
Refactoring is reality-bending. Over and over, you change the status quo to align with a vision. It’s about “making the change easy”, one iteration at a time. But here’s the catch: each step creates a new reality. And that reality can trap you, just like the last one did. You lose sight of the vision.
I’m accessing my Nextcloud, Linkding, Calibre e-book server, and other “cloud” services hosted on the NAS in the other room via their online domain and a Cloudflare tunnel. That means traffic essentially needs to be routed from my Mac, through Cloudflares servers online, and then back to the NAS. With that, what could be network file transfers actually take forever with my Telekom DSL.
As of today, TableFlip v1.7 is available as a free update for all customers, and available to download on the website. It’s a maintenance release to update everything for modern macOS, but it packs something delightful: a user interface overhaul with new icons and a modern macOS window style.
I had this great idea, using URL Schemes on macOS apps, assembled by very simple HTML files that contain a <form> only and produce a GET request so that the data is part of the URL: To my dismay, I discovered that all browsers encode spaces in input and textarea elements as plusses: +. Turns out that’s valid and expected.
Whenever I can, I participate in our local software craftspeople’s Global Day of Code Retreat (GDCR) in November. Yesterday was this year’s event. It was the first time I remember where I haven’t taken away something new for myself as a programmer. I learned much more about people. My mind has been occupiec with a lot of things in recent months, most of which were about our newborn baby. For me, having a child changed all priorities: the baby comes first.
The context menu trick doesn’t seem to work anymore, and you need to go to System Settings, it seems. (I’m not yet on Sequioa.)
TL;DR: You can still run unsigned apps, but it’s a bit more difficult. After trying once, you have to go to System Settings → Privacy & Security, scroll to the end and click Open Anyway for that app.
I find myself looking at an NSView subclass that actually doesn’t do much. It’s an opaque container for a text field that sets a label text. “Opaque” inasmuch as its outward appearance is being view, hiding the actual component’s complexity inside. This is just one example of an oft-repeated pattern: delegate to sub-views, using view composition instead of inheritance. This reduces the public API quite a bit by virtue of its opaque nature.
Alex covers concepts like replacing the notion of validation user input (with true/false results to represent its validity) with the notion of parsing – transforming unprocessed values into processed ones that uphold certain guarantees, like matching a string pattern. For example for email addresses.
The biggest idea tying all of the things Alex covers together is iteratively using Swift’s strong type system to sketch and then build a domain that ensures certain classes of mistakes are impossible to make. Because if you would try to do them, the compiler would complain.
I also put into my Zettelkasten a couple of concepts that didn’t have a home before, like
Black hole arguments to express requirements in types;
the idea that only compilation errors prevent faults in production;
“Type, Define, Refine” flow to give iterative type-writing a name;
that we can achieve type unions in Swift via enums;
Recently, our family grew by +1, and we got our lovely daughter back home for a couple of days. The experience in the hospital or clinic surrounding the birthing process before, during, and after made me realize that If you read life hacks and productivity tips including checklists, you’ll know the adage that “if it’s good enough for plane pilots, it’s good enough for you” or something like that. Over the past couple of weeks, I found that checklists would have been super useful in many situations around giving birth to a child. We did have a checklist with items to pack before heading to the hospital. But that was it.
Swift is full of syntactic sugar. One particular taste of sweet is the callAsFunction feature. If a type has a method of that name, you can call an instance or value of that type like a function. That’s used in SwiftUI to pass actions around in the environment, for example. Quick demo: See how the call looks like there’s a function updateWidgets(newerThan:) but actually it’s calling the value itself?
I wanted to write something positive about the Fairphone Fairbuds. Here it is: They are a great idea. That’s it. Please move on. Continue reading for gory details, but this really sums it up: Replaceable batteries at the cost of a larger form factor is a tradeoff I am absolutely willing to make and advocate for. I hope more companies try this in our greener future.
In the not-too-distant future of late summer 2024, we’ll be expecting our first child. This is your opportunity to share with someone willing to listen what you usually have to bash into other people’s heads: What heartfelt wisdom do you have to share? Is there anything you wish you knew when you had your first child?
By default, the string output of JSValue in JavaScriptCore printing (e.g. via my console.log) will produce a mere [object Object]. That’s not very helpful. It’s simple to pretty-print objects (if they are merely data-containers) via JSON:
If your macOS app is a SwiftUI app with custom button styles that targets macOS 15+, you don’t need my click-through fix anymore. Reported via Feedback Assistant on 2024-04-11, FB13720950 is now closed because macOS 15 Beta 2 solved the issue.
Primitive Obsession is not called “Primitive Preference” for a reason: we can find ourselves clinging to primitives like addicts, even when every aspect of our project nudges us, hints, and screams that we should be doing something different. It’s never just “a string”. It’s never just “an integer”.
In my recent post titled “The Rake and Its Prongs” I introduced a function towards the end, called NSRange.isValidInsertionPointLocation(at:). It’s used to consider the after-end location as part of the range.
I wanted to show, just this once for this project, the *compilation* buffer in a bottom split. I also wanted the split to be dedicated, so no other buffers would occupy it by accident, and not be deleted (aka closed) when using delete-other-windows. That should be reserved for code buffers.
Some days I forget to run Denote to create a new journal file for the day, and then the appropriately named note file is missing when I want to write down things later. That’s not how you use a daily journal, I know, but I use Emacs and denote.el to write down things like exercise logs or when I get sick, and these are information I can backfill easily the day after.
It’s a directory of 950+ menu bar apps for macOS. I browsed it a bit, and while each page is a bit short (5 items per page), it’s great to discover new tools – and also check out UI design trends in menu bar apps.
For example, I’m surprised that there are still so many NSPopover style windows with the speech-bubble pointy thing (because almost all apps I use don’t use popovers anymore). At the same time, there are many apps with what looks like custom NSMenus to embed more complex views as menu items.
When you type in a text editor, you always type out of some range. When your insertion point or cursor is blinking at the end of a word you just typed, you expect to still be “in touch” with the word, and that the next key you press will for example add a character to that word. This is a useful deception for us human users. It’s not actually part of the technological underpinnings.
Documentation-writing is not a goal in itself. Code, tests, documentation – all of these are tools to communicate in writing with other programmers. That includes your future selves as it includes teammates and project collaborators. Kent Beck, “The Documentation Tradeoff”:
KOMA-Script is Omakase. If you trust the package, you’re in good hands to get consistent output from compiling LaTeX documents. I’ve been using Markus Kohm’s KOMA-Script for most of my life with LaTeX in some way or another because the package’s document classes really nail typical German typesetting requirements, e.g. requirements of DIN letter formats (yes, there is a standard for layout and typesetting of letters!). And the scrbook document class ships with tasteful defaults for book typesetting.
With the power of outline-minor-mode in Emacs, you can turn any text buffer into an outline – with the killer feature of “cycling”, i.e. folding and unfolding outline elements. This includes rg.el-managed buffers, search results powered by ripgrep.
I figured out a way to consistently change a NSRange, e.g. of a selection of text or the insertion point in a text view, to select surrounding words in DeclarativeTextKit. But first off: Dear heavens! This wasn’t easy. I’m still not happy with the solution. While there are three to four hundred test cases (the vast majority is generated) that helped me narrow things down, I can tell you up front that I’m not proud of the resulting 160 lines of code.
Marco (@esamecar@social.lol) posted a list of alternatives. From that list, I filtered out MAS-only and Chinese-subtitled apps: I’m running Ice at the moment. You can organize menu bar icons into “always hidden” and just “hidden”. The latter will reveal itself when you e.g. click on the menu bar, the former won’t by default. You can bind shortcuts to show either or both of these groups.
I’ve now finished adding an example adapter to get a structural representation of a Markdown code block to my app. It bridges the abstract syntax tree (or “token tree”) of libMultiMarkdown to NSTextStorage-compatible UTF-16 substring ranges – which DeclarativeTextKit works with, reducing code size and potential for errors even further:
I started working on DeclarativeTextKit mostly out of desperation, because the naive approach wouldn’t do the trick any longer. I complained about this on Mastodon in the beginning, but until now, I haven’t actually shown the code that made me want to change things.
In DeclarativeTextKit, I found the abstraction of an “expression” in my vocabulary to represent the Domain-Specific Language’s instructions useful. Here is an example of a valid block of changes: This uses two kinds of Swift Result Builder to define the DSL’s grammar: The rules of the grammar are essentially this:
The recent release of TableFlip v1.6 is the first one that includes a new in-app purchase (IAP) component I assembled for my projects. It’s used in TableFlip to purchase a lifetime license within the app.
With future releases, the package will support in-app purchases of consumables or individual features, too.
A FastSpring account with the modern backend that everyone gets. (If you are still on “Classic”, you’ll know.)
Create an Embedded Storefront to display a very minimal shopping UI on a HTML page. Put this on your website or another trusted source: You will have to tell FastSpring that the form is legit with a domain-based allow-list, so you should really own the source.
Include the package in your app and point to the URL of your self-hosted embedded storefront.
Bonus: if you use my Trial and Licensing package, you can quickly unlock the app after a purchase has been made. The IAP component notifies your app on success.
With that in place, you can display a dedicated purchase window inside your app that doesn’t require a large context switch to the browser to make a sale:
The setup is quite short. I use a dedicated service object:
classPurchaseLicense{letstore:FastSpringStoreinit(){self.store=FastSpringStore(storeURL:storeURL,purchaseCallback:{store,purchasesin// Could have multiple purchased items, e.g. if you// offer in-app purchases of bundled features.assert(purchases.count==1)guardletpurchase=purchases.firstelse{return}forlicenseinpurchase.licenses{// Unlock via license.licenseName & license.licenseCode, e.g.// using the https://github.com/CleanCocoa/TrialLicensing package://// TrialLicensing.AppLicensing.register(// name: license.licenseName,// licenseCode: license.licenseCode// )}})}funcshowStore(){// Localize and customize the window title:store.showStore(title:"Purchase MyApp")}}
I introduced this feature to my apps because its predecessor was a popular choice to purchase a license among WordCounter users. There, it is being picked for about 50% of license sales in total.
It appears that if the option exists, it will be used. I can’t tell whether having this option actually increases sales or not, though.
There’s been progress on the Declarative Text Kit API idea, and today I want to share an insight into building a DSL with Swift Result Builders. A detail that I didn’t grok until I built it. {{TOC}} Inserting text into a string, text view, or other kind of text buffer is simple enough: you need a location and then use the appropriate API to put a string into the target there.
I invested the past two weeks into making the Markdown highlighting component of my app The Archive better overall. More secure C token pointer handling, more performant token tree mutations, so faster highlighting and fewer pitfalls to worry about.
Use open source liberally to make progress. Avoid open source dependencies in the long run if you can. Start a project by ingesting open source libraries to make progress fast and see if your product works. That’s fine. If it’s not too much, you can leave them in. But when the product matures, and if you offer a library yourself, you may benefit from reducing dependencies to ease product maintenance. Pick what you really need:
As the saying goes, if the title of a post is a question, the answer is “no”. I don’t know how you would sensibly apply that to this one 🙂 Most of the days, for most of the time, I’m a software developer. I code, I plan, I think and learn. At other times, I’m writing things: on this blog, for book manuscripts, as letters and email; to plan, to help, to teach and to share.
In a StackOverflow question, Thomas Tempelmann asked: How to get all TableCellView prototypes from a TableView object? […] How do I get to that TableCellView in its prototype form when I only have a reference to its TableView object, so that I can alter its tooltip for all rows added later?
Making just one type @MainActor can result in cascade of errors at all usage sites where the compiler now cannot provide that MainActor guarantee. This virality can make it really hard to incrementally adopt concurrency with targeted changes. Perhaps that’s not too big a deal for smaller code bases/teams, but I bet this is a killer for big projects. So what do you do?
You make use of dynamic isolation to contain the spread!
Instead of throwing (static) type annotations around, you can ease into the adoption of actor isolation with (dynamic) preconditions and running blocks of structured and unscructured concurrent code.
The problem is explored in great detail. Every big company fails at this. Type (font) design and UI design and icon design are all part of the problem.
After the build-up, this part killed me:
What can be done: icons fonts
STOP.
USING.
FONTS.
FOR.
ICONS.
I recommend looking at the pictures to spot all the annoying alignment problems and have a good time with this.
Why change perfectly servicable code today when there are problems to solve? “I’d be changing it again next week. So where’s the value? Might as well keep it as-is.” When you are inclined to change code without defects, this could indicate a new understanding that needs expressing. Changing existing code to reflect a new understanding is all we have. This is how we pay off debt in our design.
Ever wanted to recreate a beautiful Mac OS X Aqua app in SwiftUI? Technical Note TN2124 contains cool debug information, and a screenshot of the DebugTest Info app. Here’s a live-coding session (24mins) to recreate the DebugTest Info app:
On macOS, there’s 1 active window that receives your key input. Then there’s all the other windows, which are potentially invisible or obstructed by other windows. These inactive windows can be brought to the front and made active with a click. Then you can interact with these windows like normal. With inactive but visible windows, you cannot select items from lists, for example. The UI reflects this with a greyed-out or dimmed representation. You need to click once to activate the window (on the surface of the window, so to speak), then another time to interact with the control.
Programming is a Soulslike. Dark Souls is notoriously difficult. You need to memorize enemy movements and patterns to get good at the game. You cannot beat it casually. The “Git Gud” meme traces back to this whole ordeal: You need to become a better player, learn the movements and patterns, in order to beat the game.
Decoupling is hard work, especially when taken to extremes – as the team of Diablo II did when they decoupled the whole rendering stack of the original game from the rest of the engine to reuse both independently. Today I learned that Diablo II Resurrected is a true remaster: new 3D graphics, but the engine hasn’t changed. In an interview with Eurogamer, Rob Gallerani pointed out that
Swift KeyPath is a great shortcut for property-based mapping and filtering of collections: When you need a simple transformation, the simplest probably being the boolean negation operator, you only have two choices: So either you use the key path and separate the negation – which makes it harder to read as “I want the opposite of X”. Or you drop the key path and use a regular closure.
I spend a couple of hours investigating the XeLaTeX purgatory where character mapping live. I didn’t know what these were before, too, don’t worry. I’ll walk you through it. The observable behavior is the following. Let’s say you start with Markdown with “dumb” quotes like this: … and use a conversion tool like Pandoc or MultiMarkdown to turn it into LaTeX, and you pick German quotation marks (the non-guillemet ones). You’ll see why it’s important for this example to use the German quotation marks. They are different from the curly quotes you know from English texts.
Of course you don’t need comments in your code. But your future self appreciates explanations: What is this nested loop good for? What is its purpose? Comments provide context, and shortcuts for understanding: “you don’t need to read this, just think of it as …” Once you explain what the purpose is, suddenly you may also find a way to express the intent clearly in code.
I found out that you can bundle TrueType Font files (.ttf) with Swift Packages just fine. It’s not as declarative as adding Info.plist entries to your app, but the code is very simple. First, add fonts as managed resources under e.g. Sources/PACKAGENAME/Resources/Fonts/ (last subdirectory is optional):
Based on the calendar, I’m about two months into a server script project for the Zettelkasten website, but measured in actual work weeks, it’s now the start of week three. I notice that I’m now in a comfortable rhythm to develop features, and that’s always an exciting milestone.
Octavi Navarro reports that (some) people think (some of) his games should be free instead of paid: I’ve got hundreds of “this game should be free” angry reviews over the years but I find it shockinge every time. These are not evil corporation executives, but consumers (most probably underpaid workers themselves) who advocate very vocally against remunerated work.
On Mastodon, we had a discussion about whether you are more or less productive with SwiftUI or UIKit/AppKit. Der Teilweise (@teilweise@layer8.space) chimed in with an actual, measurable benchmark: a flexible-width window, with reflowing text, and equal-size buttons. Doable in 10 minutes. Can SwiftUI beat this?
Modern FastSpring storefronts come in three standard flavors. On top, you can create your own JavaScript client page that uses the Store Builder Library under the hood. Here they are: The most basic storefront is the Web Storefront (docs). It’s a hosted web shop where you can offer multiple products. Here’s mine for apps.
To tell OpenAI’s web crawler to skip your site, add these lines to your robots.txt (see docs): (via Hidde de Vries) The outlook is bleak: There’s no way to win this if you don’t want to be scraped at all, ever, except by not putting things online. If they don’t scrape your content, they scrape the copycat sites as Rik Schennink pointed out. Or ignore the robots.txt rule. (How could you tell, anyway?)
My highlights are instances where you have to think outside the box, like a good puzzle:
You cannot describe complex actions on random data directly (like cutting gaps from audio tracks); but you can offer inverse actions, like undo operations, and then essentially test that applying an action, undoing it, redoing it, then undoing it again doesn’t change the result.
Testing a search function, you can’t actually test the search (without implementing the search functionality inside the tests again) because you don’t know the test data. But you can test that filtering the search results works, because filtering should product subsets of the unfiltered search.
You can describe web apps as state machines with valid transitions and then go ballistics with trying all kinds of interactions. That’s the promise of Quickstrom: You don’t need to specify each state of the web page as an example, but you specify the properties of each valid state and then ensure that no combination of interactions and button presses produces an invalid state. (Relying on app introspection, this probably works much better on the web with headless browsers than in Xcode/on mobile.)
None of this sounds like a replacement for what unit tests would do. But they do sound like a much better high-level approach to regression testing!
If nothing else, I’m intrigued to learn more about this now.
TableFlip v1.6.0 got approved to the Mac App Store (direct customers got the update a bit earlier, as usual).
Check out TableFlip
The least exciting feature first, so that it doesn’t go unnoticed: you can now scale the font in TableFlip (aka “zoom in and out”).
I found this among my notes from 2013, and think it’s a fun little tool for analog productivity – the portable Kanban board! It’s a foldable personal Kanban board, suitable as an Every Day Carry in either A3 or A4 size (or US Letter or whatever). This produces four quadrants and the folded size is ideal to stuff it into a backpack, book, or maybe even your pants. Thus, it’s convenient to transport to university, school, or work.
Here’s an anecdote for you: Imagine a dev team that performs task estimates expressed in “story points”, Agile style, and encounters a large estimate. Large, in this team, means “13 or more.” Then in one of these sessions, a specific task initially received an estimation of 13 story points. This marks the team’s threshold for considering the division of tasks into more manageable pieces by convention.
As a resource to learn, the approximations are more than good enough. They are excellent and by virtue of being interactive, they are also much better to get a feeling for everything than the SwiftUI documentation’s images can ever be. There’s only so much an API documentation can teach you before you need to observe how it really behaves.
Since it’s in a browser, the preview is of course even faster than Xcode Previews would be, and without the crashes. (Oh, the crashes …)
I wish the SwiftUI Field Guide had been available a year ago when I had to figure out so many things through trial and error!
Some sections apparently aren’t finished yet (they’re greyed-out), but you can learn a lot about the reverse-engineered layout system’s inner workings.
Went through some old notes this week (I’m doing this AppKit/UIKit stuff for surprisingly many years!) and found a problem with the tags I used in one of my notes. Let’s dive right in with an example: It’s a how-to note with a code snippet. Its tags are: #appkit, #image, #screenshot.
In this fourth and probably still not final part of my series on NSToolbarItems with segmented controls, I just want to share a problem and a quick fix that Nathan Manceaux-Panot brought up today. The series spans 8 years and is this: Nathan recently went through the series to implement segmented controls in toolbars but discovered that the overflow menu items would not enable (a validation problem) and when they enable, they don’t fire the action. When he brought this up today, I investigated.
I can’t for the life of me remember which trash bin bag size to buy. Once I find a fit, it’ll be months before I buy the next batch. By then, I’ve long forgotten which one I bought. Some more expensive ones have the bag’s size printed all over them. That helps exactly one (1) time: until you buy a cheaper make of the same size. Next time, it’s guessing time again.
Here’s a truly inspirational list of things to do from the 32-Bit Cafe, “a community of like-minded website hobbyists and professionals helping to make the personal web fruitful and bountiful again”. It covers these topics:
Page Ideas
Potential Website Topics
CSS& Page Design
Art & Graphic Design
Technical Tasks
Accessibility
Interactivity
Social
It’s a refreshing read (and brings up a lot of nostalgia)!
Developers see the bugs and problems of their products, and thus they are prone to not charge a high price instinctively. The price of an app signals its value or worth to the prospective customer looking at the price tag. Jordan Morgen shares this from the Spend Stack days:
Today was a day of convergence. Our home server/NAS had a lot of SATA-related kernel errors and drive failures in the past weeks that I couldn’t track down. I replaced the drive and the cables and things have quieted down. This means I was SSH’ing into the server quite a bit this month. Mild data loss ans corrupted file systems included.
So I found this list of books I read and which I wanted to put on this blog in my inbox. It’s from a migration from OmniFocus to Emacs/org-mode from 2019, and the title is “Transformative Reading 2017”. What were the picks back then? And being 7 (!) years wiser, what do I think about the picks now? Here’s the list. I don’t know why I originally ordered them this way, but I left it as-is.
I admit: I’ve been relying heavily on ChatGPT to get to grips with some PHP things. Asking for interpretation, alternatives, and PHP 8-specific stuff was a lot of help. I’ve been using this in a separate floating window (aka ‘frame’) in Emacs next to my editing context, and that was great. Until I accidentally closed the buffer and lost the history.
Normally, you’d associate file path extensions with major modes in Emacs via auto-mode-alist. The associative list contains entries like ("\\.html" . web-mode) so that when you open (aka “visit”) an HTML file, Emacs automatically switches to web-mode, which in turns supplies shortcuts and syntax highlighting and so on.
I was rummaging through my Zettelkasten today, looking for a reference. I found the note 20190823100132 You can choose when you live in surplus and in the spirit of celebrating a new year, I find it is worth sharing: Seth Godin in Living in Surplus:
I’ve recently created a note in my Zettelkasten with a structure I haven’t used before: a timeline. It’s basically an enumerated list with 40 items and a divider that marks “now”. Things above the divide are in the past; things below the divide are in the future. I’m collecting rough things to keep in mind below the divide (like a GTDtickler file would). Above the divide, the granularity increases as I track individual things that happened.