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!

My xcopen adventures: playground workspaces

Now that Xcode 12 supports Swift Packages for playgrounds, I thought it was time to expand xcopen to build not only playgrounds but also allow you to embed them in workspaces.

xcopen in a nutshell

If you’re not familiar with xcopen (I’ve only mentioned it briefly on this website), it’s my answer to xed. It does what xed does more or less and adds more features that I use a lot.

I built xcopen to handle command-line activities that I regularly perform during development. If you run it without arguments, it looks for a workspace and then opens that. If no workspace is found, it looks for xcode projects and playgrounds. If you pass it file names and paths, it opens those instead.

OVERVIEW: 
xcopen ...        Open files in Xcode.
xcopen docs              Open .md and .txt files.
xcopen new               Create new files (if they don't exist), open in Xcode.
xcopen xc|ws|pg(w)       Open xcodeproj, workspace, or playground.
                           * Add ios|mac|tvos to create playground.
                           * Add w (pgw) to create playground in workspace.
xcopen pkg|xpkg          Open Package.swift in TextEdit or Xcode.

USAGE: xcopen [ ...] [--background] [--folder] [--open] [--no-open]

ARGUMENTS:
                   Files to open. If blank, opens xcworkspace or,if not
                          found, searches for xcodeproj. 

OPTIONS:
  -b, -g, --background    Open Xcode in the background 
  -f, -e, --folder        Enclose new items in folder 
  --open/--no-open        Open newly created playgrounds/workspaces (default:
                          true)
  -h, --help              Show help information.

Shortcuts let you gather up your docs (like README.md, CHANGELOG.md, and LICENSE.txt) and open them together for edits.

You control whether Xcode opens in the foreground or background, enabling you to keep working without Xcode taking up your immediate attention.

Recently, I added support for playground creation. Need a Mac playground? xcopen pg mac. It emulates Finder naming  so there won’t be naming conflicts. Instead, it builds macOS, macOS 2, macOS 3, etc as your root playground names. Based on feedback from my Twitterati pals (waves hi!), I added a flag that lets you group them together in a subfolder if you don’t want multiple playgrounds cluttering your working directory.

Adding Workspaces

Today, I decided to start working with Swift packages, so I added workspace creation:

xcopen pgw mac --folder

Using pgw builds both a playground and an associated workspace. Adding --folder embeds them both into a new folder. Otherwise they are created in the working directory.

Using Swift Package Support

Add any folder containing a Swift Package to your workspace:

  • Files > Add files to workspace name (may be greyed out); or
  • Project navigator contextual pop-up > Add files to workspace name; or
  • Or just drag the folder above your playground entry in the Project navigator to ensure you’re not adding it directly to your playground.

If your package has dependencies, they’re listed in the Project navigator.

Next, try importing the new package. If it doesn’t autocomplete, quit and restart Xcode and re-open your workspace. For some reason, in this early beta it doesn’t seem to get picked up immediately.

Then test out the functionality you’ve imported. In the following example, I’m using a custom exponentiation operator:

Wrap up

I’m using xcopen a lot these days, tooling it to make my workday easier. If you find a feature you think I should include please open an issue at github. And if you like the utility, do let me know. Thanks!

The easiest way to install xcopen is via mint, which you can install with brew. Once you have mint, all you have to say is mint install erica/xcopen.