Archive for the ‘How To’ Category

Converting spreadsheet data to Markdown

I find it’s a lot easier to prepare a table with Numbers than by hand. I was using this today and thought I’d share the how-to.

Step 1: Select the material you want to table-ize.

Step 2: Paste it into text edit. Numbers uses tab delimiters (at least it does for me).

Step 3: Use find/replace to replace all tabs with a vertical pipe symbol (|). Copy any of your tabs to populate the find field.

Step 4: Use find/replace to replace all newlines with the sequence pipe symbol – newline -pipe symbol. Copy a newline from your text to the find field and in the replace field type |, then paste the newline again, and then type | again.

Step 5: Add pipe signs to the start and end of your file. Before:

After:

Step 6: Duplicate the first line and replace all text between bars with dashes:

Step 7: If you have an empty left-top-corner like I do, add at least 3 dashes to the 2nd line between the first set of pipes:

And you’re done.

Pushing the gist envelope: gists with pics and zips

People often don’t realize how powerful GitHub’s gist pasting service is. It’s more than just a “paste” site. Gist offers a full version control system extension to GitHub’s main site. I’ve been working on developing version control training materials and gist is a great way to introduce the fundamentals.

Many gist users know that the site offers you pushbutton convenience to paste one or more files. You can create open gists and “secret” ones hidden from public view. Gist also lets you fork, revise, and explore diffs between revisions.

For example, you can work in groups when writing. Collaborators can fork and make changes to offer content feedback. You can then use diffs to see what edits were made.

Here are some diffs from a recent Raw String proposal I worked on:

And this is the corresponding “rich diff”, which is slightly prettier:

And there’s a lot more you can do with gist. That’s because gists, as version control repositories, can be cloned to your computer, modified, and pushed back to GitHub. This means you can, for example, set up albums of pictures or host an easy-to-distribute zip file.

Each gist URL is a repository’s address:

git clone https://gist.github.com/erica/7cd24c6ab2f737735a9ab2b95628c549

As a gist’s owner, you have commit privileges, allowing you to edit your gist from your computer.

The command line enables you to add binary files that you can’t from the web interface. I grabbed a bunch of kitten pictures from Pexels and added them to my gist. A nice way to create simple albums:

If you click “Download ZIP” at the top right, GitHub zips up the repository contents (in this case five kitten PNGs) and copies them to your computer. This is not cloning; the zip file just stores the source files, not the full git repo.

It’s just as easy to host an archive file. When you have an Xcode project or playground that you need to share, Gist provides a great intermediate service alternative to iCloud or Dropbox. If you need privacy, use the “secret gist”  button when creating the gist.

This isn’t, of course, the end of what you can do with gists. Because each gist is a git repository, you can perform all the same commands you would in any git repo. Gist, of course, has a limited interface, so you won’t be able to, for example, switch between branches from the gist website. On the other hand, you can perform other tasks that don’t depend on a GitHub UI like listing diffs:

% git diff cb9271da5070f11602d3ab436a05fb9705409fd2
diff --git a/raw.md b/raw.md
index 8ed7306..ea4b5ed 100644
--- a/raw.md
+++ b/raw.md
@@ -104,9 +104,8 @@ Escaping hinders readability and interferes with inspection, especially in the l
 
 ### Candidates
 
-A good candidate for raw strings:
+A good candidate for raw strings is non-trivial and is burdened by escaping because it:
 
-* Is non-trivial.
 * Is obscured by escaping. Escaping actively harms code review and validation.
 * Is already escaped. Escaped material should not be pre-interpreted by the compiler.
 * Requires easy transport between source and code in both directions, whether for testing or just updating source.

I think GitHub’s gists are pretty awesome. And now, at least for me, they’ve gone from handy but mindless pastes to something really special.

Do you have a special way to use gists? I’d love to hear about unconventional ways to use this utility site to push boundaries and introduce new functionality. Drop a comment or an email and let me know.

Using TextEdit as your git editor

For a little while, I’ve been using emacs as my git editor because I strongly feel that vi is something that should happen to other people. It’s been fine but not everyone loves emacs or likes the debris it leaves in its wake. So I thought I’d try out TextEdit instead.

After a little testing of approaches, I first tried out this:

git config --global core.editor /Applications/TextEdit.app/Contents/MacOS/TextEdit

I picked this rather than some of the other approaches because it’s straightforward in launching the app. There’s one problem though, which is that git doesn’t pick up on the fact that a file has been edited and closed when the application remains active:

% git commit --amend 
hint: Waiting for your editor to close the file... 
(...waits forever...)

The commit completes and works when I quit TextEdit but I don’t like  to quit the app each time I update a commit, especially if I have other files open. The amended commit does complete on quit:

% git commit --amend [master 59a0934] Testing TextEdit!
Author: erica <erica@ericasadun.com>
Date: Thu Jul 12 08:02:26 2018 -0600
1 file changed, 3 insertions(+), 1 deletion(-)

So I changed my approach. I turned to open instead:

git config --global core.editor "open -W -n"

TextEdit is open‘s natural environmental editor for text. You don’t have to specify more than open for git to work with the right editor.

This approach uses two tweaks. When you launch an app with open, the -n flag instructs open to launch a new instance of the application even if one is already running. You may see two “TextEdit” icons open in your dock, for example.

Combine -n with -W and you have a slightly better solution.  The -W “wait” flag tells open to keep waiting until the launched application has finished running. This allows git to to wait “for your editor to close the file”, which you do by quitting TextEdit. Sure, I’d much prefer that TextEdit run using a “single file” mode, but this isn’t a bad solution.

Sadly, I couldn’t find any undocumented launch flags to make TextEdit run in single file mode and while it’s easy enough to write a Mac app that edits a single file at a time and quits on closing, it’s not a practical solution for everyone.

Is there a way to automatically propagate “done” back to git without ending a TextEdit process? I couldn’t find one. If you have a better idea, please let me know.

My Mac mini, kernel_task, and dusty hardware: Bring back my mini’s zing

Over the past few weeks, my Mac mini has been getting less and less responsive. The kernel_task has been spinning up taking up ridiculous amounts of CPU time and dragging my system to its knees.

I did what anyone would: I ran hardware diagnostics (passed). I ran SMART diagnostics (passed) and then I web searched the hell out of the interwebs, and there I found an unusual suggestion. Several sites suggested that I may have built up dust accumulation in my mini and that the fan control was trying to mitigate this.

Fortunately, I’m one of the lucky ones (for very low values of “lucky”) with a self-serviceable 2012 Mac mini instead of the upgrade a few years later that removed the service hatch. (*shakes fist*. *darn you Apple.*)

I opened it up and sure enough it was pretty dirty in there. I vacuumed and then I used compressed air to finish (after finding the child who had “acquired” it for an “important project” several months ago and never returned it). I performed a thorough vac and dust of the work area where it rests along with my many backup and external data drives.

Plugged it back in and for nearly a week so far I have not had the same issues reoccur. It seems a tiny bit black magic but it also seems to be working (fingers crossed).

I thought I’d write up a quick post to share my experience and see if anyone else has been through this and has advice or suggestions. For me, it’s like having my Mac back again. I still need a refreshed mini (hint Apple hint) but I have serious concerns about buying one without a self service hatch.

Apple’s move away from self-service products has cemented its appeal as a “computing appliance” provider but it also means expensive service calls for any problem. Much as I’d like them to give us a Mac Pro Lite, at a lower price point than the Pro and a higher one than the mini, with lots of configuration options,  I just cannot see that kind of product ever existing in today’s Applesphere.

A lot of us mini owners cling tenaciously to our six-year-old hardware (or, for some, 4 year old), but I’ve seen no indication that the mini has a future at all in the lineup. If it’s not a product appealing to the BMW owner set or essential for internal Apple use, I don’t really see it having a place in Apple’s marketplace.

One indication against this gloom is Apple’s magnificent iPod line, which has at least gotten semi regular updates. The iPod touch is about the best value you can buy with an Apple logo on it outside of the core iPad released this past March. Both fit into if not BMW owner appeal, then at least BMW owner’s kids appeal. I just don’t see the Mac mini doing that.

Is the mini dead forever? I’m not sure. I like bringing my own displays, my own mechanical keyboard, and thousands and thousands of dangled USB add-ons. That’s what a desktop does for me and why the (self service) mini is the perfect fit.

If you want to point at one change Apple made that said to me: “No mini for you, move to the left!” a la that Seinfeld episode, it was the death of the escape key. The escape key philosophically services a self-reliant power user. And I’m just not sure that Apple is in that market anymore.

Will I get my mini pro? Probably not. Will there be a paradigm shift so profound that customized dev systems can co-exist with mechanical keyboards using a new desktop system? Maybe. What do you think? Are you a mini devotee? Share your thoughts.

Better initializers and defaulted arguments

Yesterday, I was discussing initializing UIEdgeInsets. Developer Adam Sharp cleverly added computed properties to leveraging keypaths by extending the type to adopt ExpressibleByDictionaryLiteral:

extension UIEdgeInsets: ExpressibleByDictionaryLiteral {
    public typealias Key = WritableKeyPath<UIEdgeInsets, CGFloat>
    public typealias Value = CGFloat
    
    public init(dictionaryLiteral elements: (WritableKeyPath<UIEdgeInsets, CGFloat>, CGFloat)...) {
        self = UIEdgeInsets()
        for (inset, value) in elements {
            self[keyPath: inset] = value
        }
    }
}

This approach lets you use a dictionary literal to initialize your type:

let insets: UIEdgeInsets = [\.left: 8]
print(insets) // (l: 8.0, r: 0.0, t: 0.0, b: 0.0)

Pop in a few custom properties, specifically horizontal, vertical, and all, and you have a really cute way of initializing edge insets without building a plethora of custom initializers, keeping the API boundary (Thanks Daniel J) nice and compact:

public extension UIEdgeInsets {
   public var vertical: CGFloat {
        get { return 0 } // meaningless but not fatal
        set { (top, bottom) = (newValue, newValue) }
    }
    
    public var horizontal: CGFloat {
        get { return 0 } // meaningless but not fatal
        set { (left, right) = (newValue, newValue) }
    }
    
    public var all: CGFloat {
        get { return 0 } // meaningless but not fatal
        set { (vertical, horizontal) = (newValue, newValue) }
    }
}

Unfortunately, you must supply a getter: a WriteableKeyPath is a “key path that supports reading from and writing to the resulting value.” (Emphasis mine.) That’s why I included the silly return 0 statements for each getter. I originally put in a fatal error but that only got me grief because the values were being read before writing.

Incidentally, Swift does not allow you to build a write-only type for compound abstractions like these. Just in case you were thinking of going that way with your code, here’s what you can expect:

With the dictionary-initializable approach, you may use a dictionary literal with as many or as few key paths as you need to fully customize your instance:

let insets2: UIEdgeInsets = [\.vertical: 8, \.horizontal: 20]
print(insets2) // (l: 8.0 , r: 20.0, t: 8.0, b: 20.0)

let insets3: UIEdgeInsets = [\.all: 8]
print(insets3) // (l: 8.0 , r: 8.0, t: 8.0, b: 8.0)

Stephen Celis notes, “The nice thing about key paths are they’re compiler generated code. you can write a single initializer function and get everything for free without having to define one-off enums or initializers every time.”

This approach is generally useful enough that it’s worth abstracting out a little to support dictionary literal initialization for any type with uniformly-typed property members such as CGRect or CGPoint. Nate C came up with a very clever approach to do exactly that. Here’s a modified version of his approach:

/// Allows dictionary literal initialization for any
/// conforming type that declares `typealias Value`,
/// where `Value` refers to a uniform property Type
/// that can be set through a keypath-value dictionary
///
/// - Example:
///   ```
///   extension CGPoint : UniformKeypathInitializable {
///     public typealias Value = CGFloat
///   }
///
///   let p: CGPoint = [\.x: 0, \.y: 20]
///   ```
public protocol UniformKeypathInitializable : ExpressibleByDictionaryLiteral {
    /// Allow zero-argument initializer
    init()
    
}

extension UniformKeypathInitializable {
    /// Initializes each member of a keypath-value
    /// dictionary, allowing the type to be initialized
    /// with a dictionary literal
    public init(dictionaryLiteral elements: (WritableKeyPath<Self, Value>, Value)...) {
        self.init()
        for (property, value) in elements {
            self[keyPath: property] = value
        }
    }
}

You provide a typealias for `Value`, which in this case means the type of the values supplied in the dictionary, and the magic happens for you.:

extension UIEdgeInsets: UniformKeypathInitializable {
    public typealias Value = CGFloat
}

That’s all it takes. Add the custom compound properties and you’re good to go.

Interestingly enough, during this process, I came across possibly the most inscrutable Swift error message ever (which I believe is saying something). Here’s one of my early attempts before I found Nate’s solution, and the error it produced:

Gotta love Swift.

Anyway, if there are errors in the post, fixes, improvements, or suggestions (and you know there always will be), let me know. Email, tweet, comment, whatever you like. Thanks as always!

Carrying user-sourced code forward in Swift Playgrounds for iOS

Had a really neat challenge today, as a Slack-buddy attempted to work with Apple’s exquisitely insufficient Playground Book documentation. His goal was simple: he wanted to be able to incrementally grow and test code from page to page, copying the user’s work as they moved on.

In theory, Swift Playgrounds for iOS enables you to build books where your reader/student incrementally builds code. Each page introduces a new concept, a new tweak, or a new approach. It’s a great way to layer each lesson on a previous take, or to take one lesson and branch it out to multiple endpoints.

You can either build, build, build to one big story or take one core concept and show many different ways to apply it. Either way, you want to be able to bring code — whether from the most recently edited page or from a shared core page — forward, so the reader/student can further engage with it, edit it, and make it fit with each page’s challenge.

Implementing “code forwarding” (I just made up that term) proved trickier than expected. Playground book workflow is often “understood” (that is, you have a deep understanding of what’s required because you’ve worked with it a lot or you’ve poked around at Apple’s examples or reversed engineered to see how things work) rather than explained step by step in the official docs. Because of this, he ran into several roadblocks along the way.

  • First among these, is that Apple does not provide a Playgrounds Book Author tool for Mac. You have to build your books by hand, going through the specs and hoping that each iteration works. Most of the time it does. Sometimes, maddeningly, it does not.
  • Second, you have to transfer the book to the iPad for each test (I use AirDrop™), and guess at what went wrong if it doesn’t work. When testing a series of book-based exercises, you have to either hard-code each “success” sequence (and there’s no way to set a “I am debugging/developing this book” flag) or actually do the coding, which can take a lot of time, especially if you’re debugging page 7 and you have to work through the exercises on page 1 – 6 for each test of page 7.
  • Finally, if your book is even a little out of spec with what Swift Playgrounds for iOS expects, it’s going to die without much feedback or explanation, leaving you scratching your head, cursing Dev Tools (but we really love you guys, we do), or otherwise venting frustration.

I wasted a bunch of hours because I wanted to make this work. And finally, I managed to get things working to my satisfaction. I thought I’d take the time to write things up to save you some bother. Here are a few things I learned while deep diving into today’s experiments.

The Zen of User Code

Code-forwarding allows you to propagate user-sourced/user-edited code from an earlier page in a playground book to a later page in a playground book. When you code-forward from a “source” to a “destination”, Swift Playgrounds for iOS makes a copy of the earlier code and places onto the current page.

That code is copied once and you aren’t given the option to re-copy unless you reset the page. Every page in Swift Playgrounds offers a reset option in the ellipsis menu, but its discoverability is low. Apple expects each reader/student to work through exercises linearly, progressing only when each previous problem is solved. This means that you don’t get “live” updates by popping back and making new changes to the source page. The destination copies once.

That also means you cannot apply code-forwarding until your page is set as complete. By “complete”, I mean that the book’s source code and Swift Playgrounds accept that the reader/student has done sufficient work to move forward and progress to the next page/exercise.

This usually happens by executing a page epilogue. The epilogue tests the state of the page’s data, determines if the problem was solved (for example, whether the robot reached the end square and the code progressed to a hidden portion containing this test), and then updates a user assessment. Unless a page’s assessment status is “passed” (that is, done), the reader/student is not offered code copying on the following page.

This is built into Swift Playgrounds for iOS, and is an underlying assumption on how progressive learning plans operate. It’s a critical pathway for building page-by-page progress and enabling code-forwarding. This is why the following snippet includes its bit of hidden code. This code allows a user to pass, that is receive a passing grade/assessment for the page, without doing any more work than running the current page:

//#-copy-source(id1)
//#-editable-code 
func foo() {
    // ... starter code here
}
//#-end-editable-code
//#-end-copy-source

//#-hidden-code
import PlaygroundSupport
PlaygroundPage.current.assessmentStatus = 
    .pass(message: "Great job!")
//#-end-hidden-code

Building Code-Forwarding

The preceding code incorporates two essential parts of using a copy-source markup area.

  • First, is the actual tagged copy-source code. This delimits the code that gets copied forward to one or more other pages. Make sure to mark it editable when you want to present a challenge requiring end-user-reader modification. You can omit editable tags when you want the next step or branches to start with code you source yourself. It’s an unusual approach but it’s not illegal to do so.
  • Second, is the hidden assessment update. Normally you’d use more sophisticated logic to determine whether a reader/student has met those challenges laid out in the current page before allowing them to .pass or .fail. When you just want to demonstrate core functionality, make it clear in your marked-up write-up that the user must run the code before continuing. Use the approach in this code to “pass on first run” for demonstration. You’ll probably want to update the message to something along the lines of “Great! You’ve seen this work, move to the next page to start making changes.”

Building The Destination

Crafting a destination page is trickier than laying out acopy-source area: You must update your page’s manifest as well as its content source. The manifest will expect properly internationalized source strings. That means at a minimum, a code receiving page will need a Contents.swift file, a Manifest.plist file, and a PrivateResources folder with at least one localized lproj folder (in my case, en.lproj), which in turn holds the ManifestPlist.strings file.

Here’s what a simple manifest looks like for a copying destination. Keep in mind that each value entry for the CodeCopySetup keys is actually a placeholder for localization.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CodeCopySetup</key>
	<dict>
		<key>CopyCommandButtonTitle</key>
		<string>CopyCommandButtonTitle</string>
		<key>DefaultCommandButtonTitle</key>
		<string>DefaultCommandButtonTitle</string>
		<key>NavigateCommandButtonTitle</key>
		<string>NavigateCommandButtonTitle</string>
		<key>NotReadyToCopyInstructions</key>
		<string>NotReadyToCopyInstructions</string>
		<key>ReadyToCopyInstructions</key>
		<string>ReadyToCopyInstructions</string>
	</dict>
	<key>Description</key>
	<string>Description</string>
	<key>LiveViewEdgeToEdge</key>
	<true/>
	<key>LiveViewMode</key>
	<string>VisibleByDefault</string>
	<key>MaximumSupportedExecutionSpeed</key>
	<string>Fastest</string>
	<key>Name</key>
	<string>Name</string>
	<key>PlaygroundLoggingMode</key>
	<string>Off</string>
	<key>Version</key>
	<string>1.0</string>
</dict>
</plist>

I followed Apple’s example in my ManifestPlist.strings file, so the English expressions aren’t terribly exciting. The Name field used in the manifest is spelled out in addition to the button text:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CopyCommandButtonTitle</key>
	<string>Copy My Code From the Last Page</string>
	<key>DefaultCommandButtonTitle</key>
	<string>Start Coding on This Page</string>
	<key>Name</key>
	<string>Copying Text from the Previous Page</string>
	<key>NavigateCommandButtonTitle</key>
	<string>Return to Previous Page</string>
	<key>NotReadyToCopyInstructions</key>
	<string>Be sure to complete the previous page before you move on to solving the next step.</string>
	<key>ReadyToCopyInstructions</key>
	<string>You can bring over your algorithm from the previous page to continue improving it.</string>
</dict>
</plist>

Here, each possible assessment state and action is given a human-readable form. I was unable to make the system “default” to the items mentioned in the Playground Page Manifest documentation (such as “Copy my code” and “Start with provided code”). I’m sure if I tried hard enough, I could have gotten this working per the docs but I didn’t have the time to push.

In building the code-destination contents, link each identifier you used in the source (it’s id1 in this example but it can be any key you want to use) and the page to copy from (Page1). This page names comes from the name of the playgroundpage file hosting the user-edited or user-sourced content.

You must mention the page because you may keep enhancing the same progression of code from page to page, while using a single identifier. If you start on page 1, update on page 2, when you get to page 3, you want to copy from the updated source on page 2, not page 1. Mentioning which source you want to copy helps keep you and the user on track.

//#-editable-code
//#-copy-destination("Page1", id1)

//#-end-copy-destination
//#-end-editable-code

If you’re using a branching storyline (for example, you might explore variations on a sort or showcase different blending modes for merging images), you can place this destination code on each branch page.

More often, you’ll want to progressively modify code through a series of exercises. To carry the code further, add copy-source tags around the destination as in the following code, using the same id1 identifier, and refer to #-copy-destination("Page2", id1) for the next copy on Page 3 and so forth. Read this directive as this is the copy destination for the code tagged with id1 sourced from page 2.

Here’s what an edit-and-carry approach looks like for a second page, referring back to ("Page1", id1). In my imagination, this is the first time code has been copied and this markup sets up a user-editable progression that can be carried to the third page and beyond.

//#-copy-source(id1)
//#-editable-code
//#-copy-destination("Page1", id1)

//#-end-copy-destination
//#-end-editable-code
//#-end-copy-source

That’s pretty much all you need: proper tags, proper localized strings, and proper id/page references. If you’d like to try out a copy of my playground, you can grab a copy from here or email me for a copy if that doesn’t work.

Fixing Mail Plugins for High Sierra

Are all your flagged emails back? Do you want them gone? Mail plugins help make macOS mail a bit less awful. If your Mail bundles stopped working, it’s not hard to get them back up and running. The secret lies in adding a `Supported10.13PluginCompatibilityUUIDs` entry in to any mail plugin bundle’s internal Info.plist file. You can then move the plugin back from the disabled folder to the main plugin folder.

The overall technique changed in 10.12, requiring a separate entry field that’s OS-specific. You can pull the current compatibility key by issuing:

defaults read /Applications/Mail.app/Contents/Info.plist PluginCompatibilityUUID

This reads the compatibility UUID and prints it to the terminal command line. You need this UUID to edit the Info.plist file. Starting in 10.12, you need to use the XX.YY format as part of the key name. For example, here’s what the 10.13 version looks like:

<key>Supported10.13PluginCompatibilityUUIDs</key>
<array>
	<string>CompatibilityKeyHere</string>
</array>

Once you’ve done this, move the bundle back to the right folder and restart mail. The bundle should hopefully continue working.

Simulating a second finger during drag

You can drag and drop in the iOS simulator by clicking and holding an item. The item “pops” and you can then drag it to a destination. Today, an Apple engineer shared a neat way to free up a “second finger” during this process.

Pause and press the control key. This pins an item mid-drag, enabling you to use the Mac cursor as another touch. You can then retrieve your drag by grabbing the paused item and conclude your drop.

Grepping for parentheses

Just because I had to do this today and thought I’d share. Either use the -E option, for example,

grep -E "let \(" */*.swift

or use egrep directly:

egrep "let \(" */*.swift

Both egrep and grep -E use extended regular expressions, allowing you to use a simple backslash rather than trying to get your shell to cooperate with hyper-escaping. You can use the same approach with sed as well:

echo "(hi)" | sed -E "s/\(/[/"

I hope this helps someone.

How To: Finding your iTunes Connect Vendor ID number

Apple sent me an email:

Apple was recently notified that your bank account information has been manually corrected by the processing bank for iTunes payments to vendor [my personal vendor id number]. Unfortunately Apple cannot continue processing payments with the current banking details provided in iTunes Connect. To avoid unnecessary payment delays or returns, please log into iTunes Connect and provide the correct account information in Agreements, Tax, and Banking. For instructions on how to update banking, see ‘How do I add or edit a new bank account?’ in iTunes Connect Resources and Help. After the banking changes have been entered in our systems, payments will resume.

Oh shiny.

I pull out my latest statement and off I go to update my details. Here’s my problem. I don’t know which vendor account Apple is referring to. Like many developers, I use multiple iTC accounts. When I log into each one, I cannot find the vendor ID. Argh.

To save you lots of time, do this:

  • Log into any iTC account.
  • Manually navigate to https://reportingitc2.apple.com/reports.html
  • The vendor ID is listed next to your name. You should not have to re-authenticate when hopping from iTC to this page.

This helped me figure out which affected account was being referred to in the email and was able to update my info.

To update a direct deposit routing number, you need to hop into Agreements, Tax, and Banking. Click on the Bank Info > View button. You cannot edit the routing number in your existing Current Bank Account. Instead you need to Add Bank Account with the same details and the new routing number and then select that as your new account. Once you do it will take 24-48 hours for the change to go through and you will not be able to make further edits during that time.