X-Oriented Programming, Using Functions or Classes?

Working on a greenfield project is nice, but it also brings up all the uncertainties of the start: which components, modules, objects, interfaces do you need? With Swift, you even have to decide if you want to design the system in an object-oriented or a functional way. As far as the core functionality is concerned, you do have this choice. (When it comes to UIKit/AppKit, you don't.)

Continue reading …

Win32 API Programming With Swift

With some luck, all my Windows programming wet dream might actually come true one day. There's progress on making Swift available on Windows, and there's progress on providing a Win32 API wrapper for Swift so you can share model code across platforms and write UI for Windows and Mac separately.

Check out the GitHub repo: https://github.com/compnerd/swift-win32

It's experimental, but apparently way less experimental than it was a year ago!

ExactScan Leaves the Mac App Store

Berlin-based company ExactCODE, makers of ExactScan and OCRKit, experiences trouble with Catalina and a rather short time to submit bug fixes for their app before the public release of Catalina earlier this month. They decided to leave the Mac App Store behind:

  • each manual update review by Apple causes delay and drama
  • AppStore does not support paid upgrades, only new App, in-App purchase or subscriptions
  • Apple takes 30% and that is not sustainable to run a company and pay salleries
  • it is not provide to provide free updates forever
  • if you purchased our application this year we provide a direct license, if you had it significantly longer, we think a paid upgrade is fair for continuously developing, improvements, and support

[…]

There is mostly only one benefit [of using the MAS] for users: one central place for purchasee and updates. However, there are many negatives, such as: […]

I don't know if they could've done more to prepare for the macOS upgrade. Recently, folks on Slack shared screenshots, and it turns out that as a serious Mac developer you apparently have external hard drives full of previous and future macOS versions, plus a stack of different Xcode versions (that are no longer available for download by Apple!) – that's required by virtually everyone in order to support multiple OS versions and fix bugs. The Catalina beta also was very flaky.

After releasing the Catalina Golden Master build to developers on October the 3rd, we immediately finished fixing any new crash or issue we could find over the weekend. In our opinion, leaving developer just four (4!) days over a weekend with a public release on October the 7th is not very helpful nor professional.

They have a point here. But could they have fixed the same bugs earlier in preparation of the Golden Master release?

The call for App Store submissions went live on October 3rd, too, at the day of the Golden Master release. So even if they fixed all the bugs early, Apple would have had only 4 days to review all App Store submissions, which sounds like a bad idea nevertheless.

See also:

Shameless plug: I also wrote a book on selling outside the Mac App Store in case you want to leave the App Store, too, or make your company more resilient through availability in multiple stores.

Reducing the Blog Post Email Digest To Once a Week

After a couple weeks of experimentation, I find that twice weekly email delivery of my recent blog posts is one too many. I wrote a lot in the past months, so each issue contained quite a few links. But who wants more email with less links in it? I don't, so I changed the interval to weekly, delivered every Thursday, covering a summary of the whole past week.

Continue reading …

Model-View-Presenter, Form Validation, and a Unidirectional State Machine

As an exercise, I wrote a simple iOS scene transition from a login form (root view controller) to a success scene. I was experimenting with the Model-View-Presenter approach where view and presenter know each other, but communicate through protocols, so you program to an interface, not the implementation. I ended up using a simple state machine to represent the view's state, and employed form validation that is represented in the types. I like how the result reads, so here's a rundown of the exercise:

Continue reading …

Xcode Bug Turns Out To Be the Hardened Runtime Setting

What I called an Xcode bug last week turned out to be a broken project configuration that was absolutely my fault. Back when Hardened Runtime was introduced, I switched it on at the Xcode project level, not just for the app target. When you set "Enable Hardened Runtime" on the project level, all targets inherit this setting: the app, the unit tests, and the UI tests. With Xcode 11, it now turns out that hardening the runtime for the test runner broke an internal library lookup in perfect accordance with the utility of the Hardened Runtime settings.

Continue reading …

Fix Missing Font Fallbacks for NSTextView

Rich Siegel recently wondered on Slack why NSTextView would suddenly display empty placeholders for some glyphs when the font does not support them, instead of falling back to a safe font like it usually does. Chinese characters never got displayed. Michel Fortin remembered a similar problem, and the potential fix was quite simple:

In summary, if you have to change the font after the text storage was edited, do it in willProcessEditing and it’ll do the right thing. Don’t do it in didProcessEditing.

That turned out to be what tripped up Mr Siegel's text view, which now happily displays CJK/CJKV again. For more details and some background about how you can detect this problem in your apps, read Michel's post.

Building a Rich Domain In Iterations

I'm currently revising code from 3 years ago. The result is a replica of the old stuff – updating isn't worth the effort, it's written in Swift 3 and the code base is small, so I rewrite it and copy useful parts over. The public interface already differs from what I wrote 3 years ago. In the first step, I translated the old code but took what was there. For example, back then I had a DateRange type. Swift Ranges were less powerful back then, so I added a couple of convenient methods that I can now replace with the standard library protocols instead. So I demoted the type to typealias DateRange = Range<Date>. I also had a Date type (this was before NSDate dropped the "NS") that I renamed to NormalizedDate. In the domain I work on, a date isn't a date-time-combination, but just the year, month, and day. Confusingly, this type was a mere wrapper for NSDate that dropped the time information for quick comparisons.

Continue reading …

Emacs Settings for PHP Development

Teaser image

The project that I've been working on over the weekend, I worked on in emacs. This is part of my re-learning the basics of text editing and programming in emacs to slowly move away from TextMate when it comes to scripts. I want to move away from TextMate because I eventually want to transition to be productive on a Linux machine – that means, to create stuff in an otherwise foreign operating system. Emacs is portable, so that's a good start.

Continue reading …

The Archive Update Hiatus Due to a Severe Bug in Xcode 11

This announcement is a very difficult one: I cannot work on The Archive for the next week or two, maybe, more. That's because with Xcode 11 and Swift 5.1, UI Tests stopped to work. Like, at all. I have created a Feedback ticket (FB7338237) and now also have a Technical Support incident open, awaiting a reply by Apple engineers to help with this.

Continue reading …

Making os_log public on macOS Catalina

Since macOS 10.15 Catalina, you cannot easily make public logging the default anymore. os_log hides contents from string interpolation by replacing the string with <private>, unless you explicitly annotate the value with %{public}. That's probably supposed to force developers to think which parts absolutely need to be part of the log and thus reduce accidental logging of private information. Saagar Jha shows how you can enable your dev machine to log everything again.

Xcode 11 Displays Crash Logs in Context of Your Projects

For all throughout human history, you had to symbolicate your user's crash logs using atos or a 3rd party tool to get human-readable output. Otherwise, you will not know the symbol or method name of your app's parts that are involved and see memory offsets instead. But with Xcode 11, double-clicking a .crash file now opens Xcode instead of Console, and you can view the crash log in the context of a project. That's very nice.

Continue reading …

Processing David Epstein: Range (S01E04)

I uploaded another episode of the video series where I document how someone might process reading notes from a book after reading it. I don't have any Range related project, so here I'm just processing the book from start to finish instead of looking for anything in particular.

This episode is shorter, because I didn't find many useful ideas in Chapter 3. And the ideas I did find interesting were not citable, so I had to look the originals up. That took most of the time, but did ultimately not produce many new notes. It's interesting that after a couple of session I already begin to form an opinion of the quality of Epstein's research. There's lots of endnotes, but the quality is … well, enjoy this episode to find out more!

Episode list:

  • Episode 1: Introduction
  • Episode 2: Chapter 1, "The Cult of the Head Start"
  • Episode 3: Chapter 2, "How the Wicked World Was Made"
  • Episode 4: Chapter 3, "When Less of the Same Is More"
  • Episode 5: Chapter 4, "Learning, Fast and Slow"
  • Episode 6: Chapter 5, "Thinking Outside Experience"

"Make Money Outside the Mac App Store" Second Edition Ebook Available

Teaser image

The large update and rewrite of "Make Money Outside the Mac App Store" is finished. I have published an update of the book to Leanpub edition and the one you can get directly from my website. The print edition is still work-in-progress, but I should have something available later this month.

Continue reading …

Macoun 2019 Code Retreat Workshop Postmortem

Yesterday, Oliver Böhm and I have conducted our Code Retreat Workshop at Macoun 2019. Opposed to the passed-down spirit of Code Retreats, where throwing your code away at the end of each session, our workshop's participants often wanted to see how the other teams attempted to solve the problem at hand. So we decided to prepare a public GitHub repository. As of now, there's nothing to see, but I'm curious to find out if the functional programming language tag team will upload their OCAML- and Scheme-based implementations, and which names all the other folks have chosen during the sessions where they had to write tests first.

Continue reading …

Processing David Epstein: Range (S01E03)

Another episode went live today – earlier than the last weeks, because I'll be travelling to Frankfurt for the Macoun conference from Thursday to Sunday.

This chapter was harder to process. There seemed to be parts missing in the story. Maybe I’m too sloppy and haven’t noticed the missing pieces? Either way, something’s amiss in Range land. I also notice that the topics from chapter 1 were connected to the topics here somehow, but it’s just a hunch how things will fit together eventually. I’m looking forward to the next episodes, because I hope it “clicks” and I know how to split the sequence of notes in the structure note up into new Zettel notes and then work with the more powerful web of notes instead of this monolithic book overview.

Episode list:

  • Episode 1: Introduction
  • Episode 2: Chapter 1, "The Cult of the Head Start"
  • Episode 3: Chapter 2, "How the Wicked World Was Made"
  • Episode 4: Chapter 3, "When Less of the Same Is More"
  • Episode 5: Chapter 4, "Learning, Fast and Slow"
  • Episode 6: Chapter 5, "Thinking Outside Experience"

September App Updates

The build tools have changed a bit with the release of the programming environment Xcode 11, and I figured this might just be as good a time as any to publish small compatibility updates in preparation for macOS Catalina and to support iOS 13. So I released updates to nearly all my apps in the past 7 days:

Continue reading …

What to Do When You Want to Affect the Order of Subscription Callbacks in ReSwift

This blog post was originally meant as a reply to the ReSwift issue "Any way to control the priority of subscription callbacks?" – If you find asking yourself this question, this should help. This implementation detail is hidden by design. If you find yourself wanting to affect the order of subscription callbacks, there's a concept of a sequence waiting to be extracted somehow. Or, the other way around: if you want to affect the order, your design is wonky.

Continue reading …

Processing David Epstein: Range (S01E02)

I uploaded episode two of the book processing video series on David Epstein's book Range:

In this episode, I process the few highlights from Chapter 1. I end up with many more links than last time already. I also employ the method of creating a forward-link, i.e. adding a link to a note that does not exist but that I need at a certain location in my structure note, then add the details. The upside: you already have 1 connection!

Episode list:

  • Episode 1: Introduction
  • Episode 2: Chapter 1, "The Cult of the Head Start"
  • Episode 3: Chapter 2, "How the Wicked World Was Made"
  • Episode 4: Chapter 3, "When Less of the Same Is More"
  • Episode 5: Chapter 4, "Learning, Fast and Slow"
  • Episode 6: Chapter 5, "Thinking Outside Experience"

Add an Internet Access Policy to Your App

The devs of Little Snitch came up with a convention to annotate network traffic, so to speak. It's called "Internet Access Policy" and will show the purpose of your app establishing a connection in Little Snitch's warning dialogs.

It is implemented by adding a InternetAccessPolicy.plist to the app bundle with a dictionary of connection details. This could potentially be used by other 3rd party firewalls as well. That's what I like most about it: it provides useful information to end users without locking developers into anything. A Property List is innocent enough and could potentially spawn other uses.

Look at the project website to see how many of the big names have added IAP's to their apps already, plus yours truly: Acorn, BBEdit, MindNode, Ulysses, Paw – and many others.

This reminds me of a time 10 years ago when macOS didn't support tagging of files, yet, and the OpenMeta tagging convention was introduced by indie devs and became a roaring success with various productivity apps.

Calendar Paste v3.3.0 Hits the App Store

Teaser image

I have just released an update to my very first commercial app, Calendar Paste. It's now v3.3.0 and sports iOS 13 compatibility, Dark Mode, and overall better layout. So it's mostly a cosmetic and "minor tweaks" update.

Calendar Paste 1 was introduced back in late 2012. That's 7 years ago! So, happy birthday, Calendar Paste!

To celebrate, Calendar Paste 3 will be available for free from Oct 1st until Oct 6th, and then 50% off until the end of October. The App Store listing doesn't seem to be updated despite it being "Ready for Sale", for whatever reason, though. Maybe it's already fixed when you read this. v3.2 will work just fine, but not sport Dark Mode, yet.

The app was my first real app project, sufficiently simple to start with, yet interesting enough to be sold on the store.

Calendar Paste 1 kicked off my indie app developer business. I still don't make a lot of money and have to freelance to stay afloat, but this is a very cool path to have taken. I'm loving it. In case you wonder: it never was a huge commercial success. For 7 years, it brought in something between $3 and $130 per month, with a guesstimated average of $18.53/month. Unless it didn't sell at all, that is. So much for the dreams of yours truly in 2012 to get rich quickly, right? But I learned to code in Objective-C, use Core Data, program for iOS 5 (and then iOS 6 before the app was finished). That's worth a lot.

That's why I don't sunset Calendar Paste, even though each year's iOS update takes away time for maintenance. And I cannot warrant to add all the planned features to the app because the time is better spent on other, larger projects.

I also wrote a diary of the progress, taking extensive notes in a tutorial-like fashion, that I eventually wanted to publish as a $5 ebook – but I never did. It just didn't seem to be worth the trouble, and shortly after most of the code was already outdated, and the lessons learned not easily applicable to Swift.

Sorry if you're still waiting for iCloud sync. It's theroetically possible, but would take about a week or two to implement and test, and that's just too costly at the moment. One day I'd like to out-source development of features like this, but to do that I'd need to accumulate larger savings – so either me or someone else working on the app depends on the same solution and y'all have to hold out a bit longer.

Always Ask for MailChimp Newsletter Reconfirmation Outside of MailChimp

When you want to merge newsletters in MailChimp like I do and tell your subscribers to sign up at the new list and unsubscribe from the old to not receive any notifications – then you're setting yourself up for trouble! Because it turns out that mass-migrations from list A to list B mostly register as mass unsubscribes from list A. And an unsubscribe rate of 9% or higher (I don't know the actual minimum value, but 9.23% triggered it for my account) will automatically up the risk level of your whole MailChimp account.

Continue reading …

Don't Wait. Show Up. Or It Might Be Too Late Again

When someone close to the family dies, a lot of dust is stirred up. My godfather died last week, and I was informed that his burial will be tomorrow – more by accident than by plan. We hadn't talked in 5 years, and in late 2018 I became curious what he and his wife were up to. Why not visit them for a change, since they don't come visit anymore? I was at their place a couple times, but most of the time, like once every month plus for birthdays or so, my godfather and his wife visited us at home, back when I lived with my father.

Continue reading …

Processing a Book to Extract Notes, a Video

For the Zettelkasten knowledge project, I've recorded and cut my first video. The process was a pain, and I think I'm going to write about it later – but for now, I'm happy with the result and very proud to have finished this mini project.

It's part of an ongoing series to demonstrate the method we talk about on our blog. Here's the video:

Comments are very, very welcome. So head over to the forums and tell us what you think of it!

Episode list:

  • Episode 1: Introduction
  • Episode 2: Chapter 1, "The Cult of the Head Start"
  • Episode 3: Chapter 2, "How the Wicked World Was Made"
  • Episode 4: Chapter 3, "When Less of the Same Is More"
  • Episode 5: Chapter 4, "Learning, Fast and Slow"
  • Episode 6: Chapter 5, "Thinking Outside Experience"

Make Money Outside the MAS v2 Book Writing Stage is Done

My ebook update routine has changed a bit for Swift 5 this year: I've expanded the "Make Money Outside the Mac App Store" book typoscript a lot. It's about twice as long, with many more explanations for the setup, many new additional feature discussions, and practices that I've learned of in the last years. FastSpring's revamped store kind of forced me to re-take screenshots anyway, so I figured I might as well take more.

Continue reading …

Newsletter Revival

Not everybody knows how to consume RSS, so I wanted to try to make updates to the blog available via email. That's why I have now revived the newsletter signup page. This is the plan: I'll experiment with this setup for a while, but I think the current options cover almost anything I currently do, or plan to do, so that both developers and customers of my apps get something interesting in their mailboxes.

Continue reading …

Please Also Change Your Feed URL's Inside Your Apps

In my post about redirecting the DevMate update feed, I missed the opportunity to mention that you should probably also update your app very soon to not rely on DevMate's framework.

Mr Boy van Amstel at Danger Cove picked up the topic and explains how you can change your feed URL inside the app using the Sparkle SUUpdaterDelegate methods. DevMate's closed-source framework wraps this in its DM_SUUpdaterDelegate_DevMateInteraction protocol.

Your battle plan thus should be:

  1. Redirect the remote feed URL to deploy updates to existing customers working with old versions, and
  2. Change the feed URL used inside the app as soon as possible and deploy an update to not rely on the redirect for too long.

I use the SUUpdaterDelegate to switch feed URL's in The Archive and WordCounter, too, in case you wonder if this is a good idea in the first place.

See also:

Fira Code 2 Released

Teaser image

My favorite monospace programming font, Fira Code, was updated to v2. This is even supposed to solve an issue with regard to line height inconsistencie with bold fonts that the Fira Mono team decided not to fix. (Darnit!)

Fira Code is fun. I like the operator ligatures for e.g. != the most. Good stuff.

I Am at Macoun 2019

I'll be at this year's Macoun developer conference in Frankfurt (Main) again. This time, I'm not giving a tech talk, but hosting a workshop with Oliver Böhm. The Macoun is a German conference, and it isn't expensive. I think that's a great combo. You should go if you can. The community, or family, built around the conference over the past dozen years is very, very welcoming. You cannot not learn something when you attend.

Continue reading …

Wireframe for Server-Side License Activation

Server-side app license code validation, as I imagine it, in a nutshell: If the token expires and there's no server connection, you have to figure out how punishing you want to be. I suggest you do not punish by default and assume people have good intentions. Possible escalations: Remote or server-side deactivation of licenses can be useful to prevent continued use after refunds.

Continue reading …

Become an Acquired Taste

Seth Godin points out that people usually say "an acquired taste" as if that is something bad. I never thought about this phrase much, but he does have a point here. Indie developers and creators in general should consider to be a pleasure for the introduces few: that thought is in line with getting your first 1000 true fans, and everything the crew at Basecamp (formerly 37 Signals) around Jason Fried preach for years. Retain your individuality. Make something that is indispensible for people instead of making a throwaway mass product.

Continue reading …

UINavigationController Inside a Popover

Noah Gilmore wrote about his approach to use UINavigationController inside a popover without subclassing the navigation controller. Even though I don't work on iOS apps, like, at all these days, this sounds too useful to go by unnoticed.

Hopefully this was a helpful look into the world of preferredContentSize, view controller wrapping, and UIKit popovers. Here's a tl;dr:

  • To define the size of your popover with autolayout, set preferredContentSize to the result of systemLayoutSizeFitting
  • To animate popover size updates at the same time as navigation controller animations, wrap your UINavigationController in a PopoverPushController (see code below)
  • When you change your controller's preferredContentSize, be sure to change the preferredContentSize of your controller's popoverPresentationController's presentedViewController as well

See NGPopoverForceResizeTest, the resulting sample app on GitHub.

With DevMate Closing Shop, Here's What You Can Do

Teaser image

DevMate are closing their doors. The announcement isn't news anymore, but since I didn't use their service, I didn't think much of it. I did report back in 2016 when they changed their pricing model. Nowadays I discover more and more people struggling to migrate away, and my blog pops up in their searches – apparently because I wrote a book on selling apps on FastSpring.

Continue reading …

Prying Open the Vanilla Forum Control Flow to Create Discussions in Embedded Forms

We're using a Vanilla Forum for the Zettelkasten Method and ME Improved. It's a PHP-based forum, and I like how modular it's built. Writing plugins and themes isn't a pain, and that's something in my book already. It also allows us to power the comments with the forum. When you are the first to write a comment, the built-in "System" user creates a discussion with the blog post's title and a short excerpt and your comment is added.

Continue reading …

Detect When Your macOS App Has Been Moved During Runtime

Daniel Jalkut of MarsEdit fame released a little Swift helper to detect when a macOS app has been moved during runtime. His write-up explains everything. In short, moving an .app bundle while the app is running will inevitable result in resources not being found, and occasional also in crashes.

This or a similar helper is a practical necessity in all applications that are distributed outside the Mac App Store.

  • Check out RSAppMovementMonitor on GitHub.
  • The long defunct LetsMove used to be a tool that tried to prevent the issue by offering to move the app to /Applications upon launch from any other directory.

Live FastSpring Store Examples

Teaser image
The demo page with a modal shopping cart shows perfectly how you can make your own website into a store; you don’t need to switch to an e-commerce website provider at all!

I discovered the FastSpring Examples page the other day. There, you can interact with different store setups to get a feeling for the capabilities of FastSpring's new and updates Store Builder API. The examples also either include or link to documentation pages and CodePen-like source code demos.

All in all, I think this is a very cool addition to the docs.

When you play around with the examples, you will see that the new store styles aren't just for fancier checkout pages. The new store has an API that you can easily (!) use to transform even a static HTML website into a dynamic store front, including a live shopping cart. This is very, very powerful.

See also:

Looking for a Family Altar's Totem

Last December, my grand-father died. His breathing got shallow, he wasn't hungry all of a sudden, then went to take a nap in his favorite chair. There, he slowly, and I hope painlessly, began to tune out a bit, fall asleep, and eventually cease to … live. In the days after his death, I helped a bit with the funeral preparations, and I take care of my nearly blind grand-mother once a week ever since. I spent a lot of time with my grand-parents. We were pretty close. I love them, and so I begin to crave for some kind of totem that keeps a piece of my grand-father's live somewhere visibly in my life.

Continue reading …

Book Updates Underway

I am currently editing a new version of my ebooks to update them to the latest Swift and include new features. Writing prose instead of code for the better part of the week took some getting used to, but I really enjoy the change of pace. Especially now that my flat-mate got a 1-year-old dog that has to be guided about what it means to be living under a roof with both of us.

Continue reading …

Find Non-64-Bit Apps Using the Free Go64 Tool

From the St. Claire Software website:

Quickly scan your disk for applications, see which ones are 32-bit, and find upgrade information with the click of a button.

Go64 makes it easy to plan for the future.

And yes, it's FREE!

Find out which apps will stop working after Mojave and why. Does deep scans of apps, e.g. checking embedded frameworks and helper app bundles.

I used to use their Default Folder X about 10 years ago, and still use Jettison. I trust the dev.

The Archive v1.4.0 Released, Introducing Tabs

Teaser image

This is a pretty huge update. It introduces native macOS multi-tabbing to The Archive – which means you can now open multiple tabs or windows and have different search contexts active. We call these contexts "Workspaces". On macOS 10.12 and newer, you'll get native tabbing of windows; for macOS 10.11 and earlier you'll get multiple windows only.

Continue reading …

NiftyMenu -- Automatically Recreate a macOS App Menu Bar in HTML

Did you ever want to re-create the macOS Main Menu in HTML to show stuff related to your app? It's hard for me to accept that anyone would ever build this, but Brett Terpstra did.

Check out the live HTML demo. This is so weird! It's like you're running a Mac desktop in your browser.

If you write documentation for your app that includes menus, and you want to automate the process, taking into account the ever-changing nature of an app's menu during development, niftymenu might be just your thing! The menu can be grabbed from any app, and it comes with, well, nifty features for taking screenshots.

Paid Up Front: Two Perspectives to Make this Business Model Work to Your Advantage

I found an interesting connection between two articles about paid up front apps, and how this paywall can work to your advantage in two ways by separating the ap user's population into two groups, prospects and paying custoemrs: I never thought about the effect of paywalls on support email volume until Jordan Morgan launched his app Spend Stack the other day and now published an interesting argument pro paid up front pricing. Paid up front will limit your user base a lot compared to free-to-download/freemium, that's true, but you'll have a far lower volume of support emails, and you will only get emails from paying customers. Jordan receives 10–15 emails/day at 500 downloads/day right after launch. If you think freemium will increase downloads tenfold to 5000/day, he would also have to deal with hundreds of emails!

Continue reading …

The World's Most Comprehensive Guide to Implementing Programmatic Creation of Tabs in a Single NSWindowController That You Shouldn't Adhere To

This is a follow-up to "Programmatically Add Tabs to NSWindows without NSDocument" from January this year. There, I was creating NSWindow instances from storyboards and re-used a single window controller to manage them. The point of that post was to solve the problem that newly created tabs will themselves not respond to the "+" button – or any menu action, for that matter, because they fell out of the responder chain at first.

Continue reading …

How I Never Forget to Pack Stuff in My Backpack

I almost never forget to pack stuff for my regular trips to the gym, family visits, or shopping. Of course I sometimes do forget to pack something for a weekend trip, or I overpack books when I visit friends over the weekend and am afraid to run out of reading material during the train ride. But for the daily leaves of my appartment, I do not forget to pack keys, phone, money, writing utensils, sketchbook, etc.

Continue reading …

Convert MS Word DOCX Files to Markdown with Images

I don't know when was the last time I received a Microsoft Word .docx file. However long the streak may have been: it has been broken today. The document contained links and embedded images. I was instantly taken aback by the prospect of all the manual labor of extracting the images and saving them to files, not even knowing how MS Word behaves nowadays.

Continue reading …

Lightweight CrashReporter Library for Mac Released

The wording here is 100% Brent’s achievement. Love the humble tone.

I compiled a crash reporter library with CocoaPods support. Check it out:
https://github.com/CleanCocoa/CrashReporter/

The past couple of days, I've been working on integration of an automatic crash reporter. Turns out that on some machines The Archive is crashing regularly during search, and I want to track this down. I need data by more than the most courageous users who can venture into the Console to track down crash report files and send them to me.

So I bit the bullet and began work on a server script that would accept crash report files and email them to me. I think that's better than making users email me crash reports directly. Not everyone has Mail.app set up on her Mac, after all.

Fortunately, Brent Simmons open-sourced NetNewsWire 5 and blogged about his crash reporter. Brent has decades of experience on the Mac, so whenever he publishes a practical tip, I listen.

Just have a look at the code: Collecting .crash files and sending them over? Sounded simple enough! NetNewsWire even sports a very humble crash reporter UI, and automatically sending reports on the user's behalf if the user choses to do so. The required Swift types are simple enough to integrate once you figure out where Brent put all the helper extensions :)

I cannot stress enough how amazing open source is. All this experience, assembled in publicly available code repositories for everyone to grab and use. This is crazy.

I am now rolling this out for my apps, starting with the WordCounter and The Archive on their respective "beta" update branches to see how it works in practice. The Swift code is simple and I trust it doing its job. But the server script, well, it needs to be battle-tested now!

I'm going to add public-key encryption of messages and/or some sort of authentication to prevent malicious attackers from spamming me with emails that, well … I send to myself.

Move! Work Break Timer v1.4.0 Released

I just released an update to Move!, the work break app I develop and use. It fixes a couple of user experience issues, like displaying a Dock icon when you view the preferences so you don't lose the window.

Also, the app's preferences didn't display license details properly on Mojave and above. The app now uses Swift 5 and updated external libraries under the hood, is properly notarized (hello, Catalina!) – and a couple of MiB larger than before. I am looking forward to when we can depend on the Swift runtime being available on user devices.

Fixed Code Highlighting on the Blog

I manually edited about 400 occurences of code block markers from #!swift into \“swift`. I hope I didn't break anything in the process. So far, things look good. If you find oddly looking posts or broken code blocks, please tell me about them. There's no way I can find problems on my own in 10 years worth of blog posts :)

Continue reading …

Fold Current Level-1 Heading in Emacs Org-Mode Outlines

There's no standard shortcut to fold the current subtree of an org-mode outline. When I work in org-mode outlines, I usually am 3 or more levels deep into a so-called "subtree" and want to get back to the root item, fold it to hide the details, then drill down into another item. I use that when I am working on an app and want to have a look at a planned milestone nested deep down at a different point in the outline.

Continue reading …

Combine E-Book for Free

There is a free ebook out there that teaches you the basics of Combine, Apple's reactive framework introduced at WWDC 2019. It is pretty long already, given the time author Joseph Heck (@heckj) had to learn about Combine and then write about it. From what I saw, I think it's a good introduction to reactive programming in general.

Seriously, you should read it: https://heckj.github.io/swiftui-notes/

And the book's source is available, too! https://github.com/heckj/swiftui-notes

From the repository's name, swiftui-notes, and this description:

A collection of notes, project pieces, playgrounds and ideas on learning and using SwiftUI and Combine.

… I can only assume it gets expanded even more over time to include both SwiftUI and Combine!

Check it out and share it; Joe Heck deserves some internet love for his efforts.

Post Overview Updated

I don't know what's going on this week, but I have written a lot for the blog. I also carved out an hour and a half today to update the structured overview of articles on this website. I always wanted to make it a good entry point but didn't update it during the transition to another blog platform, then back again, and now it's 2 years later already. Phew!

Slides for the "More Money, More Control" Webinar on Selling Outside the Mac App Store

Today I noticed that I never followed up on my FastSpring webinar of 2017 called "Beyond the Mac App Store - A Practical Guide to Go From Code to Cash" with the slides! Sorry, folks! I'll also upload the checklist we served attendees. You can find the slides on slideshare.net, because I have an account there and don't know what else to do with PDFs: https://www.slideshare.net/DivineDominion/beyond-the-mac-app-store-a-practical-guide-to-go-from-code-to-cash

Continue reading …

TableFlip Mac App Store Experiment Stats: Sources

Here's some more background info for indie devs. To follow up the release of TableFlip on the Mac App Store and me noticing that yes, people buy the app without any marketing, I wanted to share sales origin stats today: according to the available data collected by the App Store, 100% of purchases were made after a search inside the Mac App Store so far.

Continue reading …

WordCounter for macOS Version 1.5.0 Released

I updated my app the WordCounter for Mac to v1.5.0 today. This update includes a couple of modernizations behind the scenes, but most importantly fixes problems that were related to the app being a Dock-less menu bar app. You couldn't manage any of its windows well. Now you can, because when any additional window is shown, a Dock icon is added on the fly.

Continue reading …

How Do You Activate Sparkle's XPC Services?

The Sparkle XPC fork is even better than I would've imagined! As I mentioned in the previous post about Sparkle's XPC branch, you can switch to the new framework version and the new types without having to change much else. The new API is very similar to the old one. If you switch to the work-in-progress ui-separation-and-xpc branch, then you can continue to use CocoaPods or Carthage if all you want is the new API.

Continue reading …

How to Migrate to the New Sparkle Updater XPC Branch

This is the first post in a series: The standard solution to provide app updates outside the Mac App Store is to use the amazing Sparkle framework. The releases of Sparkle don't support sandboxed applications well, though, so the core maintainer Kornel Lesiński (@kornelski) is working on a more secure fork of Sparkle next to the existing one. He's maintaining the regular Sparkle, and the fork. The fork features various XPC services to deal with Gatekeeper security configurations and Sandbox entitlements in a proper way. (I have to phrase it in such a vague way because I have no clue about the details, yet. Explanations are very welcome in the comments!)

Continue reading …

Use Shared NSUserDefaults for XPC and Interface Builder Nibs

The default Interface Builder approach of using a "Shared User Defaults Controller" object breaks down if your app preferences are not stored in the standard place. Both my apps The Archive and the Word Counter for Mac are comprised of multiple executable applications. For The Archive, it's the main app and the Quick Entry popup window. They share some settings, like which theme and font size is used. To share these settings, I rely on NSUserDefault (or just UserDefaults in Swift now). I cannot use the UserDefaults.standard, though, because that is tied to the currently running app's bundle ID. In the case of the main app, it's the ID of the main app; but for the Quick Entry helper – or any helper app –, it's the helper's bundle ID. This way, the defaults dictionaries are not shared.

Continue reading …

Imposing Bans and App Store Sanctions

Brent Simmons wrote about imposing sanctions by making apps unavailable in certain countries (in his case: Saudi Arabia) in November 2018. I never thought about the mere possibility of doing so. It's an intriguing thought: even when politics don't result in whatever you want, you can always be picky about who you do custom with. It's a power we have, a power every producer and craftsperson has. Turn down a business for moral reasons.

Continue reading …

From bash to zsh on macOS

Teaser image

In anticipation of macOS 10.15 Catalina, I have changed my shell from bash to zsh. macOS 10.15 will use zsh as the new default, and I was pretty sure that things will break immediately unless I prepare – so I did prepare, and I found the transition very simple.

Continue reading …

Browser-Like Navigation in The Archive

This weekend, I released an update to the note-taking app I'm working on for a while called The Archive. This update is pretty big for people not getting updates from the opt-in "Cutting Edge" update channel, because all of a sudden the app allows you to navigate back in time. The navigation stack (what you'd call browser history available from the navigation buttons in your browser) behaves like this:

Continue reading …

How to Add Files to mpd using python-mpd2

The music player daemon, mpd, and its client counterpart mpc operate on a managed directory structure. All paths are relative to this root directory. You cannot make mpd play a file from just anywhere, it seems. This is important to know when you script MPDClient using python-mpd2, because when you try to add any absolute path, even those pointing into the managed directory, you'll be in trouble.

Continue reading …

Emacs for Remote SSH Python Development

Teaser image

I am using Emacs for over a year now to manage my tasks. I like how I can mix tasks with long form notes in a single outline. It's good. We had to play with vi and emacs for a while at University. I'm very happy I got used to the very basics of both editors because I ended up using vi a lot when SSH-ing into remote machines, and now Emacs for everything else.

Continue reading …

Being Afraid to Change a Working App

Today I work on The Archive. The focus is on an issue brought up by the community. Search results don't update the sort order when you modify a note unless you refresh manually. In fact, the issue is expected behavior. The Archive, being a note-taking app where you can filter notes on disk with a live search, is designed to not update the search results for an active search term. Att all. This should prevent the note from disappearing from the results if you remove the search term from its contents. If you search for "foo" and get 10 results, the note you currently edit should not disappear when you cut the search term, "foo", from it. The Archive protects the search results; a mere live-reload would change the list to 9 results, removing the currently edited one, and that'd be pretty confusing.

Continue reading …

TableFlip Is Now Available on the Mac App Store

Teaser image

TableFlip is now available on the Mac App Store!

I also released updates to the non-Mac App Store version that fix CSV editing problems and improve the user interface. Of course both versions have the same features, so you're not missing out on anything if you only own one version.

The app store page is a feast for the eyes. There's a demo video (I already know how I can improve it a lot with the next update), Zebras, and lovely icy mountains.

Please share the news to help the app get traction. That would be super helpful!

Using Drag and Drop with NSTableView

Nate Thompson compiled a tutorial on how to implement drag & drop in NSTableView. It's a good read.

I remember how weird it felt to implement this the first time. Drag & drop is actually realized via the pasteboard. So it's more like cut and paste with a visual representation. From this you get the ability to put multiple content representations into the pasteboard at once, so the drop container can decide how to handle whatever it receives.

Hosting Downloads on Amazon S3 with CloudFront

Since early 2019, I host downloads for my app The Archive on Amazon's servers. The S3 bucket is a cheap-enough storage of the zip files, and the CloudFront cache is a content distribution network across the globe that improve download speeds. Here's a long tutorial, because I will most likely forget how I did all this in a while, and chances are you don't know how to do this either.

Continue reading …

Fixed the Blog Archive

I was looking for an old post and found that the paginated list of blog posts didn't quite contain all the posts I expected. Turns out I introduced an off-by-1 bug there, computing pages for 10 posts per page, but working with all_posts.each_slice(10-1), effectively dropping 1 post per page.

Of course my website isn't unit-tested, so no wonder :)

Add Navigation Buttons to NSTouchBar

Xcode and Safari sport Touch Bar items to navigate back and forth. Have a close look: When you put two NSTouchBarItems next to each other, there usually is a gap. Between the navigation controls, there is a mere hairline divider, but not the regular fixed-width space. They are not realized via NSSegmentedControl, though. Compare the navigation buttons with the system-wide controls far to the right: volume, brightness, play/pause. (I'm listening to the 1980s Pop Radio on Apple Music at the moment, in case you're curious.) The system controls are a NSSegmentedControl. They have rounded corners for the whole control, while the navigation buttons have rounded corners for every button. Also, the navigation buttons have the default button width.

Continue reading …

Do Not Apply Code Heuristics When You Need a Broader Perspective

You can only improve things inside the frame you pick. If your frame is too narrow for the problem you try to solve, you cannot properly take everything into perspective. That's a trivial statement as it's only re-stating the same thing, but it's worth stressing. Apply this to code. If you focus on code heuristics to improve your code base, you cannot improve the structure of your program. Even though the structure is manifested as code, it's not code you should be thinking about. It's concepts. Code is just the textual representation you stare at all day. Structure is what the imaginary entities of your invention bring forth.

Continue reading …

Refactoring The Archive's ReSwift Underpinnings

While I was refactoring my app's state recently, I noticed that there are virtually no dev logs about The Archive and how I manage state and events in this app. It's my largest app so far, still the amount of reflection and behind-the-scenes info is pretty sparse. This post outlines my recent changes to the app's state. It's a retelling of the past 5 weeks or so.

Continue reading …

Replace RxSwift View Model Types with Free Functions

Instead of writing view model types that expose the input and/or output ports as properties, consider writing free functions that are the transformation. This reminds me of Ken Scambler's proposal to get rid of test doubles through side-effect free function composition. One of his examples, simplified, goes like this: Instead of testing what customer.pay(amount, wallet) does in your tests by mocking the wallet object, you rewrite Wallet as a value object and change pay to return the effect.

Continue reading …

Programmatically Add Tabs to NSWindows without NSDocument

The Cocoa/AppKit documentation is very sparse when it comes to tabbing. You can make use of the native window tabbing introduced in macOS Sierra with a few simple method calls, though. Conceptually, this is what you will need to do: However, there are some caveats when implementing these methods naively. The plus button may stop working (no new tabs are added when you click it) and all default shortcuts are broken, their main menu items greyed out.

Continue reading …

NSAppearance Change Notifications for Dark Mode and RxSwift Subscriptions

Last year, I had a full-time job from May until November and haven't had much time to prepare for Mojave. Then it hit hard, and I am still tweaking my apps to look properly in Mojave's Dark Mode. I welcome that macOS offers two modes now, .aqua and .darkAqua in programmer parlance. Because then I don't have to implement a homebrew solution in all my writing applications.

Continue reading …