Caesar Salad

2 minutes read

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

One minute read

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

One minute read

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

One minute read

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

5 minutes read

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: