Caesar Salad

salad

Years ago there was this iPad app called “The Best Ceasar”, some bloke thought it was a good idea to make one whole app just about one recipe… he was right. The app is long gone but here is the gist of the recipe as far as I remember it.

Dressing

Ingredients

  1. Anchovies
  2. Garlic
  3. Worcestershire Sauce
  4. Red Wine Vinegar
  5. Olive Oil
  6. Parmesan Cheese
  7. Salt
  8. Pepper

Preparation

  1. Smash the anchovies in a bowl into a fine paste.
  2. Press in 3 cloves of garlic.
  3. Add a dash of olive oil (depending on how oily your anchovies are).
  4. Add a table spoon of Worcestershire sauce.
  5. Add a table spoon of red wine vinegar.
  6. Add salt and pepper to taste.
  7. Mix it all together.
  8. Add in parmesan to obtain a thick-ish dressing.

Croutons

Ingredients

  1. Bread
  2. Olive Oil
  3. Garlic

Preparation

My current go-to recipe is to:

  1. Press garlic into a bowl of olive oil.
  2. Baste bread slices with the garlic oil.
  3. Put the slices in an oven until crispy.
  4. Cut the toasted bread into croutons.

Chicken

Ingredients

  1. Chicken Breast
  2. Salt
  3. Pepper
  4. Olive Oil

Preparation

If the chicken breast is thicker than about 15mm, cut it in half (to make it thinner).

  1. Pre-heat the oven to 200ºC (or 180 with fan on).
  2. Salt and pepper the chicken.
  3. Put some olive oil in a pan and heat it at high temperature.
  4. Sear the chicken on all sides until golden on the outside.
  5. Put the chicken in the oven, cook until the inner temperature is a bit under 75ºC.
  6. Dice the chicken.

Serving

Wash and dry (as much as you can) a couple of heads of romaine lettuce. Add the dressing and toss it properly. Mix in the chicken and croutons, toss it again, sprinkle with more grated parmesan.

How to Get the Local IP Address of an Apple Watch

The Apple Watch does not display its local IP address in the settings for some reason. Here is one way to get it, if you have a second Mac on the network.

  1. Open terminal on the mac, run python -m SimpleHTTPServer.
  2. Get your Mac’s local IP address (option+click on the network icon in the toolbar).
  3. Send yourself an iMessage with text http://YOUR_MACS_IP:8000.
  4. Turn WiFi off on your iPhone. This is important because the Apple Watch can use your phone’s WiFi.
  5. Open Messages on your watch, find the message, tap on the link.

In the terminal on your Mac you will see something like 192.168.0.21 - - [12/Oct/2021 12:00:00] "GET / HTTP/1.1" 200 -.

And there you have your Watch’s IP address.

Carrot Spice Muffins

I’ve been trying baking lately but making a large cake badly and then eating it isn’t much fun. Here is a recipe for a great muffin instead:

Ingredients

I’m using espresso cups for measurement, these doses are for four small muffins. Multiply as necessary.

ingredients

  1. One and half cup of plain wheat flour
  2. One cup of brown sugar
  3. One cup of grated carrots
  4. About 25g of unsalted butter, softened at room temperature
  5. A spoonful of apple sauce
  6. One teaspoon of cake spices—this one in particular is a mix of dried orange, cassia, ginger, nutmeg and caraway from M&S
  7. One large egg (separate yolk from white)
  8. One teaspoon of baking powder

Preparation

Start by mixing the dry ingredients (flour, sugar, spices and baking powder) together.

Add butter, apple sauce, and egg yolk. Mix thoroughly together until you have a consistent batter.

Whip up the egg whites into snow.

batter

Progressively add the snow into the batter, carefully mixing them together.

Pour the mix into small muffin forms.

poured

Cooking

Preheat the oven to 180°C. Bake for 20 minutes at 180ºC (with convection on).

muffins

Abusing the SwiftUI Switch Statement

My now preferred way of handling complex if/else statements inside a SwiftUI view:

fileprivate struct ReminderTime: View {
  let date: Date
  let reminder: Reminder
  var body: some View {
    switch true {
    case reminder.dueDate == nil:
      Text("Reminder.Time.Nil")
    case Calendar.current.compare(reminder.dueDate!, to: date, toGranularity: .day) == .orderedAscending:
      Text(reminder.isCompleted ? "Reminder.Time.Done" :  "Reminder.Time.Overdue")
    case reminder.isAllDay && Calendar.current.isDate(date, inSameDayAs: reminder.dueDate!):
      Text("Reminder.Time.Today")
    case reminder.isAllDay:
      Text(reminder.dueDate!, style: .date)
    case !reminder.isCompleted && date > reminder.dueDate!:
      HStack {
        Image(systemName: "exclamationmark.triangle")
        Text(reminder.dueDate!, style: .time)
      }
    default:
      Text(reminder.dueDate!, style: .time)
    }
  }
}

At least until the if let construct will work.

Storing Colors in Core Data—The Whole Story

The task: Store colors (Color, UIColor and CGColor) in Core Data, while remaining 100% compatible with the SwiftUI color picker.

Setting Up the Core Data Model

In your model add an attribute to your entity and set its type to Transformable. Select it and in the Data Model Inspector (right pane in Xcode, last tab) set the Transformer to “SerializableColorTransformer” and Custom Class to “SerializableColor”.

These two strings are magic values, and I’ll explain their origin later.

The whole thing should look like this:

ss-xcode-data-model

Code Implementation

Core Data has several requirements on what can be stored. It has to be NSCoding or NSSecureCoding. This in turn means that it has to be an Obj-C class. This rules out both CGColor (a non-Obj-c class) and Color (a struct).

In my case I don’t care about color spaces other than P3 and sRGB. Let us create a class that stores red, green, blue and alpha components and a color space. The class will transform any color space other than P3 into sRGB.

Here is the whole listing for the class, which I called SerializableColor. This is where the magic string for Custom Class in the previous section comes from.

// SerializableColor.swift

import Foundation
import struct CoreGraphics.CGFloat
import class CoreGraphics.CGColor
import class CoreGraphics.CGColorSpace
import class UIKit.UIColor
import struct SwiftUI.Color

public class SerializableColor: NSObject, NSCoding, NSSecureCoding {
  public static var supportsSecureCoding: Bool = true
  
  public enum SerializableColorSpace: Int {
    case sRGB = 0
    case displayP3 = 1
  }
  
  let colorSpace: SerializableColorSpace
  let r: Float
  let g: Float
  let b: Float
  let a: Float
  
  public func encode(with coder: NSCoder) {
    coder.encode(colorSpace.rawValue, forKey: "colorSpace")
    coder.encode(r, forKey: "red")
    coder.encode(g, forKey: "green")
    coder.encode(b, forKey: "blue")
    coder.encode(a, forKey: "alpha")
  }
  
  required public init?(coder: NSCoder) {
    colorSpace = SerializableColorSpace(rawValue: coder.decodeInteger(forKey: "colorSpace")) ?? .sRGB
    r = coder.decodeFloat(forKey: "red")
    g = coder.decodeFloat(forKey: "green")
    b = coder.decodeFloat(forKey: "blue")
    a = coder.decodeFloat(forKey: "alpha")
  }
  
  init(colorSpace: SerializableColorSpace, red: Float, green: Float, blue: Float, alpha: Float) {
    self.colorSpace = colorSpace
    self.r = red
    self.g = green
    self.b = blue
    self.a = alpha
  }
  
  convenience init(from cgColor: CGColor) {
    var colorSpace: SerializableColorSpace = .sRGB
    var components: [Float] = [0, 0, 0, 0]
    
    // Transform the color into sRGB space
    if cgColor.colorSpace?.name == CGColorSpace.displayP3 {
      if let p3components = cgColor.components?.map({ Float($0) }),
         cgColor.numberOfComponents == 4 {
        colorSpace = .displayP3
        components = p3components
      }
    } else {
      if let sRGB = CGColorSpace(name: CGColorSpace.sRGB),
         let sRGBColor = cgColor.converted(to: sRGB, intent: .defaultIntent, options: nil),
         let sRGBcomponents = sRGBColor.components?.map({ Float($0) }),
         sRGBColor.numberOfComponents == 4 {
        components = sRGBcomponents
      }
    }
    self.init(colorSpace: colorSpace, red: components[0], green: components[1], blue: components[2], alpha: components[3])
  }
  
  convenience init(from color: Color) {
    self.init(from: UIColor(color))
  }
  
  convenience init(from uiColor: UIColor) {
    self.init(from: uiColor.cgColor)
  }  
  
  var cgColor: CGColor {
    return uiColor.cgColor
  }
  
  var color: Color {
    return Color(self.uiColor)
  }
  
  var uiColor: UIColor {
    if colorSpace == .displayP3 {
      return UIColor(displayP3Red: CGFloat(r), green: CGFloat(g), blue: CGFloat(b), alpha: CGFloat(a))
    } else {
      return UIColor(red: CGFloat(r), green: CGFloat(g), blue: CGFloat(b), alpha: CGFloat(a))
    }
  }
}

// MARK: Transformer Class
// For CoreData compatibility.

@objc(SerializableColorTransformer)
class SerializableColorTransformer: NSSecureUnarchiveFromDataTransformer {
  override class var allowedTopLevelClasses: [AnyClass] {
    return super.allowedTopLevelClasses + [SerializableColor.self]
  }
  
  public override class func allowsReverseTransformation() -> Bool {
    return true
  }
  
  public override func transformedValue(_ value: Any?) -> Any? {
    guard let data = value as? Data else {return nil}
    return try! NSKeyedUnarchiver.unarchivedObject(ofClass: SerializableColor.self, from: data)
  }
  
  public override func reverseTransformedValue(_ value: Any?) -> Any? {
    guard let color = value as? SerializableColor else {return nil}
    return try! NSKeyedArchiver.archivedData(withRootObject: color, requiringSecureCoding: true)
  }
}

The important parts are described as follows.

To register the transformer (explained below), add this line to the init() method of your @main. For example

@main
struct Eventail_v4App: App {
  init() {
    // ...
    ValueTransformer.setValueTransformer(
      SerializableColorTransformer(),
      forName: NSValueTransformerName(
        rawValue: "SerializableColorTransformer"))
  }
  // ...
}

The rawValue is where the Transformer magic string in the model come from.

Deeper dive

Instead of commenting the file excessively, here are explanations of details.

NSCoding inheritance

For this we implement encode(with coder: NSCoder) and init?(coder: NSCoder). These methods will enable the class to be serialized and deserialized using any standard decoder/encoder.

Note: As you can see this class will interpret any unknown color space as sRGB, this means that this is not backwards compatible.

Color space transformations

These happen in two places. When encoding the value, the class always passes through CGColor as this is the only color type that can be split into components.

Note: In init from Color I bounce through init from UIColor, this is because weirdly Color.blue.cgColor == nil but UIColor(Color.blue).cgColor has a value.

For deserialization I always pass through UIColor as this type has convenience initializers for both sRGB and P3 colorspaces.

Transformer

Core Data can write only a few basic types, including NSData. Transformers are classes that can be registered for specific types and transform them into, and from NSData.

In order to implement a transformer you need to do three things:

  • Write a class which inherits from NSSecureUnarchiveFromDataTransformer and overrides the allowedTopLevelClasses class variable adding your class type to the list
  • Override the transformedValue and reverseTransformedValue methods. These are named as seen from CoreData perspective.
    • transformedValue transforms NSData into your class, in this case SerializableColor.
    • reverseTransformedValue transforms the class into NSData. You do not need to implement this method if you are never writing to CoreData yourself. In this case change the return value of allowsReverseTransformation to false.
  • Register the transformer in your @main somewhere.

Creating Sf Symbols with Affinity Designer

Creating SF Symbols is supposed to be easy. Create a template using SFSymbols app. Modify the SVG and import in Xcode. It is all documented here.

SFSymbols template requires you to manually create and adjust images for all weights and sizes. This is fine if your image has lines that need to follow the weight. Many of them don’t or could auto scale.

Nevertheless I’ve made this Affinity Designer template that you can use. Open it, edit the one symbol it contains (called Template) and you are all set.

ss-affinity

⬇ sfsymbol-template.afdesign

Unfortunately although Affinity Designer produces a perfectly valid SVG—and it will get validated by SFSymbols—Xcode does not like it.

This is what Affinity exports:

<g transform="matrix(1.22074,0,0,1.22074,498.674,600.389)">
   <g id="Ultralight-S">

This is what Xcode wants:

<g id="Ultralight-S" transform="matrix(1.22074,0,0,1.22074,498.674,600.389)"> 

Alright, let’s do Perl:

#!/usr/bin/perl
my $file = $ARGV[0];
open FILE, $file or die "Failed to open file: $!";
$/ = undef;
my $xml = <FILE>;
close FILE;
my @weight = (
  "Ultralight", "Thin", "Light", "Regular", "Medium",
  "Semibold", "Bold", "Heavy", "Black",
);
my @size = ("S", "M", "L");
for my $w (@weight) {
  for my $s (@size) {
    $xml =~ s/<g (transform=[^>]+?)>\s+<g (id="$w-$s")>(.*?)<\/g>/<g \2 \1> \3/s;
  }
}
print($xml, "\n");

This will finally transform the SVG into a format that Xcode will swallow.

⬇ fix-sfsymbol.perl

Tricking Programmers Into Being Helpful

People[1] accuse Stack Overflow of being unwelcoming to newbies. Older programmers complain that the site quality has decreased because of the influx of low quality questions.

I am part of the latter crowd. I expect that if I give out my time to help somebody, they will reciprocate and invest time into understanding the problem and redact a concise question. Unfortunately when one goes through review queues on Stack Overflow, a good portion of the asked question is copy pasted textbook exercises.

Here is a thought:

Even though answering computer puzzles is annoying on a Q/A website, there is a whole bunch of websites specializing in just that—interview preparation courses.

What if we mixed the two? Imagine a service that gives you short programming challenges. Except these would be generated from low effort Stack Overflow questions1. Posted solutions would get sent to Stack Overflow until an answer is accepted. After that the question would stay up, but answers would no longer propagate as we don’t want to spam the Stack Overflow questions.


  1. Maybe only in part. Ideally the service would have more structured problems too. ↩︎

Staying in Shape During Lockdown

The thing I miss the most from times before the lockdown is my Gym. After years of not being able to stick to any sport I have managed to find my thing, and that was weight lifting with few exercises that don’t change often. This allowed me to concentrate on the form and most importantly: see the progression.

Nowadays, I struggle to get motivated to move. At first I took up running but then they banned that too in normal hours. I took up Convict Conditioning again a bit, which is an interesting book, and also got recommended the darebee site. But both of these have the same issue, the progression can only really be measured in the number of repetitions or sets, and that just does not really work for me. I still do the work, because one must, but not nearly enough to keep the blood pumping.

So I took up a game I played a lot 10-15 years ago—Stepmania.

ddr-setup

Stepmania is the open source program that is compatible with data for Dance Dance Revolution, the first game in the genre. Back in the day I was one of the first members of the DDR.SK1 organization that promoted machine dance games in Slovakia, and organised a few tournaments. Stepmania was also base of various commercial iterations such as In The Groove or Mungyodance. The great thing about all these is that the data is compatible, so it is now possible to shove it all into one program and play.

The great thing about Stepmania is that the progression is clear: you either finished a song or not, and you have a score. The game itself is quite a bit more physical than your Sunday jog, so it’s also perfect for me.

I am unsure if I will continue playing after the confinement, but I am glad that I still have the mat and a physical activity that is not a chore.

I lost quite a bit of practice and speed but luckily it’s like biking, it eventually comes back.

ddr-anthem


  1. Since then it was bought and merged into another org. ↩︎

How to get into a junior position and keep it

You are fresh out of school looking for your first job, maybe you have done an internship or have some other first experience. The most impressive thing on your resume is still sitting in the Education section. You might be asking yourself: “How do I stand out?”

Be familiar with what you are selling

One thing people often do, is to pad their resume with every school project and tech they had a course on. This is understandable: what else should they be talking about, right? Not everybody has side projects that are in line with their career choices.

Although I disrelish sifting through pages of resumes, I don’t get too worked up about it. Unless the position you are applying to is massively coveted and the company will receive hundreds or applications, people responsible for picking candidates will sit through it. I know I did.

Now here is the principal thing:

Only mention projects and tech that you are confortable discussing at length, and in detail.

Reading a three page resume is a chore but can be done in a couple of minutes. A candidate in the room, however, deserves attention for at least half an hour. The job of the interviewer is to learn about the candidate and find out if they will fit the company and be productive. With nothing but the resume to grasp on, the conversation naturally gravitates towards the mentioned projects. So if I ask you what was your role during a particular project, what were the challenges, or what did you like or dislike when using a particular tech, you’d better have an answer ready.

Understand things before tearing them apart

There is a large chance you will be recruited as a part of a team. You may have read a lot about 10x developers but don’t pay too much attention to that. Most software is not written by superstars1.

Personality is something that will be evaluated during the probation period. A good personality indicator for coders is the manner how they approach an existing code base. Usually the first (and second… probably the third too) impression is that the code sucks. This is for two reasons: First is because it was written by people. Second is for you to discover. And I mean this quite literally.

Before spewing expletives ask your colleagues to explain why the code works the way it does, why is it structured as it is and so on. Chances are there are reasons that may not make sense now but may have when the code was written.

Some examples:

  • The code was written before a feature X in language even existed. Rewriting it is not a priority.
  • The code originally depended on library X but it was refactored out.
  • Implementing feature X was required to secure funding 10 years ago, it was never used since and there was no time to remove it.2

The first reflex is the urge to rewrite everything. I invite you to read this article by Joel Spolsky to find out why this is a really bad idea.

Only once you have understood the reasons, you can start fuming and lamenting what a pile of garbage you have inherited. But this will keep you from making mistakes that could lead to disaster and lost time down the road. Example: Refactoring a library that ends up breaking some client’s plugin they depend on. It will also avoid being the obnoxious winger during lunch.

A homework assignment

I challenge you to go through your resume and write notes about every project you chose to mention. Think about your role and contribution. Recall how you solved a particular problem, or at least how the final presentation went. I am sure you were not one of the freeloaders, so prove it.


  1. Also they probably don’t read recruiting advice on random blogs. ↩︎

  2. My personal favorite. ↩︎

System Shock

First time I played System Shock was around 1998 but I have never managed to find one button so I never finished it. Two decades later, Night Dive studios released the Enhanced Edition current hardware and has “modern” controls. I started a game, completed several levels, and then real life started interfering so I shelved the game once again.

In April of year 2020, the Dos Game Club selected System Shock as the game for the month. This time I decided to finish it.

And finish it I did.

ss-end-screen

After confined corridors, come massive halls.

After completing it I felt the same void one does after having finished a book. The story is thrilling. Watching the fates of various protagonists unfold is fascinating. The game is technologically impressive for the time with varying environments, lightning and shading pushing the 256-color graphics to the limit and dynamic soundtrack seamlessly switching tracks depending on the situation. Although clearly aged the game has managed to completely pull me in.

A very interesting technical achievement was security cameras that were rendering in-game spaces real time. At some point I wondered it it was possible to get two of the cameras display at the same time and it turns out that although it is possible, the authors tried to make this impossible. Observe:

See how the screen on the right turns into static when the one in the left comes into field of view? That’s ingenious.

I find that it still has the best kinds of puzzles. You know, actual puzzles, not some sort of dumbed down mini games, jumping puzzles or crafting. Jump scares often result in instant death so the player needs to stay alert all the time, making them even worse.

There are other features of the game I still miss in modern games, for example the possibility to take notes on the map. This has come very handy to keep track of various objects I kept lying around for later. Come to think of it, since most modern story based games are laid out as one long hallways they probably don’t need the notes.

Level design is a masterpiece too, although it is required to go back and forth, the game always opens a shortcut once a sector is cleared so it makes it easier to navigate the large space station.

Voice acting is top notch, albeit I found it distracting that the voice does not match the text description, so I ended up reading everything twice.

Can I go in there, or is it just another wall?

One thing I need to rant about is the cyberspace sections. These are unnecessarily tedious, mostly because of the transparency of the walls making it really hard to know where to go at some points. Especially for someone like me who has already hard time evaluating distances and knowing my left from right. The controls are quite wooden and combatting ICE nodes requires a dose of luck and patience more than anything else.

I like how Shodan gets progressively madder at each step you make. This is something I don’t remember from System Shock 2 but I’ll have to replay it and see. As an antagonist she is really one of the best out there.

I don't think it's me who has no hope.

ss-notes

System Shock has left me with more than just good feelings. I have also kept something physical: my notes I took during the playthrough, to keep track of codes and objectives. At some point the player has to find a malfunctioning relay. I have missed the data log specifying its identification number so I had to scour the engineering level and find all of the relays, then enter them into the diagnostics tool and find which one was broken. Paper and map notes were wery handy in this instance.

Since I have not kept track of the numbers given by the destroyed comptuers, I had to take a scenic route just before the ending and re-visit all of the previous locations and find all of the places where the computers were.

I went into the last room doped on every possible substance.

Due to my play style I have ended up with a ton of grenades, ammunition and healing drugs to last me for the final combat twenty times over. I wonder if it would be possible to design a game in a way to force me to not squirrel objects that much. At the same time, the laser rapier is a mighty good weapon once you have found the closest power station. The end fight was relatively easy and final cyberspace section ended surprisingly quickly.

See you on the Von Braun!