How to Fix Mach-O Header Code 0x72613c21 When You Try to Export Your App in Xcode

I was preparing a test build to check if linking against a new library worked fine in production. Trying to distribute the app using my Developer ID (but this would also have happened in a step before uploading to the App Store), I got this:

Found an unexpected Mach-O header code: 0x72613c21

Oops?

Like probably most developers, I have absolutely no clue about many crucial steps in the app making process. Especially stuff like code signing and most build settings elude me – I pick up pieces over the years, but not with the same speed with which I’m getting better at writing apps. So I didn’t understand what’s going on at all here. The search results I got weren’t that useful, so this post is meant to fill the vacuum for the next person running into this.

Didn't hurt to also make a video demo if you don't know where to click

Code 0x72613c212 Indicates You Are Embedding a Static Library

As far as I understand the message, embedding a static library in your app binary can produce code 0x72613c21. There may be other causes. But in short, this was the issue for me.

My app bundle is embedding a .framework bundle which in turn embeds a 3rd party library. The first embedding step is fine. The second isn’t, because the 3rd party library is a static library. I didn’t pay attention to this because I had no clue this is a problem in the first place.

An Apple Technical Note, TN2435, has an explanation for this. It’s a good resource, but it wasn’t among the first suggestions of my initial search; it did pop up when I asked the search engine why not embed static libraries xcode, though:

The following error indicates your app embedded a static library.

Found an unexpected Mach-O header code: 0x72613c21

This is caused by placing a static library in a bundle structure that looks like a framework; this packaging is sometimes referred to by third party framework developers as a static framework. Since the binary in these situations is a static library, apps cannot embed it in the app bundle.

How to Check If a Binary Is a Static Library

If you’re not sure if you have a static or dynamic library, TN2435 even includes instructions to check:

Terminal command to determine if a binary is a static library

file <PathToAppFramework>/<FrameworkName>.framework/<FrameworkName>

Example output for a static library

Mach-O universal binary with 2 architectures

<PathToLibrary> (for architecture armv7): current ar archive random library

<PathToLibrary> (for architecture arm64): current ar archive random library

I checked, and sure enough, this is the truncated output. Looks similar:

.../Versions/a/libMultiMarkdown: Mach-O universal binary with 2 architectures:
    [arm64:current ar archive random library] [x86_64:current ar archive random library]
.../Versions/A/libMultiMarkdown (for architecture arm64):
    current ar archive random library
.../Versions/A/libMultiMarkdown (for architecture x86_64):
    current ar archive random library

Compare this to a regular framework built in Swift:

.../Versions/A/JSONAttachment: Mach-O universal binary with 2 architectures:
    [x86_64:Mach-O 64-bit dynamically linked shared library x86_64]
    [arm64:Mach-O 64-bit dynamically linked shared library arm64]
.../Versions/A/JSONAttachment (for architecture x86_64):
    Mach-O 64-bit dynamically linked shared library x86_64
.../Versions/A/JSONAttachment (for architecture arm64):
    Mach-O 64-bit dynamically linked shared library arm64

There, you see “dynamically linked”, so conversely, the other isn’t.

This TN2435 I have just found when I wanted to look for more references for my own notes. I didn’t know about this yesterday when I ran into the problem. Most search results I found suggested using otool -h and otool -f instead. But I could not make sense of the results at all. And for the library in question, the static lib appeared to be the concatenated result of many .o files, so the output was crazy long, too.

Instead of even bothering with otool for library inspection, just check the binary product with file instead.

Fix: Do Not Embed Static Libraries

The solution, also mentioned in TN2435, is a simple change:

Once you’ve confirmed the library is static, go to the Build Phases for the app target in Xcode. Remove this library from any build phase named “Copy Files” or “Embed Frameworks.” The library should remain in the “Link Binary with Libraries” section.

By default, when you add a library to a framework or app target, Xcode chooses “Embed & Sign”. Was
the case for me as well here.

Change the library setting to 'Do Not Embed'

Change that to “Do Not Embed”, and you’re golden.

The result is that the framework is removed from the 'Embed Frameworks' build phase, but not the 'Link Binary With Libraries' phase; you can also perform this step manually, esp. if your Xcode version doesn't show the convenient 'Do Not Embed' dropdown

See also:

Open Magit for Current Repository from the Terminal

I’m having all my project in git repositories. And since I discovered the magic of Magit in Emacs, I sometimes want to have a familiar, interactive interface to select hunks for a commit without having to fire up a proper GUI app for stuff that I don’t already edit in Emacs.

In line with my recent idea to connect Finder and Emacs dired, I figured it might be nice to have a bash/zsh alias that visits the current directory in Emacs using Magit. (I use iTerm 2 as my terminal emulator of choice, not Emacs.)

With the emacsclient program, I can -e evaluate Lisp code from the shell. And with (magit-status PATH-TO-REPO), I can show the git repository at pwd in Magit.

After a short web search, I discovered some more elaborate aliases on Reddit and ended up combining the emacsclient invocation with a osascript call to bring Emacs to the foreground. The resulting alias is:

alias magit='emacsclient -a emacs -e "(magit-status \"$(git rev-parse --show-toplevel)\")"; if [[ -f `which osascript` ]]; then osascript -e "tell application \"Emacs\" to activate"; fi'

Split into a function for readability:

function magit () {
    git_root=$(git rev-parse --show-toplevel)
    emacsclient -a emacs \
        -e "(magit-status \"${git_root}\")"
    if [[ -f `which osascript` ]]; then
        osascript -e "tell application \"Emacs\" to activate"
    fi
}

If osascript is not found, e.g. on non-macOS systems, you won’t get an error. Looking at this for longer, it might make sense to extract an bringemacstofront alias in the future for reuse.

I don’t know how to write a “bring app window to front” for any flavor of Linux distro. Please share how you’d do that in the comments – I bet it’s different for every window manager of desktop environment anyway, but maybe there a universal X server command or something?

Two Improvements to Open macOS Finder Window in Emacs Dired + Automator Quick Action Downloads

In my previous post, I shared a function to fetch macOS Finder’s frontmost window path using AppleScript. The result was then opened in dired so you can import your Finder session into Emacs, so to speak.

Here are two improvements I discovered today when reading Sacha Chua’s Emacs News. (Downloads are at the very bottom.)


Redditor /u/cyanj shared that there’s a ns-do-applescript function we can use instead of calling out to the shell, spawning a new process. And it definitely feels faster!

If you want that, too, replace ct/finder-path with this:

(defun ct/finder-path ()
  "Return path of the frontmost Finder window, or the empty string.

Asks Finder for the path using AppleScript via `osascript', so
  this can take a second or two to execute."
  (let ($applescript $result)
    ;; Script via:  https://brettterpstra.com/2013/02/09/quick-tip-jumping-to-the-finder-location-in-terminal/
    (setq $applescript "tell application \"Finder\" to if (count of Finder windows) > 0 then get POSIX path of (target of front Finder window as text)")
    (setq $result (ns-do-applescript $applescript))
    (if $result
        (string-trim $result)
      "")))

ns-do-applescript returns the script’s string result, if possible, which I also find more straight-forward than creating a temporary background buffer and capturing contents there. It works in my emacs-head@28 via homebrew. If you build Emacs from source, check if the ns-do-applescript function is available at all.

If you have this in your init.el, wrap everything in (when (memq window-system '(mac ns)) ...) as usual so you don’t accidentally try to execute the ns- prefixed function on e.g. Linux.


The post on Reddit there originally pointed to Álvaro Ramírez’s “macOS: Show in Finder / Show in Emacs” article, which introduces another enhancement to my set-up.

Up until that point, I got this Finder interop:

  • Reveal current file/directory that I see in Emacs in Finder
  • Open Finder’s frontmost window in Emacs dired

Both require Emacs being focused. So I’m telling Emacs to do everything: remote-control Finder, or fetch its window’s path.

Álvaro now introduces a macOS Service so that you can start in Finder and then tell Emacs to open this location. So you don’t have to switch to Emacs and fetch the Finder window. Tell, don’t ask, if you will!

His AppleScripts, copied here for preservation, are:

  1. Reveal file or folder in Emacs dired

     #!/bin/bash
     current_dir=$(dirname "$1")
     osascript -e 'tell application "Emacs" to activate'
     path/to/emacsclient --eval "(progn (dired \"$current_dir\") (dired-goto-file \"$1\"))"
    
  2. Visit (aka open) file in Emacs directly

     #!/bin/bash
     osascript -e 'tell application "Emacs" to activate'
     path/to/emacsclient --eval "(find-file \"$1\")"
    

Both bash scripts need to be wrapped into Automator Quick Actions (used to be called “Services”) that take files or folders from Finder as input. The shell script action takes the input as arguments, not the default as stdin. Check out Álvaro’s post for screenshots.

I tweaked the lookup a bit and added common (?) paths to look for emacsclient so you don’t have to hard-code the current value into the Automator action. Plus I added error handling if emacsclient can still not be found:

...
# You may have to add the location of your emacsclient to PATH for lookup to work
PATH=~/bin:/usr/local/bin:/usr/bin:$PATH

if [[ ! -f `which emacsclient` ]]; then
  >&2 echo "emacsclient not found"
  >&2 echo ""
  >&2 echo "Script needs tweaking. Looked in PATH: ${PATH}"
  exit 1
fi
...

Since I have an Apple Developer ID, I figured I might as well bundle these actions up for you and also sign them so macOS doesn’t complain about the downloaded script. In about 6 months or so, we’ll probably be transforming these Automator actions to Shortcuts.app workflows, which I actually am looking forward to.

Download Reveal & Open in Emacs workflows

Open macOS Finder Window in Emacs Dired

Quite often I have a directory open in macOS Finder that I want to open in Emacs dired, e.g. to mass-rename files, copy stuff to a remote machine via SSH, or what have you.

In my last post, for example, I used Gifox to create a GIF and then viewed the result in Finder. To add the GIF to my blog post, I needed to copy it over into the appropriate website project directory. Since I was writing that post in Emacs already, I wanted to quickly copy the file from my Downloads folder (my default scratchpad for output like screenshots and screen recordings, including GIFs) over to the blog post’s location.

Now there’s two ways to move the file you see in Finder to a directory that you have access to in Emacs:

  1. Either reveal the Emacs file/directory a new Finder window (I am using reveal-in-osx-finder.el for that) and then use Finder to copy stuff between the directories;
  2. or visit the Finder window’s directory in Emacs and use dired to copy or move the file from one pane to the other.

Up until today, I couldn’t do the latter, and I quite like the experience of copying files between two dired panes, so this post documents how I ended up using AppleScript to fetch the Finder window’s location and jump to the path via dired.

Get Finder window location via AppleScript

I have a helper function to cd to the frontmost Finder window in the terminal for ages, based on a 2013 post by Brett Terpstra (of course):

# Part of my bash/zsh aliases file:
cdf() {
  target=`osascript -e 'tell application "Finder" to if (count of Finder windows) > 0 then get POSIX path of (target of front Finder window as text)'`
  if [ "$target" != "" ]; then
    cd "$target"; pwd
  else
    echo 'No Finder window found' >&2
  fi
}

This calls AppleScript from the command line to ask Finder.app for the frontmost window’s path.

This can be called from within Emacs, too, and I wrote a helper for that. Initially, I did overcomplicate everything and reacted to the exit code in case of AppleScript command failure instead of using Brett’s approach to just return an empty string when there’s no Finder window.

I’ll be using the same approach to access the Finder window’s location from Emacs.

Use osascript in Emacs to talk to Finder

What follows is not onw but two versions of more or less the same thing: asking Finder for the location using a command line invocation from Emacs.

I share both with you so you (and I) learn some Elisp along the way, because although the approach I’ll show first is simpler, I found the stuff I learned in the approach I’ll share last very educating, too. (And I ended up adding a couple notes to my Zettelkasten to document what I learned.)

The simple approach: a literal translation of Brett’s code

I shared Brett’s original AppleScript-based shell function above already. It either produces an empty string when no Finder window is open, or a POSIX path to the window’s location.

To call the osascript command line utility from Emacs, I use call-process and pass the AppleScript as an argument:

(defun ct/finder-path ()
  "Return path of the frontmost Finder window, or the empty string.

Asks Finder for the path using AppleScript via `osascript', so
this can take a second or two to execute."
  (let ($applescript)
    (setq $applescript "tell application \"Finder\" to if (count of Finder windows) > 0 then get POSIX path of (target of front Finder window as text)")
    (with-temp-buffer
      ;; Produce a list of process exit code and process output (from the temp buffer)
      (call-process "/usr/bin/osascript" nil (current-buffer) nil "-e" $applescript)
      (string-trim (buffer-string)))))

The trick here is to put both the STDOUT (and STDERR) output into a buffer. Wrapped in with-temp-buffer, this effectively captures the command output in an invisible background buffer and then returns the trimmed result as a string.

This is apparently the most simple approach to getting the string output of a shell command. Both shell-command and call-process are designed to insert the result in a buffer, e.g. to write a log, or insert the result right where you are typing. That’s probably useful when you want to e.g. select text and then call a program to act on the selection (e.g. uniq or tr, although there’s Emacs Lisp equivalents of course) and finally replace the selected text with the output from that command. – This focus on interactivity makes capturing the output as a string instead a bit awkward, though.

Update 2021-07-12: I’ve updated my script to use ns-do-applescript instead, which performs the same much quicker. See the follow-up post.

To open the path that’s returned here in dired (aka “jump” to the path), the following interactive function does the real work and produces a “No Finder window found” message just like Brett’s original shell function:

(defun ct/dired-finder-path ()
  (interactive)
  (let (($path (ct/finder-path)))
    (if (string-equal "" $path)
        (message "No Finder window found.")
      (dired $path))))

Now let’s have a look at my original, more cumbersome approach for everyone’s education.

The more complex approach that handles error codes

I ended up here because my actual cdf command line helper function produced a different AppleScript code – one that didn’t default to the empty string when no Finder window was open, but produce an error instead. I actually don’t find this more but less user friendly than Brett’s code from 2013.

To look up the code, I used which cdf in my terminal. It turns out that some zsh plug-in I installed along the way overwrote the code by Brett, so I got this:

$ which cdf
cdf () {
	cd "$(pfd)"
}

$ which pfd
pfd () {
	osascript 2> /dev/null <<EOF
    tell application "Finder"
      return POSIX path of (insertion location as alias)
    end tell
EOF
}

There’s no error handling, so without a Finder window open, the AppleScript execution will fail with an error as we’ll see below.

Also interesting to note: The AppleScript execution here pipes in a heredoc string via STDIN. Note that Brett’s original, shorter version uses the -e parameter to specify the AppleScript code as a string.

Initially, I tried to use shell-command, which also supports the backwards-piping using a heredoc. On top, this Elisp function allows you to specify a STDOUT and a STDERR output buffer to capture success and failure in parallel.

But everyone (including the Emacs manual) told me to not use this for programmatic shell command executions, so I switched to the slightly more cumbersome call-process. Using a heredoc was awkward with call-process’s separation of command name and parameters, so Iended up passing the script as a parameter instead using -e, too.

An upside of call-process over shell-command is that it returns the exit code of the command execution. So (call-process ...) will return 0 for successful execution of the command, or the error code (e.g. 1) upon failure. That arguably can make reacting to success and failures a bit simpler since the exit code is available. But then you need to react to both the output and the error code, which adds complexity.

That’s why the resulting variant of my function ct/finder-path produces a tuple or list of values, e.g. (list 0 "/path/to/window") on success, or (list 1 "XYZ Error Message beep boop") on failure.

(defun ct/finder-path ()
  "Ask Finder for the path of the frontmost window using AppleScript
and return the exit code as the 1st list item and the path as
the 2nd list item of the result.

This uses `/usr/bin/osascript', so it can take a second or two.

The return value is of the following form:
`(list PROCESS-EXIT-CODE PROCESS-OUTPUT)'.

For non-zero exist codes, you should treat the output as an
error message."
  (let ($applescript)
    ;; This concatenates the multi-line AppleScript into a string:
    (setq $applescript (mapconcat 'identity
                                  '("tell application \"Finder\""
                                    "  return POSIX path of (insertion location as alias)"
                                    "end tell")
                                  "\n"))
    (with-temp-buffer
      ;; Produce a list of process exit code and process output (from the temp buffer)
      (list (call-process "/usr/bin/osascript" nil (current-buffer) nil "-e" $applescript)
            (string-trim (buffer-string))))))

As a consequence, the actual “go to Finder window in dired” command unwraps has to work with the tuple of values and unpack the two elements to conditionally display a failure message:

(defun ct/dired-finder-path ()
  (interactive)
  (let* (($result (ct/finder-path))
         ($exit-code (car $result))
         ($output (cadr $result)))
    (if (eq 0 $exit-code)
        (dired $output)
      (message "Fetching Finder window failed (maybe no window open?):\n%s" $output))))

The minibuffer then displays this when no Finder window is open at all:

"Fetching Finder window failed (maybe no window open?):
72:77: execution error: Finder got an error: AppleEvent handler failed. (-10000)"

Yes, including the quotation marks. Not the prettiest, but serviceable.

I got the idea to return both the exit code and the captured output in a list from StackOverflow. The original author there suggests to extract a wrapper for this kind of command line invocation that looks very useful:

(defun process-exit-code-and-output (program &rest args)
  "Run PROGRAM with ARGS and return the exit code and output in a list."
  (with-temp-buffer
    (list (apply 'call-process program nil (current-buffer) nil args)
          ;; Note: I'm trimming the output to drop trailing newline!
          (string-trim (buffer-string)))))

(process-exit-code-and-output "which" "bash")
;; => (0 "/usr/bin/bash")

Since I didn’t end up using this approach and don’t act on the exit code at all, I didn’t bother to introduce this generalized process helper.

But it’s a neat idea that might come in handy (and which I hopefully remember later).

Limit the scope to macOS

To limit the availability of these functions to macOS (in case you also run Emacs on Linux or Windows), wrap the function defuns in:

(when (memq window-system '(mac ns))
  ;; paste defun's here
  )

I have this in my init.el and when I’m on macOS, I can invoke ct/dired-finder-path just fine, but don’t even see this function on Linux.

NSTextView Bug: Automatically Scroll Insertion Point Into View Broken When Setting NSTextContainer's lineFragmentPadding to Zero

Today I found that NSTextView has a bug where it won’t automatically scroll to keep the insertion point visible when you hit Enter or Return to insert a line break.

Usually, a text view keeps the insertion point visible as soon as the user types. You can scroll away, but as soon as the text view content change via user input, the insertion point scrolls into view automatically.

Unless you set NSTextContainer.lineFragmentPadding to 0.0: then it still works for all characters but line breaks.

The text view's scroll position sticks to y:0.0 when you hit enter.

This means when you’re at the bottom edge and hit Enter, your insertion point will vanish from screen. The expected behavior is for the text view to scroll down a line or two.

Any positive value that’s not 0.0 will work – including 0.1, which isn’t even a visible pixel’s width.

So if you are customizing a NSTextView in your app and find that entering line breaks doesn’t automatically scroll anymore, check that the lineFragmentPadding isn’t set to 0.0! The default value is 5.0, by the way, and setting this to 0 is probably a bad idea for your user interface anyway. Some horizontal margin from the edge of the text view makes the overall appearance look way less cramped.

(Submitted to Apple as FB9302224)

The example code is so simple that I didn’t upload it to GitHub since it fits into 20 lines of code. So if your code looks something like this, you’ll notice the problem, at least on Big Sur:

@main
class AppDelegate: NSObject, NSApplicationDelegate {
    @IBOutlet var window: NSWindow!

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        let scrollView = NSTextView.scrollableTextView()
        scrollView.translatesAutoresizingMaskIntoConstraints = false

        // Add scroll view to window
        let containerView = window.contentView!
        containerView.addSubview(scrollView)
        containerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[scrollView]|", options: [], metrics: nil, views: ["scrollView" : scrollView]))
        containerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[scrollView]|", options: [], metrics: nil, views: ["scrollView" : scrollView]))

        // Break scroll-insertion-point-to-visible
        let textView = scrollView.documentView as! NSTextView
        // textView.textContainer?.lineFragmentPadding = 0.1  // works
        textView.textContainer?.lineFragmentPadding = 0.0    // breaks
    }
}

TextKit 2 Introduces Block-Based Layout

I’ve just watched the TextKit 2 WWDC video. Without hands-on experience, I cannot say much about the tech, but judging from the presentation, all of the changes sound like an amazing step forward to make rich text-based interfaces nicer to work with. I didn’t particularly enjoy implementing some things in TextKit 1.

One thing that sticks out to me is the focus on blocks. TextKit 2 now comes with more ways to affect the layout and intersperse things based on the notion of inserting block-level elements; also called ‘paragraphs’ in some contexts. But not in the way you and I talk about paragraphs of text; in the technical understanding, we also treat empty lines between paragraphs of text as a spacing block element, and that’s also called a paragraph … So that’s why I prefer ‘block’, to avoid confusion.

The surprising element to me here is that this is somewhat in line with the syntax to format Gemini web pages about which I wrote last week. It’s an improvement over Markdown’s (accidental) edge cases and the use of inline styles. Gemini limits these to bold and italic text.

It’s no wonder that the writing app Ulysses switched from a pure-ish Markdown with all it’s weird cases and parsing problems to a structured format they call ‘Markdown XL’ see a comparison) where you can safely operate on the block-level first without having to look for the line above, below, or whatever to affect the result. (This also means it was introducing a new file format, but at least you can export easily.)

In a lot of cases (in most cases, maybe?), Markdown’s quirks aren’t relevant to users. – Does a novelist really need the ability to indent lists 4 levels deep with 4 spaces per level, and then mix in even further indented code blocks in between list items, but also wants to manually apply line breaking and thus manual indentation of the wrapping paragraphs that should not become code blocks? That’s a simple case to test your Markdown editor’s compliance with. And I think it’s utterly irrelevant in practice. In 3 years, I haven’t received a single complaint that The Archive’s syntax highlighting doesn’t support 100% of the original Markdown’s capabilities. Could be that people just want to write notes with a list or quote here and there, and this is inherently limiting the problem scope.

So TextKit 2 is going to be available to developers, soon, when macOS 12 hits, and working with text blocks is going to be simpler in a couple of years. That’ll mean easier insertion, nay, injection of images and other non-text attachments. I do wonder if this in turn will make non-block based decorations harder, like line numbers. I’ll report my findings.

– To kick things off, I’ve changed all my blog posts that were tagged #textkit to also carry a new #textkit1 tag. I don’t want to separate TextKit 2 and TextKit 1 tags completely, since there is overlap, so describing a superset #textkit with two subsets, #textkit1 and #textkit2, makes most sense. I also need a catchy name for block-based markups. I think we’ll be seeing more of these.

Further info:

Trash File from Emacs with Put-Back Enabled on macOS

I’ve enabled using the macOS system trash in Emacs to feel safer experimenting with dired and other file manipulation functions in Emacs.

This enables using the trash by default:

(setq delete-by-moving-to-trash t)

When this is enabled, move-file-to-trash is used to delete files. The manual reveals what’s affected, but only the function API docs from within Emacs tell the whole story (and are super hard to search online for whatever reason):

If the function system-move-file-to-trash is defined, call it with FILENAME as an argument.

Otherwise, if trash-directory is non-nil, move FILENAME to that directory.

Otherwise, trash FILENAME using the freedesktop.org conventions, like the GNOME, KDE and XFCE desktop environments. Emacs moves files only to “home trash”, ignoring per-volume trashcans.

So by default, the delete-by-moving-to-trash variable enablement does nothing.

Set the user-default trash path to make deletion move a file to trash:

(setq trash-directory "~/.Trash")  ;; fallback for `move-file-to-trash'

With this, all files are moved into the trash, but you cannot use the macOS ‘Put Back’ functionality. I expected an extended file attribute to control this, but it’s something else, apparently. Reading the attributes of files trashed by Finder doesn’t reveal anything.

But there’s a command-line utility called trash that can be told to use Finder’s functionality to move a file to the trash and enable put-back.

Successful trashing of a file, see the message in the minibuffer

So if we revisit the function docs, it says trash-directory is just a fall-back. The first attempt is made using the function system-move-file-to-trash if it exists. If we declare that function, we can use it to use the trash CLI app; and for good measure, we’ll limit it to macOS:

(when (memq window-system '(mac ns))
  (defun system-move-file-to-trash (path)
    "Moves file at PATH to the macOS Trash according to `move-file-to-trash' convention.

Relies on the command-line utility 'trash' to be installed.
Get it from:  <http://hasseg.org/trash/>"
    (shell-command (concat "trash -vF \"" path "\""
                           "| sed -e 's/^/Trashed: /'")
                   nil ;; Name of output buffer
                   "*Trash Error Buffer*")))

This just forwards the file path to the trash CLI utility and then prepends a string to format the output; trash -F uses the Finder trash functionality; trash -v returns the path name of the trashed file on success; and with the sed replacement, we get "Trashed: /path/to/file.txt". This output is printed in the minibuffer after deletion as you see in the screenshot.

References:

I'm Thinking About the Gemini Protocol a Lot

In the past couple of months, I’ve been thinking about the Gemini Protocol on and off. As a protocol, it’s a SSL/TLS-enabled text transfer protocol. The stuff it’s supposed to transfer is text files sorta similar to Markdown. And the client is supposed to render the result.

It sits somewhere between Gopher and HTTP.

If HTML is the language of the web we know via HTTP, Gemini’s text/gemini plain text result is the language of the Gemini protocol.

Unlike Markdown, which was designed to translate to HTML nicely and be human-readable at the same time, text/gemini doesn’t translate to HTML and is only made to be read the way it’s written; plus it’s made to be simpler to write parsers for.

I’m not thinking about the protocol implementation. I’m thinking about this plain text part.

I’m a huge fan of Markdown to write everything – my notes and my website content and my book manuscripts alike. But the text/gemini specs are captivating, too: they decide to not allow inline links at all. If you want to write a link, it must be written on one line per link. The whole plain text format is line-oriented. Here’s an example of a few links:

=> gemini://example.org/
=> gemini://example.org/ An example link
=> gemini://example.org/foo	Another example link at the same host
=> foo/bar/baz.txt	A relative link
=> 	gopher://example.org:70/1 A gopher link.

Clients typically seem to render this as a list of links where “An example link” is the click-able anchor text, and the target URL is not shown on screen. You know, just like HTML links, but more restrictive.

DistroTube.com in a HTTP browser to mimick the Gemini rendering

If you’re curious how that scales, check out Derek Taylor’s DistroTube.com page that is nowadays a rendered HTML output of his “Gemini Capsule”. (It was one of Derek’s videos that brought the topic to my attention.)

I am drawn to “Gemini” the topic, and text/gemini the format, because its promise of simplicity is still appealing. Just text; that’s nice. And the one-link-per-line policy resembles how I write my notes. Inline links are too noisy for my taste when I’m writing and consuming the plain text notes as they are. Rendering text to e.g. HTML is a different thing; but for notes, I often prefer to have an annotated reference per line.

The whole Gemini topic is some form of escapism, I think. And that’s not bad. Escaping the web of 2021 that is ridden with ads, full of cookie consent banners, and where … ‘weird’ platforms dominate the web traffic, it’s perfectly understandable that folks want to go someplace else. A Gemini Capsule might be just that thing.

It’s a nerdy past-time activtiy. You’ll find cool people in Gemini Space, for sure. But some say this is the future of the web – but the plain text world never appealed to the main stream users once GUIs game along. So don’t plan to transfer all your traffic over to gemini:// anytime soon, anticipating that the HTTP web is going to go away.

See also (in a list of links, one per line):

How to Print on macOS With An NSTableView and Customize the Result

So a client of mine recently asked how he could customize his app’s printing.

The app’s main window there mostly showed tabular data, so when you’re inside the NSTableView and hit ⌘P to print, the table view will redirect its draw(_:) output to the print dialog. That’s how you get printing for free.

Note: Since NSView.draw(_:) is the basis for the printed output, CALayer settings will be effectively ignored.

How to customize how a view is drawn when printed

If you want to customize the printed output, you can check which NSGraphicsContext is active when you override the drawing routine. Adapted from the old Apple docs:

class MyView: NSView {
    override func draw(_ dirtyRect: NSRect) {
        if NSGraphicsContext.currentContextDrawingToScreen() {
            // draw for screen
        } else {
            // draw for print
        }
    }
}

How to customize the view hierarchy that’s printed

My client wanted to customize the printed output beyond the drawing routine. The default table view drawing is fine. But he wanted to prepend a descriptive text before the table. The page headers won’t do because they repeat on every page. The printed page should contain the table view and a multi-line label that’s only visible when printing.

Printing relies on NSView, so we can use NSStackView to lay out contents on the page for printing. This requires a rebinding of the print shortcut from the system default to any custom @IBAction function so we can handle the command ourselves.

Create a NSPrintOperation programmatically

That changes the question from “how can we customize drawing a view” to “how can we define the content on the page”. This in turn boils down to bringing up the printing panel with custom content like this:

let sheetHostingWindow: NSWindow = ...
let viewToPrint = NSStackView()
// ... prepare the stack view ...
let printOperation = NSPrintOperation(view: viewToPrint)
printOperation.runModal(
    for: sheetHostingWindow,
    delegate: nil,
    didRun: nil,
    contextInfo: nil)

That’s it – create a NSPrintOperation and then show the system printing panel for the print operation.

I whipped up an example project with the resulting code. Check it out to see everything.

So if you ever created a view hierarchy programmaically, you can easily fill in the gaps to prepare the view to be printed. Note that you can also load a view hierarchy from Nib and print that. Either way, make sure the Auto Layout constraints fill the page.

Prepare programmatically created views for printing

I found that the main view that’s going to be printed produces the best output if you set its frame to the page size, excluding the page margins. This information can be changed in the system default “Print Setup” dialog, for example. Again, I got the idea from the archived docs:

extension NSPrintInfo {
    var availableContentSize: NSSize {
        NSSize(width:  paperSize.width  - leftMargin - rightMargin,
               height: paperSize.height - topMargin  - bottomMargin)
    }
}

// ...

let initialFrameForPrinting = NSRect(
    origin: .zero,
    size: pageContentSize)
let stackView = NSStackView(frame: initialFrameForPrinting)
// set up view and then create NSPrintOperation as seen above

I’m not sure why setting the initial frame to the available page size helps, since the view easily resizes and is printed onto multiple pages as needed. But for smaller stack views, this will try to fill the available page without zooming-in on the content. This could be a quirk in how the printed view is embedded into the page, maybe Auto Layout isn’t used when printing and that affects the result, maybe it’s a stupid oversight somewhere else in the code – but if you run into a similar problem, here you go.

Armed with this knowledge, you can now prepare your custom view hierarchy for printing and start the print sheet with it.

How to print a NSTableView

Print dialog with a wrapping label that's only part of the page, not the app's window

In the sample project, I’ve added a multi-line label just before the table view. And to print the actual table view, I added a new table view for printing. So the stack view that’s being printed has 2 arranged subviews.

Printing a custom view hierarchy with the same table data that you see on screen won’t work if you move the on-screen table view over. I tried, that’s hacky and looks weird. When you take the tableView from your app window and put it into the stack view for printing, it’ll be removed from the app window. Any view can only have 1 parent view.

Programmatically creating a new NSTableView instance worked best. This required a couple of adjustments to the approach.

The client wants to keep Storyboards. So the table view on screen is set up in a Storyboard, the table view on the page is set up in a Nib or programmatically. (I chose the programmatical route to mix things up and show how it’s done.)

In order to have the on-screen and the for-print table views populate from the same data source, we can actually reuse the view controller that is the NSTableView.delegate and dataSource. That will prepare the table data, but we do have to move the creation of NSTableViewCells all into the code. The cells cannot be configured in the Storyboard alone: the Storyboard references aren’t known to the programmatically created table view, and adding this connection sounded like more trouble than it was worth.

Instead of pointing the programmatically created table view to the cells in the Storyboard, I made the table view from the Storyboard obtain its cells programmatically. To get an experience that’s pretty close to the stuff you see in Nibs or Storyboards, use this, cobbled together from docs and StackOverflow of course:

extension NSUserInterfaceItemIdentifier {
    static let cell = NSUserInterfaceItemIdentifier("my_custom_cell")
}

class TableCellView: NSTableCellView {
    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        self.identifier = .cell

        let textField = NSTextField.label()
        textField.autoresizingMask = [.width, .height]
        self.textField = textField  // is an 'unsafe' reference
        self.addSubview(textField)  // creates a 'strong' reference

        textField.bind(
            NSBindingName.value,
            to: self,
            withKeyPath: "objectValue",
            options: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

extension NSTextField {
    /// Return an `NSTextField` configured like one created
    /// by dragging a “Label” into a storyboard.
    class func label(title: String = "") -> NSTextField {
        let label = NSTextField()
        label.isEditable = false
        label.isSelectable = false
        label.textColor = .labelColor
        label.backgroundColor = .controlColor
        label.drawsBackground = false
        label.isBezeled = false
        label.alignment = .natural
        label.controlSize = .regular
        label.font = NSFont.systemFont(
            ofSize: NSFont.systemFontSize(for: .regular))
        label.lineBreakMode = .byClipping
        label.cell?.isScrollable = true
        label.cell?.wraps = false
        label.stringValue = title
        return label
    }
}

The view controller in turn relies on the Cocoa Bindings of NSTableCellView.objectValue to the text field and has very simple data source and delegate implementations:

extension ViewController: NSTableViewDelegate, NSTableViewDataSource {
    func numberOfRows(in tableView: NSTableView) -> Int {
        return items.count
    }

    func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
        return items[row]
    }

    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
        return tableView.makeView(withIdentifier: .cell, owner: self)
            ?? TableCellView()
    }
}

This works for the table view from the Storyboard and the custom table view for printing. If you have multiple columns, you have to adjust everything, of course.

To top things off and document everything in the example project, here’s the table view creation code as well:

let tableViewForPrint = NSTableView(frame: .zero)
let soleColumn = NSTableColumn()
soleColumn.resizingMask = .autoresizingMask
tableViewForPrint.columnAutoresizingStyle = .lastColumnOnlyAutoresizingStyle
tableViewForPrint.allowsColumnSelection = false
tableViewForPrint.allowsColumnResizing = true
tableViewForPrint.addTableColumn(soleColumn)
tableViewForPrint.selectionHighlightStyle = .none
tableViewForPrint.allowsEmptySelection = true
if #available(macOS 11.0, *) {
    // Avoid Big Sur's default horizontal padding in print-outs
    tableViewForPrint.style = .plain
}

tableViewForPrint.dataSource = self
tableViewForPrint.delegate = self
tableViewForPrint.reloadData()

Some alternative approaches to creating a new table view for print:

  • Use the on-screen table view’s draw(_:) method and call it where appropriate. Think of it like a canvas: this could be a custom NSView subclass that forwards intrinsicContentSize and draw(_:) to the table view to replicate the pixel output.
  • Create a copy of the on-screen table view. I tried copy() and serializing and deserializing to NSData, but that didn’t work, and it was so weird I abandoned it. But maybe this could work?
  • Add everything you want to print to the NSWindow, but set isHidden = true, and then un-hide when you print. Could be a cheap cop-out that produces visual glitches.

Conclusion

To wrap things up:

  • If you want to customize the drawing routine of a custom view when it’s being printed, you can check NSGraphicsContext.currentContextDrawingToScreen().
  • If you want to print different content than is shown in the app, you may want to create a custom view hierarchy for print. NSStackView is an excellent choice for vertical layouts.
    • If you also want to share the table date on screen and on paper, a good choice is to create a new NSTableView with the same data source and add this to the printed view hierarchy.

See also:

Single Function to Center Emacs Window on Screen

Fellow Emacs-user Göktuğ Kayaalp condensed my frame centering stuff into one function as I mentioned one could do, and then improved it by adding a condition for full-screen frames. This is probably the last post on this topic, ever, because what else could be said. Right?!

It’s so nice, I want to share the code, reformatted a bit. And Göktuğ didn’t add the binding of frame to selected-frame as a fallback, so I added that back in, plus longer docs.

(defun my/frame-recenter (&optional frame)
  "Center FRAME on the screen.
FRAME can be a frame name, a terminal name, or a frame.
If FRAME is omitted or nil, use currently selected frame."
  (interactive)
  (unless (eq 'maximised (frame-parameter nil 'fullscreen))
    (let* ((frame (or (and (boundp 'frame) frame) (selected-frame)))
            (frame-w (frame-pixel-width frame))
            (frame-h (frame-pixel-height frame))
            ;; frame-monitor-workarea returns (x y width height) for the monitor
            (monitor-w (nth 2 (frame-monitor-workarea frame)))
            (monitor-h (nth 3 (frame-monitor-workarea frame)))
            (center (list (/ (- monitor-w frame-w) 2)
                          (/ (- monitor-h frame-h) 2))))
      (apply 'set-frame-position (flatten-list (list frame center))))))

(add-hook 'after-init-hook #'my/frame-recenter)
(add-hook 'after-make-frame-functions #'my/frame-recenter)

I wasn’t aware that instead of add-to-list a hook for after-make-frame-functions would do the job, too. That’s what you get from following some of the docs on the web to the letter. The after-init-hook is a great addition, too.

Since I have this auto-centering functionality in place, I find myself opening and closing new frames quite a lot. For most of 2020, I have been using a single frame, often in full-screen mode, and then have been splitting panes and experimenting with workspaces and tabs. Using the macOS windowing system instead integrates much easier with the rest of my computer life. (Though I still envy the Linux folks with their tiling window managers who use multiple tabs in emacs. Looks so tidy!)

You can also run this function in the terminal no problem. It will just exit with t and you won’t see any effect. The important part is that there are no errors when you’re not in a GUI.

See also:


→ Blog Archive