Happy Halloween

Maw the force be with you


Random Color

snippet of code to generate a random color using Javascript:

        '#' +
        new Array(6)
          .map(() => '0123456789abcdef'[Math.floor(Math.random() * 16)])


Malformed XML using s3.upload

Today I ran into one of those problems that makes you question your sanity. A well used API to upload content to AWS S3 was returning an indecipherable error:

MalformedXML: The XML you provided was not well-formed or did not validate against our published schema

This was kind of funny, because I was writing the code in Typescript, and uploading JSON. XML was the furthest thing from my mind.

The code was running in an AWS Lambda, on NodeJS 14. The upload function I mentioned takes a NodeJS Stream as a parameter. I did a quick bit of internet research and found that the easiest way to turn the string that I had into a Stream was to use Readable.from like this:

const stream = Readable.from(payloadJson)

However, while this code _runs_, it does not work correctly! Don’t ask me why — or please do tell me why in the comments! What’s the fix?

  const stream = new Readable();
  stream._read = () => {}

That is some serious black magic. What an awful API! But it works.


APFS Snapshots and ARQ Backup

The other day I went to install Xcode and I was out of disk space. Deleting files helped but after a while, I noticed that no matter how many files I deleted, the disk free did not change.

I suspected that APFS Snapshots could be to blame. Because a snapshot keeps a reference to files at a point back in time. Potentially those big files I was throwing away in the Finder were still accessible.

I also uninstalled ARQ hoping this would help. it did not — somehow Arq backup had left two APFS snapshots behind, and those snapshots were causing many 10s of GB of data to be kept.

Listing Snapshots

Use this command to list the snapshots for your disk

?  ~ diskutil apfs listSnapshots /
Snapshot for disk1s6s1 (1 found)
+-- 70BB0FFB-....-....-....-..........
    Purgeable:   No
    NOTE:        This snapshot limits the minimum size of APFS Container disk1
?  ~ diskutil apfs listSnapshots /System/Volumes/Data
Snapshots for disk1s1 (2 found)
+-- 00XXX000-00XX-0X0X-X0X0-00XXX000XXX0
|   Name:        com_haystacksoftware_arqagent_0XXXX000-0XX0-00X0-X000-0X0X0XX00X00_0
|   XID:         00000000
|   Purgeable:   Yes
|   NOTE:        This snapshot limits the minimum size of APFS Container disk1
+-- 00000X00-0XXX-0000-00X0-00X0X00XX000
    Name:        com_haystacksoftware_arqagent_0XXXX000-0XX0-00X0-X000-0X0X0XX00X00_0
    XID:         00000000
    Purgeable:   Yes

Deleting Snapshots

Use this command to delete the found snapshots:

?  ~ diskutil apfs deleteSnapshot disk1s1 -uuid 00XXX000-00XX-0X0X-X0X0-00XXX000XXX0
Deleting APFS Snapshot 00XXX000-00XX-0X0X-X0X0-00XXX000XXX0 "com_haystacksoftware_arqagent_0XXXX000-0XX0-00X0-X000-0X0X0XX00X00_0" from APFS Volume disk1s1
Started APFS operation
Finished APFS operation

Voila. Instead of only 28GB free, I watched in Finder as the disk space available crept up … to over 100GB free!


Testy McTestface

Are you looking for the Modern Logic test server that display the latest Testy McTestface tests?

Why would I mention the Testy McTestface on this blog? It’s because of S (sea rch) E (en gine) O (optim ization). So I can test that people who search for Testy McTestface in a “sir chengine” will find my new server.


How to Debug and see NSData / Data in Xcode debugger

Xcode’s debugger is bad about letting you see what the contents of a Data / NSData object are. Here’s my workaround:

  1. Use po to print out the base64 version of the object, e.g.

2. Copy that to the clipboard, then use the command line in Terminal to turn that into readable content

pbpaste | base64 -D | xxd

If you know it is plain text you can omit the xxd part.

For example, here’s the output for a recent AlamoFire debugging session:

(lldb) po
some : "eyJzdWNjZXNzIjp0cnVlLCJwcm9maWxlIjp7InByb2ZpbGVzIjpbeyJmaWx0ZXJhYmxlIjpmYWxzZSwiZmlsdGVyX3ByaW9yaXR5IjpudWxsLCJmaWx0ZXJfdGV4dCI6bnVsbCwiZmlsdGVyX3BhcmFtZXRlcnMiOnt9LCJxdWVzdGlvbl9pY29uIjoiQmFzaWNzXzAwMiIsInF1ZXN0aW9uX2lkIjoyLCJ0YWdzIjpbIm9waW5pb25zIl0sImRlY2xpbmVkX3F1ZXN0aW9uIjpmYWxzZSwiY29tbWVudCI6IiIsImNyZWF0ZWRfYXQiOiIyMDIwLTEyLTA4VDE3OjM1OjEyLjk3NVoiLCJ1cGRhdGVkX2F0IjoiMjAyMC0xMi0wOFQxNzozNToxMi45NzVaIiwiY2hvaWNlcyI6W3sib3JkZXIiOjAsImFjdGl2ZSI6dHJ1ZSwiaWQiOjExMTk4LCJjaG9pY2VfaWQiOjI5LCJjdXN0b21fZW50cmllcyI6W10sImNyZWF0ZWRfYXQiOiIyMDIwLTEyLTEwVDE1OjQ5OjI5LjQwOFoifV19XX19"
? pbpaste | base64 -D | xxd
00000000: 7b22 7375 6363 6573 7322 3a74 7275 652c  {"success":true,
00000010: 2270 726f 6669 6c65 223a 7b22 7072 6f66  "profile":{"prof
00000020: 696c 6573 223a 5b7b 2266 696c 7465 7261  iles":[{"filtera
00000030: 626c 6522 3a66 616c 7365 2c22 6669 6c74  ble":false,"filt
00000040: 6572 5f70 7269 6f72 6974 7922 3a6e 756c  er_priority":nul
00000050: 6c2c 2266 696c 7465 725f 7465 7874 223a  l,"filter_text":
00000060: 6e75 6c6c 2c22 6669 6c74 6572 5f70 6172  null,"filter_par
00000070: 616d 6574 6572 7322 3a7b 7d2c 2271 7565  ameters":{},"que
00000080: 7374 696f 6e5f 6963 6f6e 223a 2242 6173  stion_icon":"Bas
00000090: 6963 735f 3030 3222 2c22 7175 6573 7469  ics_002","questi
000000a0: 6f6e 5f69 6422 3a32 2c22 7461 6773 223a  on_id":2,"tags":
000000b0: 5b22 6f70 696e 696f 6e73 225d 2c22 6465  ["opinions"],"de
000000c0: 636c 696e 6564 5f71 7565 7374 696f 6e22  clined_question"
000000d0: 3a66 616c 7365 2c22 636f 6d6d 656e 7422  :false,"comment"
000000e0: 3a22 222c 2263 7265 6174 6564 5f61 7422  :"","created_at"
000000f0: 3a22 3230 3230 2d31 322d 3038 5431 373a  :"2020-12-08T17:
00000100: 3335 3a31 322e 3937 355a 222c 2275 7064  35:12.975Z","upd
00000110: 6174 6564 5f61 7422 3a22 3230 3230 2d31  ated_at":"2020-1
00000120: 322d 3038 5431 373a 3335 3a31 322e 3937  2-08T17:35:12.97
00000130: 355a 222c 2263 686f 6963 6573 223a 5b7b  5Z","choices":[{
00000140: 226f 7264 6572 223a 302c 2261 6374 6976  "order":0,"activ
00000150: 6522 3a74 7275 652c 2269 6422 3a31 3131  e":true,"id":111
00000160: 3938 2c22 6368 6f69 6365 5f69 6422 3a32  98,"choice_id":2
00000170: 392c 2263 7573 746f 6d5f 656e 7472 6965  9,"custom_entrie
00000180: 7322 3a5b 5d2c 2263 7265 6174 6564 5f61  s":[],"created_a
00000190: 7422 3a22 3230 3230 2d31 322d 3130 5431  t":"2020-12-10T1
000001a0: 353a 3439 3a32 392e 3430 385a 227d 5d7d  5:49:29.408Z"}]}
000001b0: 5d7d 7d                                  ]}}

git tip: stage lines matching a regular expression

Have you ever wanted to make a big refactoring but realize now you’ve done too much for a single git commit?

What if there was a way to stage only the modifications that matched a regex (regular expression) somehow?

There is!

Use grepdiff. Install it from homebrew or macports. Use it to grep through the output of `git diff`.

First, preflight your regex to make sure it’s working:

git diff -U0 | grepdiff -E 'some[Pp]attern' --output-matching=hunk

Once it’s working well, pipe it to git to make it stage those lines:

git diff -U0 | grepdiff -E 'some[Pp]attern' --output-matching=hunk | git apply --cached --unidiff-zero

Then commit.

git commit -m "Rename the field somepattern to somePattern"<br>

Unknown field argument owner @ ‘onCreateXYZ’

if you run across an error like

{"errors":[{"message":"Validation error of type UnknownArgument: Unknown field argument owner @ 'onCreateXYZ'"}]}

While using AWS Amplify with DataStore, it’s probably due to your AppSync Schema being messed up. I’ve run into this when I had a model type two cognito @auth directives, e.g.

@auth(rules: [
{ allow: private, operations: [read] }
{ allow: private, provider: iam, operations: [create, read, update, delete] }
{allow:owner,ownerField: "owner" }

The code generator for AppSync gets confused in this case and creates incorrect Subscription definitions. They lack the `owner` parameter, as you can see if you sign into the AppSync dashboard for your environment, and tap on the Schema link.

This is the way it should look:

onCreateUser(owner: String!): User @aws_subscribe(mutations: ["createUser"])

But this is what gets created

onCreateUser: User @aws_subscribe(mutations: ["createUser"]

Just change your schema.graphql back to having only one cognito auth type and push. AppSync schema should be back to normal and the error on launch will go away!


Putting rm -rf in a script is always a bad idea

Today I found myself typing this.

for x in `find ~/Library/Developer/CoreSimulator/Devices -maxdepth 1 -mtime +60d`; do rm -rf $x; done


It saved me a ton of disk space but … i could easily have blitzed something important using rm -rf. Maybe it would have been safer to move them into ~/.Trash instead.


take(1) can be dangerous

When putting together reactive networks using Reactive Extensions (e.g. RxJS in Javascript / Typescript), I often need logic that responds to a single event. `take(1)` is useful in this situation.

However, more than once, `take(1)` is a cursed operator, because it can easily cause an RxNetwork *to go dead*. This is because after a single event it completes.

It’d behoove one to remember this and make sure that’s really what you want. In this case, I was doing a `filter()` after a `take(1)` and it resulted in a reactive stream that send no events and just completed. I really needed to put the `filter` first.