Piping stdout and stderr to Preview

A while back, I wrote about how handy it was to redirect a man page into Preview. This allows you to keep the man page open, search it, and generally have a better user experience than struggling with more (or less) to navigate through the information provided there.

man -t apropos | open -fa Preview

Recently, someone asked me about more modern command line interaction, specifically, commands that use --help or similar to provide their documentation. Could that information be opened in Preview as well.

So I put on my thinking hat and set to work. The first command line utility I decided to work with was screencapture because I’ve been using it fairly heavily over the last few days. However, it appears that Apple failed to build in an actual help system beyond man. It was a poor choice to try to use to render but I decided to keep plugging away at it because I wanted to be able to pipe both stdoutand stderr to Preview.

What I came up with looked something like this, all in one line of course:

bash -c "screencapture -? &> 
    $TMPDIR/previewrendertext.txt; 
    /usr/sbin/cupsfilter -i text/plain 
        -D $TMPDIR/previewrendertext.txt 
        2> /dev/null | 
    open -fa Preview"

This all relies on cupsfilter, which can convert a file of text to a printable form, which just happens to be readable by Preview as a PDF.

I’m doing quite a bit of conglomeration, joining the stderr and stdout streams using &> and saving them into my Mac’s $TMPDIR. That file is cleaned up by the -D option from cupsfilter.

I also am removing the incessant debug messages from cupsfilter by redirecting them to /dev/null before opening the print output in Preview.

Please note that I’m still using tcsh/zsh over bash on my main system, so that certainly affects things. Since I needed a little of the bash nuance, I decided to run it all squished as a single -c command. (I’m sure if I spent enough time, I could do it all in csh but I really didn’t want to spend that time.)

As you can see in the previous screenshot, an older utility meant for man output doesn’t really look all that hot shoved into Preview via cupsfilter, especially with line lengths. There’s also no nice groffing and troffing to make everything pretty, the way you get with man:

So how could would this kludge work with a modern command-line app, such as those produced using the Swift Argument Parser (https://github.com/apple/swift-argument-parser)? First, I built a utility that would let me run any command (well, so long as it was properly quoted) without having to type all the details out each time I ran it:

#! /bin/bash

$@ &> $TMPDIR/previewrendertext.txt ; /usr/sbin/cupsfilter -i text/plain -D $TMPDIR/previewrendertext.txt 2> /dev/null | open -fa Preview

This allowed me to call preview "now --help" to redirect the standard help message from my now utility (https://github.com/erica/now)  to Preview. Yeah, originally I wanted to just pipe stuff into it but I couldn’t figure out how to get the stderr and the stdout piped together into a single stream, let alone convert them into a file form because cupsfilter doesn’t know or do pipes.

It’s pretty readable and well-formatted due to the automatic configuration that the Swift Argument Parser provides from my code but it just feels, you know, very very plain.

So I went ahead and tried to see what would happen if I groffed it up a little by passing it through /usr/bin/groff -Tps -mandoc -c instead of using cupsfilter:

bash -c "now --help &> 
    $TMPDIR/previewrendertext.txt; 
    /usr/bin/groff -Tps -mandoc -c 
    $TMPDIR/previewrendertext.txt" | 
    open -fa preview

And it’s…pretty meh. I tried mandoc, mdoc, me, mm, ms, and www formats. They all came out the same, and none of the SAP tabs really worked. I think it looks a lot more “manny” than the straight printout but the indentation really bugged:

I decided to stop at about this point as there’s really a time when further effort just isn’t worth further investment — so I could throw it out there and see if this was of interest to anyone else.

Let me know.

Xcode: Vimpocalypse Now

This has appeared at the bottom of Xcode’s Text Editing > Editing settings, sure to make many vi users very happy indeeed:

The change is heralded in the Release Notes:

Using the new vim features is super easy. When the Vim keybindings are enabled, the bottom of your source editor gets this, showing current state, plus a few reminders for those whose muscle memory isn’t quite current. It’s up to you to know your hjkluybn stuff.

You can tell if you’re navigating or editing by the shape of the cursor. The cursor is a large block when you’re navigating or using any of the commands listed here. It is a vertical pipe when directly entering text.

You can tap i to insert text, switching to text entry mode and then ESC to return to navigate. Using a colon : doesn’t do anything here, so no :wq, which is not a huge surprise as neither w nor q makes a ton of sense in Xcode.

Anyway, if your a vimficianado, congratulations and enjoy the new toy.

Controlling Screen Sharing from the command line

My world often narrows to Xcode and Terminal. There are times I just want to check in on another computer quickly and I don’t want the hassle of creating a new Finder window, going to the network, waiting for the items to load, and clicking Screen Sharing.

Let me share a quick osascript solution I use instead (and there’s an even better solution for launching just using open, thanks Chris). This example connects to Esopus Spitzenburg, my Mojave computer (aka the one that still runs everything including Photoshop and Microsoft Office).

#! /bin/sh

/usr/bin/osascript -e 'tell application "Screen Sharing" to GetURL "vnc://Esopus-Spitzenburg.local"'

If closed, the Screen Sharing application launches in the foreground. If already running, it stays at its relative hierarchy. You can add a command to activate to bring it to the front on each invocation, even if the connection is already active.

If you’d like to let the utility toggle visibility on call instead, add a request to Finder and append it to the script:

/usr/bin/osascript -e 'tell application "Finder" to set visible of process "Screen Sharing" to not visible of process "Screen Sharing"'

I have written similar utilities to open Broxwood Foxwhelp and Glockenapfel, depending on the computer I’m using. As much as I’ve intended to write a single utility instead of two or three dedicated scripts, I’ve can’t convince myself it’s worth the effort.

(Bonus points: Why are my computers named this way?)

Crafting a custom word count service

I just happened to need to do a lot of word counts today so I put together a service to make my life easier. While, I performed my initial work on Mojave but the same approach works all the way to Big Sur and, presumably, the upcoming macOS Malibu Barbie.

Open Automator

To get started, launch automator and create a new document.

Select Quick Action, Choose from the new document dialog:

Add Scripting

Drop in a Run Shell Script and then an Apple Script. You can search from them in the top-left corner. Drag them in order into the right panel.

I used /bin/bash for my shell script, shocking, I know, as I am well known for my love of all things csh. Feel free to use whatever shell suits you. The first non-argument shell variable ($1 here) corresponds to highlighted text, which can be used by the system contextual menu:

echo `echo $1 | wc -w` words. `echo $1 | wc -c` characters.

Switch the pop-up for piping output (“passing input”) from “to stdin” to “as arguments”. This allows the AppleScript to read the selected data and present a dialog. If you forget, you’ll get empty input and something like “1 words, 0 characters” all the time.

on run {input, parameters}
    display dialog input as string buttons {"OK"}
end run

Save and Run

Save the action. I called mine “Word Count”.

The action automatically saves to ~/Library/Services, in case you want to find or delete it in the future.

% ls
Word Count.workflow/

Your new quick action automatically adds a custom service to your contextual pop-ups. Just highlight anything you want to count, from text on a web page to your work in a document and open the contextual menu:

Make sure the output looks reasonable. For easiest testing, copy the text to your clipboard and then use wc directly in terminal.

And, then boom, you are done.

Let me know if this was helpful.

Pasting quoted code perfectly

You have some code you need to incorporate into a multi-line string. What’s the quickest and best way to handle it? Although I see people do this all the time, manually adding spaces to each line isn’t the best solution.

Here’s a quick Xcode tip:

  • First, paste your material into scope. Retain the indentation by using Edit > Paste and Preserve Formatting.
  • Next, if you haven’t placed them already, add the assignment and triple-quotes above and below the pasted material.
  • Select your material and use Editor > Structure > Shift-Right (Command-]) to line up the left edge of the text with the closing triple-quote. This command moves all selected material n spaces to the right, depending on how you’ve set up your tabbing. There’s a matching Shift-Left if you indent a little too much.

Hope this helps someone.

Executing command-line directly from Xcode

I got pulled into one of those conversations where I end up saying, “Fine, I’ll put up a post about it” and this is the post. Yes, you can test and run command-line apps directly from Xcode but I pretty much never do. It’s a pain with few benefits. That said, here’s how you do it.

Arguments

Let’s say you need arguments. Open your scheme (⌘<) and select the Run > Arguments tab. Add the arguments you want to pass on launch one at a time. Double-click to edit any argument:

The arguments are vended byCommandLine.arguments. Either count the array or use CommandLine.argc to find out how many arguments you’re dealing with.

print(CommandLine.arguments)
print(CommandLine.argc)

Counter-intuitively, Xcode does not automatically quote the arguments for you. This produces five arguments, not three, or six if you include the command itself:

["/Users/ericasadun/Library/Developer/Xcode/DerivedData/Test-gwehknnihlcsiucsovtbnlrdtfun/Build/Products/Debug/Test", "first", "second", "third", "fourth", "fifth"]
6

And what do you expect from the following?

You get this if you run directly in Xcode’s console:

["/Users/ericasadun/Library/Developer/Xcode/DerivedData/Test-gwehknnihlcsiucsovtbnlrdtfun/Build/Products/Debug/Test", "first", "several items at once", "third"]
4
Program ended with exit code: 0

But if you set your code to execute using Terminal:

Launching: '/Users/ericasadun/Library/Developer/Xcode/DerivedData/Test-gwehknnihlcsiucsovtbnlrdtfun/Build/Products/Debug/Test'
Working directory: '/Users/ericasadun/Library/Developer/Xcode/DerivedData/Test-gwehknnihlcsiucsovtbnlrdtfun/Build/Products/Debug'
3 arguments:
argv[0] = '/Users/ericasadun/Library/Developer/Xcode/DerivedData/Test-gwehknnihlcsiucsovtbnlrdtfun/Build/Products/Debug/Test'
argv[1] = 'first'
argv[2] = 'several'
["/Users/ericasadun/Library/Developer/Xcode/DerivedData/Test-gwehknnihlcsiucsovtbnlrdtfun/Build/Products/Debug/Test", "first", "several"]
3

Xcode’s Crazy Terminal Option

If you’re running anything with direct key input (using POSIX termios/raw mode) or curses, running in the console doesn’t work. So Xcode provides a way to run those utilities in the terminal. Visit Run > Options and scroll all the way down.

This feature is buggy as hell, produces ridiculous amounts of excess text (see this), can take a significant time to launch, and even more time for Xcode to realize the process has finished. It is impossible to use with paths that use spaces (“warning: working directory doesn't exist: '/Volumes/Kiku/Xcode/Derived'“).

I don’t like it. I don’t use it. But it exists.

Sane Command-Line Execution

Unless you’re dealing with things like automation and such, you can try out your compiled command-line apps by dragging your executable from the Products group onto the terminal. This places the path to your build at the prompt. Type out your arguments and press return:

However, I prefer to use a Copy File build phase. Select your Target > Build Phases, click plus (+) and add the executable. (I use absolute path and disable “only when installing”.) This lets you install directly to  standard locations like /usr/local/bin or ~/bin, or if you don’t want to place it there until it is stable and ready for deployment, you can use a development folder:

Assuming your destination is in your shell’s path, start a new shell for the executable to be picked up the first time. After that, you can compile and run as you like.

A different way to develop SwiftPM Packages inside Xcode projects

WWDC gave us many reasons to both migrate libraries to SwiftPM and to develop new ones to support our work. The integration between Xcode development and SwiftPM dependencies keeps growing stronger and more important.

Apple’s Editing a Package Dependency as a Local Package assumes you’ll drag in your package to an Xcode project as a local package overrides one that’s imported through a normal package dependency.

In Developing a Swift Package in Tandem with an App, Apple writes, “To develop a Swift package in tandem with an app, you can leverage the behavior whereby a local package overrides a package dependency with the same name…if you release a new version of your Swift package or want to stop using the local package, remove it from the project to use the package dependency again.”

I don’t use this approach. It’s not bad or wrong, it just doesn’t fit my style.

On the other hand, opening the Package.swift file directly to develop has drawbacks in that it doesn’t fully offer Xcode’s suite of IDE support features yet.

So I’ve been working on a personal solution that best works for me. I want my package development and its tests to live separately from any specific client app outside a testbed. I need to ensure that my code will swift build and swift test properly but I also want to use Xcode’s built-in compilation and unit testing with my happy green checks.

I set out to figure out how best, at least for me, to develop Swift packages under the xcodeproj umbrella.

I first explored  swift package generate-xcodeproj. This builds an Xcode library project complete with tests and a package target. You can use the --type flag to set the package to executable, system-module, or manifest instead of the default (library) during swift package init:

Generate% swift package init
Creating library package: Generate
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/Generate/Generate.swift
Creating Tests/
Creating Tests/LinuxMain.swift
Creating Tests/GenerateTests/
Creating Tests/GenerateTests/GenerateTests.swift
Creating Tests/GenerateTests/XCTestManifests.swift
Generate% swift package generate-xcodeproj
generated: ./Generate.xcodeproj

Although SwiftPM creates a .gitignore file for you as you see, it does not initialize a git repository. Also, I always end up deleting the .gitignore as I use a customized global ignore file. This is what the resulting project looks like:

As you see, the generated Xcode project has everything but a testbed for you. I really like having an on-hand testbed, whether a simple SwiftUI app or a command line utility to play with ideas. I looked into using a playground but let’s face it: too slow, too glitchy, too unreliable.

It’s a pain to add a testbed to this set-up, so I came up with a different way to build my base package environment. It’s hacky but I much prefer the outcome. Instead of generating the project, I start with a testbed project and then create my package. This approach naturally packs a sample with the package but none of that sample leaks into the package itself:

I end up with three targets: the sample app, a library built from my Sources, and my tests. The library folder you see here contains only an Info.plist and a bridging header. It otherwise builds from whatever Sources I’ve added.

I much prefer this set-up to the generate-xcodeproj approach, although it takes slightly longer to set-up. The reason for this is that SwiftPM and Xcode use different philosophies for how a project folder is structured. SwiftPM has its Sources and Tests. Xcode uses a source folder named after the project.

So I remove that folder, add a Sources group to the project, and ensure that my build phases sees and compiles those files. The Tests need similar tweaks, plus I have to add a symbolic link from Xcode’s tests name (e.g. “ProjectNameTests”) to my SwiftPM Tests folder at the top level of my project to get it to all hang together. Once I’ve done so my green checks are ready and waiting just as if I had opened the Package.swift file directly. But this time, I have all the right tools at hand.

Since I’m talking about set-up, let me add that my tasks also include setting up the README, adding a license and creating the initial change log. These are SwiftPM setup tasks that swift package init doesn’t cover the way I like. I trash .gitignore but since I have Xcode set-up to automatically initialize version control, I don’t have to git init by hand.

I suspect this is a short-term workaround as I expect the integration of SwiftPM and Xcode to continue growing over the next couple of years. Since WWDC, I’ve been particularly excited about developing, deploying, and integrating SwiftPM packages. I thought I’d share this in case it might help others. Let me know.

The (Switch) Case of the Missing Binding

Here’s a cool little challenge brought up this morning by a friend. Consider the following code:

switch foo {
  case .a: return "a"
  case .b(let str) where str.hasPrefix("c"), .c: return "c"
  case .b: return "b"
}

It won’t compile.

When you bind a symbol for one pattern, you must bind that symbol for every pattern in a case. This prevents you, for example, from binding str in one pattern and then attempting to use str in the shared case body. For example, consider this case. What would you expect to happen when foo is .c?

func switchTheFallthroughOrder(foo: Foo) -> String {
    switch foo {
    case .a: return "a"
    case .b(let str) where str.hasPrefix("c"), .c:
        // Using `str` here is bad!
        print(str)
        return "c"
    case .b: return "b"
    }
}

Despite my first knee-jerk refactoring, moving out the .c case to use fallthrough doesn’t work. Again, this is because str is not bound for .c and could be used in the successive case body:

However, as Greg Titus pointed out, if you switch the order to use the binding case first with fallthrough, Swift knows at compile time that the binding won’t carry on beyond that scope. This resolves the error, since str is only used in the where clause to narrow the pattern matching:

Further, when using bindings in case tests, a waterfall approach where the bound items are used before fallthrough can extend through multiple steps with the blessing of the compiler:

case .widest(let first, let second) where first.satisfiesACondition():
    // can use `first`, `second` here
    fallthrough
case .medium(let second) where second.satisfiesAnotherCondition():
    // can use `second` here even if it was bound 
    // via `widest` above via fallthrough
    fallthrough
case .narrowest: return someValue

My thanks to Greg Titus for figuring this all out!

Well that was a surprisingly bad Apple Store experience…

Remember the battery amnesty? Despite the stores doing everything they could to try to convince me not to replace the batteries, I insisted and persisted. I figured $29 would buy me the start of a new battery life-cycle.

Less than 2 years later, my daughter’s iPhone SE battery is dead.

Let me try to explain how important her iPhone is to her. She goes everywhere with it: to stores, in the car, at appointments. There is no time when she’s not tapping on it, from morning until she sleeps. It’s one of those neurodiversity things and it is her great comfort.

It took us a couple of weeks until we could finally snag an appointment last week and the appointment was for today. I have just returned.

It seems that instead of replacing her battery, they gave her back her original one–and her original iPhone, apparently. They had planned on replacing the phone as well, which I remember we were told was an option, but didn’t. The genius figured this out because her serial number was supposed to retired at the time they traded it out but instead they never did.

But because of that her device as a serial number that the Apple corporate system considers invalid. We could not get a loaner. We could not get a repair. And we won’t be able to even start our process for a few weeks more because the serial number issue must be addressed before they can even talk to us about the bad battery.

So after driving almost an hour each way, we got, well, nowhere.

No phone, and no timeline in which we can estimate how to move forward. Our only option, according to the genius, was to buy a new phone and wait it out. I declined the purchase.

So, they’ll look into it and we should get a phone call before August to figure out the next steps and set up another appointment. And start the process all over again.

Update 1:  Call in to Apple. Senior advisor (“I’m as high as you can escalate”) says: “This is the store’s problem. We can’t handle it here.” She offered to let us pay (again) for the battery replacement that was never done. After almost 3 hours on the phone, she got the Park Meadows coordinator into the call and I’ll pick up with them tomorrow.

Update 2: Final outcome: Apple will do the work we paid for almost two years ago within the next week or so! No apologies. No refunds for the work they didn’t do. No kind words about being sent home from the genius bar without any action. They offered to give me a $30 trade-in for the SE on a new phone — exactly what the online store would normally offer for a used SE.

Update 3: Just a few minutes after hanging up with Park Meadows, this arrived in my mailbox. I don’t intend to pay $269 for a repair we already paid for. Seriously, WTF?

Importing Web-based SwiftPM packages to your Xcode Playground

I’ve been kicking the wheels on Xcode 12 and its ability to use frameworks and packages with playgrounds. Up until now, I’ve only been able to import packages that are either downloaded or developed locally on my home system. However, a lot of the packages I want to work with are hosted from GitHub.

I decided to follow a hunch and see if I could import my dependency through a local Forwarding package and then use that code. Long story short: I could.

Here’s my playground, successfully running.

The RuntimeImplementation is declared in a GitHub-hosted package called Swift-General-Utility:

What I did to make this work was that I created what I called a Forwarding Utility, whose sole job is to create a shell package that depends on the remote package and forwards it to the playground. It looks like this. It is a single file called “Forwarding.swift” (no, the name is not at all magic.) in Sources/. I use @_exported to forward the import.

/*
 
 Use this to forward web-based dependencies to Swift Pkg
 
 */

@_exported import GeneralUtility

Its Package.swift installs the dependency:

    dependencies: [ .package(url: "https://github.com/erica/Swift-General-Utility", .exact("0.0.4")), ],
    targets: [
        .target(
            name: "ForwardingUtility",
            dependencies: [ .product(name: "GeneralUtility"), ],
            path: "Sources/"
        ),
    ],

And that’s pretty much all that there is to it, other than (as I mentioned in my other post about how to use SwiftPM packages in playground workspaces) that you may have to quit and re-open the first beta before you can import the forwarding.

Let me know anything that I messed up. But also let me know if this was helpful to you!