Getting disk background encryption status

Context: Making a new bootable backup of my 13″ M1 MBP before upgrading macOS and taking advantage of the fact that SuperDuper! 3.7 now supports bootable clones (yay!). However, one unfortunate fact of creating them is that SuperDuper! docs say you have to boot from that drive and turn on File Vault manually if you want it encrypted (you want it encrypted 😉 ).

I’m pretty sure there’s a way to do this with diskutil and whatnot instead of booting from the clone and turning it on there, but I didn’t feel up to digging through how to do that (it’s complicated by the need to add users manually before turning on encryption, is my vague memory of the steps I had to do last time I dug into this).

However, David – maker of SuperDuper! – was kind enough to mention that once you turn it on you can then boot back from your main drive and connect the backup clone and let it finish the encryption in the background (this is good because it said 10 days to complete – mine is a very slow USB external drive, apparently!).

The drive kept blinking for two days and I was trying to figure out how to tell how much progress it had made and if it was done or if spotlight was indexing the drive or what. Here are some commands that I found to help with that.

diskutil cs list

will produce a listing of Core Storage volumes and their “Encryption Status” and “Conversion Status” which will indicate complete or not.

Unfortunately, the bootable combined volume + “Data” doesn’t come up in that list (it’s something different, apparently).

So for that I was able to find what I wanted using:

diskutil apfs list

which outputs an entry like this for the in-progress file vault encryption volume (emphasis mine):

|   +-> Volume disk7s2 8C8FD74F-ACE0-4E1D-B67B-2F021A3C5DD7
|   |   ---------------------------------------------------
|   |   APFS Volume Disk (Role):   disk7s2 (Data)
|   |   Name:                      Data (Case-insensitive)
|   |   Mount Point:               /Volumes/Data
|   |   Capacity Consumed:         1459954380800 B (1.5 TB)
|   |   Sealed:                    No
|   |   Encryption Progress:    75.0% (Unlocked)

You can specify the container disk (which is disk 7 in this case) and see just the volumes in that container: diskutil apfs list disk7

Advertisement

iOS Feedback Assistant on non-beta iOS – accessing & visibility

The Feedback Assistant iOS app is visible and acts like a normal app on beta builds of iOS, but it’s hidden in release versions of iOS. On release builds the app icon is hidden on the Springboard/Home screen, it doesn’t come up in Spotlight searches, Siri can’t find it, and the process is hidden in the process switcher if you do get it launched.

The magic incantation to make it visible on release builds of iOS is to enter:

applefeedback://inbox?make_visible=1

in the Safari address bar to make the Feedback Assistant app on iOS always visible (spotlight, app icon, Siri search, process switcher, etc).

or, if you want to leave it hidden and auto-hiding but want to launch Feedback Assistant to file a new feedback enter the following in Safari:

applefeedback://new

Use just:

applefeedback://

alone to switch to Feedback Assistant without changing visibility or starting a new feedback (because Feedback Assistant is hidden from the process switcher, you need this if you are in Feedback Assistant and want/need to switch to another app but want to come back to Feedback Assistant to finish the feedback you’re editing, for example).

This is somewhat documented by Apple here: https://developer.apple.com/bug-reporting/

Note 1: You’ll need an Apple ID to file feedback reports.

Note 2: You can always go to feedbackassistant.apple.com to access your feedback assistant account without the Feedback Assistant iOS app, but you can’t include sysdiagnose files and it doesn’t have quite the same ease of use as the app.

How to eat half an ice cream sandwich

I told someone I’d just eaten half an ice cream sandwich and they said they were impressed. Â đŸ€”

It wasn’t trivial, because I do enjoy sweets, but wasn’t that difficult.

Here’s how to successfully eat half an ice cream sandwich (or anything else your body craves), from easy to difficult:

1) [Easy] Take an ice cream sandwich out of the package and cut it in half. Hand half to your wife/kid/friend/neighbor/etc and then pick up your half and enjoy it.

2) [Not difficult] Store your ice cream sandwiches in a freezer in the basement/garage.  Go get one and bring it to the kitchen.  Open one end of the package and slide the ice cream sandwich out of the package and put it on a plate. Cut it in half and put half back into the package.  Take that half an ice cream sandwich in a package back to the freezer in the basement/garage and put it away.  Only then do you pick up the half you’re going to eat and enjoy it.

3) [Medium] Same as above but don’t bother taking the half you’re saving back to the basement until after you eat the half you’re going to eat.

4 [Difficult] Open the package and start eating the ice cream sandwich. When you’ve eaten half of it, stop eating it, close up the package, and put it back into the freezer.

 

So, I wouldn’t be impressed that you ate only half an ice cream sandwich unless you did number 4 on this list 🙂

 

* I did (2) on the list, fwtw 😉

macOS custom window titlebar – don’t forget to implement the standard title right-click feature!

PSA: For a great macOS app experience, the details matter. As an example, if you implement a custom macOS window titlebar for a document based app* don’t forget to implement the feature whereby you display a menu with the folder hierarchy for the document if the user right clicks, control+clicks or command+clicks the document title in the window titlebar.

Just ran across an app that failed to do this and it was quite annoying. I needed to open the folder containing the document in the Finder so I could duplicate the file and selecting the folder name in this menu from the document title is the easiest way to do that. Bug report filed with the app maker, but please save me the time of filing one against your app by getting this right from the beginning 😉

* Note that the Finder implements this for Finder windows, so I’d follow their lead and interpret “document” broadly as ‘anything with a location in the file system’.

Psychology hack: Convert shopping therapy urge into donation urge

Chatting with one of my sisters and she remarked upon our mother’s Amazon book buying 
 addiction? compulsion? Orders way more books than she can or does read. Hear or read a book review that sounds good? Go order the book. Etc.

I opined that this was probably a form of shopping therapy and given our mother’s chronic health issues in recent years, probably understandable. My sister then mentioned that she just goes to the dollar store to satisfy this for very little money 🙂 but also that one of her kids seemed have a bit of a shopping therapy urge also.

I mentioned that I’d been pretty successful some years back when I hacked what felt like my own occasional shopping therapy urge (“Feeling down? Buy something!” – such an American consumer ingrained message for so many years).

What I decided to do was every time I felt that urge and couldn’t just easily talk myself out of it I’d find one of the organizations I like to support, a creative person trying to make their first album/book /comic/etc on Kickstarter, a request for help that a friend or Twitter acquaintance had shared, or similar, and make a donation instead.

I found that I got a better “feel good” feeling this way, someone benefits, and I don’t end up with more STUFF cluttering up my life. Triple Win!

I thought I’d share this hack here in case anyone else might like the idea and want to adopt it.

Automatic Once-a-Day Time Machine Backups

Update: So in macOS 12 (and likely v11 and maybe v10) you have to give cron Full Disk Access for it to work. This seems less than ideal from a security perspective, so I’ve move to using launchd via a LaunchAgent plist file. Documentation on how to use cron is in an addendum in case you prefer that.


My wife is using an older MacBook Air from late 20101 and it can’t handle a Google Meet meeting, Chrome with a bunch of tabs open, and a Time Machine backup (all over WiFi) without the video meeting having trouble. We still wanted to back it up to the two TimeCapsules we have though.

I tried just having her run a backup manually when she was done for the day, but she’s tired then and so backups weren’t happening regularly at all. Thus, it was clear I needed a way to automate this. Turns out it’s not that difficult. This post explains how.

Quick summary

  • Setup Time Machine backup destinations
  • Run a first backup to each backup destination
  • Option 1 (described in an addendum since launchd is preferred by Apple):
    • Use crontab to setup a daily call to tmutil startbackup --auto --rotation
    • Use System Preferences > Security & Privacy > Full Disk Access to give /usr/sbin/cron full disk access
  • Option 2:
    • Create a LaunchAgent plist to invoke tmutil startbackup --auto --rotation at the desired time.
    • use sudo launchctl bootstrap gui/501/ /Users/{your-username}/Library/LaunchAgents/{your-launch-agent-plist-filename} to get the system to load and enable your
  • Use Energy Saver / Battery settings, or pmset repeat wake with appropriate time and days to wake up the computer a couple of minutes before that crontab command trigger time.

Step by Step instructions

  1. First step – create the Time Machine backup destinations in the Time Machine settings inside System Preferences.
  1. Run the first backup to each destination – either manually (via right-click on the destination drive icon in System Preferences > Time Machine view), or automatically by enabling “Back Up Automatically” in Time Machine settings and then waiting until they’ve both completed their first backup (don’t forget to turn off automatic once the first backup to each has run!).
  1. Decide what time you want your daily backup to run. I chose 12:59AM.
  1. Now create a LaunchAgent file for your task. The easiest way is to use LingonX, but you can create the file in a text editor like the excellent BBEdit and may want to refer to this very helpful documentation for launchd.

Name your file something that makes sense to you, I matched the file name to the label, so: “com.geekanddad.DailyTimeMachineBackup.plist” is what I used.

Contents should be something like this:

<?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>Label</key>
  <string>com.geekanddad.DailyTimeMachineBackup</string>
  <key>ProgramArguments</key>
  <array>
    <string>/usr/bin/tmutil</string>
    <string>startbackup</string>
    <string>--auto</string>
    <string>--rotation</string>
  </array>
  <key>StartCalendarInterval</key>
  <array>
    <dict>
      <key>Hour</key>
      <integer>00</integer>
      <key>Minute</key>
      <integer>59</integer>
    </dict>
  </array>
</dict>
</plist>
  1. If doing this manually with a text editor, you’ll need to save this file into ~/Library/LaunchAgents/ and then you’ll need to tell launchd about it via something like:

    launchctl bootstrap gui/501/ /Users/dad/Library/LaunchAgents/com.geekanddad.DailyTimeMachineBackup.plist

    Note 1: If you don’t know what your uid (User ID) is (it’s the number after “gui” in the service domain target argument above), you can use launchctl manageruid command to find out.
    Note 2: If this doesn’t work, try running it via sudo and you’ll get more verbose error messages.
  1. Now all you need to do is make sure your Mac is awake at 12:59am 🙂

    A note about the time: Since waking up triggers connecting to the network which triggers auto-fetching of mail, likely calendar syncing, and who knows what other Apple stuff and this was an old and slow Mac, I chose to wake it up at 12:57am so it would have 2 minutes to complete all of the network stuff before the Time Machine backup tried to start. This is because if it’s too slow sometimes Time Machine on this old version of macOS will decide that “the backup destination isn’t available” and it will fail.

    I think you can do this via the Energy Saver (“Battery” in macOS 12.x) System Preferences pane under “Schedule” (for macOS 12.x) by selecting the “Startup or wake” checkbox and picking “Every Day” and the time you chose above.

    I didn’t go this route because I didn’t want it to wake up a shutdown Mac which might not be plugged in and then my wife would wake up to a dead laptop (not good for my reputation! :)).

    However, if that would work for you, I recommend trying that out because it’s easier.

    What I did:

    sudo pmset repeat wake MTWRFSU 00:57:00

Which sets a repeating wake event for 12:57am every day of the week. You’ll note that I didn’t use wakeorpoweron here because I only want it to wake up a sleeping computer and do not want it to turn on a shutdown computer.

That’s it!

All she has to do now is leave her laptop open at night when she wants it backed up (it won’t wake up if the lid is shut and no external monitor is attached).


Addendum – using (crontab):
  1. Using Terminal, initiate editing of the crontab for the current user (which is what I did), or potentially a system crontab (I didn’t look into this):

crontab -e

This puts you into your terminal editor with the current user’s crontab file open. Once there, add the following two lines

# For daily backups
59 0 * * * /usr/bin/tmutil startbackup --auto --rotation

This crontab entry says to run the tmutil command at 59 minutes after midnight every day. The parameters to tmutil are telling it to start a backup, run it as if it was a system initiated backup, and to automatically rotate between multiple backup destinations (you only need the last parameter if you have multiple defined).

  1. Give cron permissions necessary to run
    1. Open “System Preferences
” from the Apple menu and select the “Security & Privacy” icon.
    2. Then select the “Privacy” tab and scroll through the list on the left and select “Full Disk Access.”
    3. Select the lock icon in the lower left and enter an administrator’s credentials.
    4. select the + icon below the list of applications
    5. type Cmd+shift+g to display the path entry file selection interface and type: /usr/sbin/cron and select the cron binary

The window should look like this when you’ve got it added properly:

Security & Privacy panel inside System Preferences with the Privacy tab selected and Full Disk Access selected from the list on the left.  Shows the cron command line tool in the list on the right with the enabled checkbox to the left of the tool name checked.


Footnotes

1) She’s since upgraded to a late 2020 MacBook Air and is loving it, as you’d imagine :). Retina screen, insanely faster, insanely long battery life, etc. Only thing she’s annoyed by is all the dongles required to connect to her external keyboard, mouse, and external display 🙂 (back)

Safari – Send Window To Back Command

I often end up with a Safari window in front that I don’t want to close or minimize, but that I do want to send behind all other windows. Alas, Safari doesn’t have this command in the Window menu. I’ve requested that it be added (FB7642150) and you can also 😉

In the meantime, I don’t much like AppleScript so I don’t know it well, but this need finally hit the level where I was willing to suffer through the pain of AppleScript and hack something together (there’s probably a better way to write this). I put this into a simple Alfred workflow and it works great (yay!):

on alfred_script(q)
  tell application "Safari"
    local lastVisibleWindow
    repeat with n from 1 to count of windows
      if window n is visible then
        set lastVisibleWindow to window n
      else
        exit repeat
       end if
     end repeat
    set index of front window to index of lastVisibleWindow
  end tell
end alfred_script

You can also use this script from a scripts menu, or other utility able to invoke applescripts, by removing the first and last lines that make it a handler for Alfred.

Tested and works under Safari 13.0.5 on macOS 10.15.3

// Begin UPDATE: 2023.05.13 — for some reason this now (macOS 12.6.x, Safari 16.4.1) sends the frontmost window to be the back window minus one (sigh).

Here’s an updated (hack) script that sends it all the way to the back:

on alfred_script(q)
  tell application "Safari"
    local lastVisibleWindow
    repeat with n from 1 to count of windows
      if window n is visible then
        set lastVisibleWindow to window n
      else
        exit repeat
      end if
    end repeat
    set frontWindow to the front window
    set index of front window to index of lastVisibleWindow
    -- The above line sets it to lastVisibleWindow -1
    -- The following doesn't work:
    --   set the index of frontWindow to (index of frontWindow) + 1
    -- so hack to move entirely to the back of the visible windows:
    set the index of lastVisibleWindow to the index of frontWindow
  end tell
end alfred_script

// End Update

This script handles the case where you have minimized windows. The “set index of the frontmost window to (count of windows)” type script you’ll find elsewhere online doesn’t work if you have minimized windows.

cheers.

Improving on the common AnyCancellable .store(
) pattern.

If you’ve been reading any Combine documentation or code, you’ve probably seen code like this before:

let newPhotos = photos.selectedPhotos
newPhotos
  .map { [unowned self] newImage in
    return self.images.value + [newImage]
  }
  .assign(to: \.value, on: images)
  .store(in: &subscriptions)

Pretty standard: call something that returns a publisher, map the results to whatever type you need, assign that value to an instance variable, and finally, store the resulting AnyCancellable into a Set named subscriptions which is an instance variable or property in the enclosing scope. In this case we’re in a UIViewController subclass where it’s defined as:

private var subscriptions = Set<AnyCancellable>()

This code is from the popular book on Combine.

The problem with this pattern in this particular case, and other similar situations, is that the AnyCancellable is never removed from the subscriptions Set. Sometimes this doesn’t matter – when the enclosing scope is going to go away and the code is only called once relatively soon before exiting scope cleans up memory. But sometimes, as in the sample project this code is from, the user can do the action invoking this code (adding photos in this case) repeatedly. Each time this code is invoked the .store(...) call adds another AnyCancellable to the Set. Since this view controller is the root view controller of the app it is never closed and this memory is never cleaned up during the lifetime of the app.


An easy solution is to add an instance variable to your class (a UIViewController in this case):

private var newPhotosSubscription: AnyCancellable?

and then change the code above to something like this so the AnyCancellable is assigned to the instance variable and the .store(..) part is removed:

self.newPhotosSubscription = newPhotos
  .map { [unowned self] newImage in
    self.images.value + [newImage]
  }
  .assign(to: \.value, on: images)

When this assignment happens it frees the previous value, if any, and thus there will be at most a single AnyCancellable kept around.

Ok, that works. But I’m a perfectionist and that single AnyCancellable hanging around bugged me. That’s fixable, but as I went and fixed this issue in more places in the project there was a proliferation of instance variables as I had to add one for each place this came up.

So here’s a solution I came up with to avoid that.

First, add an extension on AnyCancellable:

extension AnyCancellable {
  func store(in dictionary: inout [UInt64: AnyCancellable], 
             for key: UInt64) {
    dictionary[key] = self
  }
}

Then change the subscriptions instance variable to be a matching Dictionary (and remove the individual instance variables you added for each case previously, if you did that already):

private var subscriptions = Dictionary<UInt64, AnyCancellable>()

In our original function which created the newPhotosSubscription and let the user choose photos, change the code to:

let key = DispatchTime.now().uptimeNanoseconds
 
newPhotos
  .handleEvents(receiveCompletion: { [unowned self] _ in
    self.subscriptions.removeValue(forKey: key)
  })
  .map { [unowned self] newImage in
    self.images.value + [newImage]
  }
  .assign(to: \.value, on: images)
  .store(in: &subscriptions, for: key)

So above we created a Dictionary instead of a Set for our subscriptions instance variable that holds on to the AnyCancellable so it remains allocated while needed. The dictionary allows us to store the AnyCancellable under a unique key, which in this case is a UInt64. On the first line above we create that key and assign it the number of nanoseconds since the device rebooted.

Then we add a .handleEvents operator to the subscription pipeline. Once the publisher has sent the .finished or .failure completion event we no longer need to keep the AnyCancellable around. Our receiveCompletion closure code removes the AnyCancellable stored under the captured key from our subscriptions instance variable.

(Note: if you preferred, you could replace UInt64 with UUID in the dictionary declaration and the AnyCancellable extension. Then instead of setting the key value to DispatchTime.now().uptimeNanoseconds you could generate a new UUID with UUID(). All that matters is that the is a unique value conforming to Hashable and doesn’t cost too much to generate).


I’m relatively new to Combine, so if you know a better way to do this, I’d love to hear about it!


Addendum

1) Be aware that Swift closures capture variables by reference by default (unlike for Objective C). So don’t re-use the key variable like I did in my project 

2) I also ran into a case where the pattern above did odd things for a Combine pipeline subscribed to a Future publisher – the store seems to happen after the pipeline has finished executing (huh?!) and thus the clean up in the completion handler is called before the store. I haven’t dug into that to understand why, but thought I’d mention it in case you are seeing things not get cleaned up when you expect like I was.

-> I later learned that this is because in Combine Future is “eager” and will complete immediately. One option is to wrap it in a Deferred publisher and another is to capture the AnyCancellable in a local variable and only save it to the dictionary if the completion block hasn’t been called yet.

“Help getting into the tech scene in Portland”

I get this question a lot, “I’m having trouble getting into the tech industry in Portland, any suggestions?” Usually from recent college graduates or people who are coming into tech from other than a CS degree path.  I just wrote up this response to one and decided I was tired of typing similar stuff in over and over so extractedit and am putting it here:

—-

Jobs resources:

There are more for sure.

While you search (since one can only spend so many hours a day searching), build something that demonstrates your skills and helps you learn more skills. Picking something you see lots of job ads for (if it’s interesting to you) is a decent way to start. Lot of demand for machine learning these days. Taking online courses can help accelerate this learning but won’t get you a job; building something that works and that demonstrates what you can do is much more likely to.

If you find a place you want to work, get to know people there and let them get to know you.(1)  If they like you then they’ll help you know about positions that open up. Learn from them what they need and go teach yourself those things. Ask smart questions (find answers to easy ones online and in documentation).

Finally, big companies like eBay, Apple, Google, Amazon etc open up there internships for current students and recent graduates in (Oct?)/Nov/Dec so inquiring about them can be a good route into larger companies.


1) don’t be creepy! See if you have any friends in common.  See if there’s a meetup or other public social gathering they go to where you can meet them.  Follow on twitter to get to know them over time.  Look for their office having an open office gathering, etc.

Tax Time: Retirement plan for Indies

Tax Time here in the U S of A.   This post is for self-employeed and small business owners in the USA but does not constitute tax advice – you should consult a qualified licensed/certified tax professional – but is just some information to help you ask them some leading questions and pointers to documentation on the IRS.gov website for educational purposes.

π

As I’m calculating the basis in my SEP-IRA due to some conversions of IRA to ROTH IRA, I’m reminded that I lost out on about 10 years of pre-tax retirement savings due to a bad CPA who said I wasn’t eligible to do so since it was an S-Corp.  Got a new CPA and found out the first one was wrong (or the law changed and he was wrong at first but didn’t inform me that it had changed).

I suggest you educate yourself by starting at IRS.gov SEP FAQs and learn enough to ask the questions I should have :-/

The great thing about a SEP-IRA, for an S-Corp anyway (not sure about LLCs or sole-proprietors), is that the business makes the contributions for the employee and these contributions are deductible business expenses.

The limits are also a LOT higher than you as an individual have.  From an FAQ sub-page linked to from the one above:

How much can I contribute to my SEP?

The contributions you make to each employee’s SEP-IRA each year cannot exceed the lesser of:

  1. 25% of compensation, or
  2. $53,000 (for 2015 and 2016 and subject to annual cost-of-living adjustments for later years).

These limits apply to contributions you make for your employees to all defined contribution plans, which includes SEPs. Compensation up to $265,000 in 2015 and 2016 and subject to cost-of-living adjustments for later years) of an employee’s compensation may be considered. If you’re self-employed, use a special calculation to determine contributions for yourself.

So if you’re making more than $72,000 a year you can start to exceed the $18,000 personal contribution limit to a SEP-IRA or 401K.

This makes it sound like you don’t have to be a Corporation (but read Publication 560 & ask your CPA):

How much can I contribute if I’m self-employed?

The same limits on contributions made to employees’ SEP-IRAs also apply to contributions if you are self-employed. However, special rules apply when figuring the maximum deductible contribution. See Publication 560 for details on determining the contribution amount.

The only real down-side that I’m aware of (again, consult your hopefully better-than-my-old-CPA CPA) is that you make this contribution for all employees who meet the criterion you set in the SEP Plan Document.  The IRS has a “model SEP plan document”, form 5305-SEP that I used and that might be enough for you.  See the details/restrictions here.

My company mostly used contractors and I set the requirement to full-time employment for 3 years which was likely to exclude all but the most dedicated and valuable employees (and which I already qualified for :)).

On the other hand, having a good retirement plan that is competitive with with a big company’s 401K plan might help you attract employees that you otherwise couldn’t, so you might want eligibility that let’s employees participate as soon as they’ve passed some introductory trial period (90 or 180 days or whatever).  Notice that the limits for the SEP-IRA are actually higher than those for a 401K plan once compensation crosses the $72,000/year threshold.  So you can actually provide a more aggressive retirement plan with a SEP-IRA plan than with that big company 401K plan (unless I’m missing something).

And the employee can also make a ROTH IRA contribution as well (subject to the usual ROTH IRA contribution limits).

A SEP-IRA is cheaper to operate than a 401K plan (which is complicated enough that you have to pay a service to operate it for you – and they aren’t cheap).  Your employees can open their own SEP-IRA account and your business simply deposits money into it.  (See the IRS site for details).

Over the 10 years I was operating with the bad CPA I could have saved a lot more for retirement than the ~$4000-$5000 ROTH IRA limit, if only I’d known….   Now you know.

Go forth and save those pre-tax dollars for retirement!

Your 50-year-old self will thank you.  😀

 

This is not tax advice. All readers should consult a qualified tax professional about any tax decisions they make for themselves or their business.