macOS Swift: tab to an NSPopUpButton

So you have a form in your app, and you want to tab from an NSTextField to an NSPopUpButton. You know, like any normal HTML form would using the tabindex attribute.

Well, you could use the dandy nextKeyView property, which lets you define which input should get focus next after tabbing. You could do it like this:

class RightViewController: NSViewController {

  @IBOutlet var rageTextField: NSTextField!
  @IBOutlet var furyPopup: MyNSPopUpButton!

  override func viewDidLoad() {
    super.viewDidLoad()
    self.rageTextField.nextKeyView = furyPopup
  }
}

It won’t work. It’ll work fine between NSTextFields and other stuff, but not your NSPopUpButton. Why? Well, [INSERT INCOHERENT SEETHE-RANTING HERE]. That’s why.

Look, the short explanation is that with NSPopUpButtons, canBecomeKeyView is set to false. Fix it by overriding using a subclass.

class MyNSPopUpButton: NSPopUpButton {
  override var canBecomeKeyView: Bool {return true} // @&$('#$')!!
}

class ViewController: NSViewController {

  @IBOutlet var rageTextField: NSTextField!
  @IBOutlet var furyPopup: MyNSPopUpButton! // <-- LOOK AT THE "MY"

  override func viewDidLoad() {
    super.viewDidLoad()

    print("(furyPopup.canBecomeKeyView)") // should be true

    self.rageTextField.nextKeyView = furyPopup // should work now
  }
}

Wait, one more thing. Don’t forget to change the popup’s class in IB, too:

Cannot select or enter country while setting up a new Synology RT2600ac router

The Synology RT2600ac router is, in my opinion, very good. That said, there’s a chance you may encounter a show-stopping issue when setting up a new one. On the 2nd setup screen, you might see a field for Country. It looks like a drop down, but it has no options. Manually typing a country name doesn’t work, either.

Screenshot of useless Country input

Worry not. You don’t need to return it. We can fix this by doing a factory reset.

Yes, a factory reset on a device you presumably… just got from the factory. I know.

Grab a paper clip (or something similarly small and poke-ey) and do this:

  1. Make sure the router is on.
  2. Find the RESET button on the back. Poke it with your clip or other poker, and hold for at least 10 seconds. You’ll see the LEDs on the front flash as if the router were rebooting. That’s because it’s rebooting.
  3. Don’t panic while it takes several minutes to come back up.
  4. Try the setup again.

When you get to the 2nd screen, the Country field should be completely gone, and you’ll be able to proceed.
Huzzah.

Change default VNC/Remote-Management port in macOS

macOS uses VNC for remote management, and has a built-in server and viewer. (Turn on the server under System Preferences > Sharing and mash the checkbox for Screen Sharing.)

But if you’re here, you probably knew that already, and you want to change the default VNC port from 5900 to something else.

Get out your terminal:

sudo nano /System/Library/LaunchDaemons/com.apple.screensharing.plist

Or use vim. Whatever. It’s your party. Your terminal party.

You’re looking for this line (line 34 in El Capitan, and since Lion):

<string>vnc-server</string>

You want to change the text “vnc-server” to whatever you want your port to be. For example, this would make it port 1337:

<string>1337</string>

Don’t use port 1337.

Then turn Screen Sharing off/on again in preferences, or restart your machine. It should use the new port now.

Connecting to it from a Mac

Use the built-in VNC viewer by going to Finder > Go > Connect to Server… Under “Server Address” enter:

vnc://your.ip.or.hostname.here.com::1337

SparkPost: Dynamic subject lines in email templates

Moving to Mandrill to SparkPost this week? (A lot of us are thanks to this.)

In Mandrill (and possibly other transactional email services) we can use the API to send emails using templates. We send a bunch of information including recipients, content, metadata, and subject, ad we let ‘er fly. Boom, done.

For the most part, I’ve found SparkPost to be pretty similar, except on one point: subject lines. Specifically for emails sent using templates. When you’re using a template, several options in your API call get ignored, including “subject”. It defaults to whatever you set when you created the template in the SparkPost UI. (It’s a required field.)

Worse, at the time of this writing anyway, there’s no clear way to fix it. It isn’t specifically covered in the documentation, and the support center answers (such as this one) are… not ideally helpful, we’ll say that.

But there’s good news. After losing a fair number of hours and hitpoints on this, I found out that…

It’s actually pretty simple

With SparkPost templates, we fill our template placeholders (name, content, etc) with curly-bracketed variables such as:

{{name}}

(Details on this are in their docs over here.) That’s what we put in the template to specify that this is the placeholder for the ‘name’ field in the substitutionData JSON array we’re sending them. Here’s a simple example of that part of our API call: (Also called a “transmission” in SparkPost world.)

{
   "substitutionData": {
      "name": "Tester Gunderson",
      "astrological_sign": "Quagmire",
      "favorite_color": "blue",
      "main_email_body": "This message is to transactionally confirm that your sign is 'Quagmire' (we didn't know that was a thing) and your favorite color is 'blue'."	
   }
}

Throw a key in there for Subject

With the rest of your substitutionData, add your desired subject line:

"subject": "We're sending this email to blue-loving Quagmires"

And then in the SparkPost UI, edit your template and change the subject line to {{subject}}. (Or whatever you want to name the variable.)

Setting dynamic subject lines in SparkPost templates
Yep. You can put variables in there.

 

And you’re done.

Well, maybe. It depends on your use case. If you want to add dynamic variables to the subject line (like the recipient’s first name) you have another battle ahead of you. Hopefully, as more of us former Mandrill users migrate over certain questions can be answered more clearly than they are as of the time I’m writing this. (April 19 2016)

Good luck out there!

PHP: Convert US State names to their abbreviations and vice-versa

Sometimes you need to convert state names to their abbreviations. Sometimes you need to convert state abbreviations to their names.

Well, this function does both.

Give it a valid state abbreviation, and it’ll give you the name. Give it a name, and it’ll give you the abbreviation. (e.g. OR -> Oregon, Oregon -> OR). Enjoy! Hope it helps.

/* -----------------------------------
 * CONVERT STATE NAMES!
 * Goes both ways. e.g.
 * $name = 'Orgegon' -> returns "OR"
 * $name = 'OR' -> returns "Oregon"
 * ----------------------------------- */
function convertState($name) {
   $states = array(
      array('name'=>'Alabama', 'abbr'=>'AL'),
      array('name'=>'Alaska', 'abbr'=>'AK'),
      array('name'=>'Arizona', 'abbr'=>'AZ'),
      array('name'=>'Arkansas', 'abbr'=>'AR'),
      array('name'=>'California', 'abbr'=>'CA'),
      array('name'=>'Colorado', 'abbr'=>'CO'),
      array('name'=>'Connecticut', 'abbr'=>'CT'),
      array('name'=>'Delaware', 'abbr'=>'DE'),
      array('name'=>'Florida', 'abbr'=>'FL'),
      array('name'=>'Georgia', 'abbr'=>'GA'),
      array('name'=>'Hawaii', 'abbr'=>'HI'),
      array('name'=>'Idaho', 'abbr'=>'ID'),
      array('name'=>'Illinois', 'abbr'=>'IL'),
      array('name'=>'Indiana', 'abbr'=>'IN'),
      array('name'=>'Iowa', 'abbr'=>'IA'),
      array('name'=>'Kansas', 'abbr'=>'KS'),
      array('name'=>'Kentucky', 'abbr'=>'KY'),
      array('name'=>'Louisiana', 'abbr'=>'LA'),
      array('name'=>'Maine', 'abbr'=>'ME'),
      array('name'=>'Maryland', 'abbr'=>'MD'),
      array('name'=>'Massachusetts', 'abbr'=>'MA'),
      array('name'=>'Michigan', 'abbr'=>'MI'),
      array('name'=>'Minnesota', 'abbr'=>'MN'),
      array('name'=>'Mississippi', 'abbr'=>'MS'),
      array('name'=>'Missouri', 'abbr'=>'MO'),
      array('name'=>'Montana', 'abbr'=>'MT'),
      array('name'=>'Nebraska', 'abbr'=>'NE'),
      array('name'=>'Nevada', 'abbr'=>'NV'),
      array('name'=>'New Hampshire', 'abbr'=>'NH'),
      array('name'=>'New Jersey', 'abbr'=>'NJ'),
      array('name'=>'New Mexico', 'abbr'=>'NM'),
      array('name'=>'New York', 'abbr'=>'NY'),
      array('name'=>'North Carolina', 'abbr'=>'NC'),
      array('name'=>'North Dakota', 'abbr'=>'ND'),
      array('name'=>'Ohio', 'abbr'=>'OH'),
      array('name'=>'Oklahoma', 'abbr'=>'OK'),
      array('name'=>'Oregon', 'abbr'=>'OR'),
      array('name'=>'Pennsylvania', 'abbr'=>'PA'),
      array('name'=>'Rhode Island', 'abbr'=>'RI'),
      array('name'=>'South Carolina', 'abbr'=>'SC'),
      array('name'=>'South Dakota', 'abbr'=>'SD'),
      array('name'=>'Tennessee', 'abbr'=>'TN'),
      array('name'=>'Texas', 'abbr'=>'TX'),
      array('name'=>'Utah', 'abbr'=>'UT'),
      array('name'=>'Vermont', 'abbr'=>'VT'),
      array('name'=>'Virginia', 'abbr'=>'VA'),
      array('name'=>'Washington', 'abbr'=>'WA'),
      array('name'=>'West Virginia', 'abbr'=>'WV'),
      array('name'=>'Wisconsin', 'abbr'=>'WI'),
      array('name'=>'Wyoming', 'abbr'=>'WY'),
      array('name'=>'Virgin Islands', 'abbr'=>'V.I.'),
      array('name'=>'Guam', 'abbr'=>'GU'),
      array('name'=>'Puerto Rico', 'abbr'=>'PR')
   );

   $return = false;   
   $strlen = strlen($name);

   foreach ($states as $state) :
      if ($strlen < 2) {
         return false;
      } else if ($strlen == 2) {
         if (strtolower($state['abbr']) == strtolower($name)) {
            $return = $state['name'];
            break;
         }   
      } else {
         if (strtolower($state['name']) == strtolower($name)) {
            $return = strtoupper($state['abbr']);
            break;
         }         
      }
   endforeach;
   
   return $return;
} // end function convertState()

Is your Bootstrap X-Editable plugin stuck in 2015?

At least as of version 1.5.1, the awesome Vitalets Bootstrap X-Editable plugin has a maximum date for its datepickers of 2015. Why? I dunno. I just work here. But it’s a simple fix.

After you get things rolling by applying the plugin to whatever element, like so…

$('#thing').editable();

Change the default maximum year thusly:

$.fn.combodate.defaults.maxYear = 2531;

Or whatever year you want.

While you’re there, you could also change the minimum date to something a little more recent than 1970:

$.fn.combodate.defaults.minYear = 2010;

Disable iMessage Sign-in Sound

If you’re like me, you don’t love it when your Mac makes that sign-in sound every time you open the lid. (Assuming you use iMessage/Messages for any IM services.) Here’s how to disable it. (And only it.)

  1. Make sure Messages is closed.
  2. Go to your Applications folder.
  3. Find Messages.app and right-click it. Choose Show Package Contents.
  4. Find the Resources folder.
  5. Look for a file called Logged In.aiff.
  6. Delete that mess.

Next time you open it, that sound will be gone. (Note: This may not work on El Capitan.)

 

Prevent Creative Cloud from loading at startup [Mac]

If you’re like me (and I know I am) you don’t love having a bunch of unnecessary stuff loaded when you don’t need it. Like Adobe’s Creative Cloud thingy. It can drain your battery (not by a lot, but still) and clutter up your menu bar.

Adobe Creative Cloud menu bar icon

Also, according to the Little Snitch app, this thing is pinging over dozen different IPs and domains across the interwebs at times. I mean, I understand pinging a couple, but this feels a little extreme.

Anyway. It gets loaded from here:

/Library/LaunchAgents/com.adobe.AdobeCreativeCloud.plist

Run this in Terminal to disable it for your user:

launchctl unload -w /Library/LaunchAgents/com.adobe.AdobeCreativeCloud.plist

If you find you miss it, bring it back thusly:

launchctl load -w /Library/LaunchAgents/com.adobe.AdobeCreativeCloud.plist

You can also delete that .plist after running launchctl unload to shut its shenanigans down even further. I haven’t done that, though, so proceed at your own risk on that one.