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
Anchovies
Garlic
Worcestershire Sauce
Red Wine Vinegar
Olive Oil
Parmesan Cheese
Salt
Pepper
Preparation
Smash the anchovies in a bowl into a fine paste.
Press in 3 cloves of garlic.
Add a dash of olive oil (depending on how oily your anchovies are).
Add a table spoon of Worcestershire sauce.
Add a table spoon of red wine vinegar.
Add salt and pepper to taste.
Mix it all together.
Add in parmesan to obtain a thick-ish dressing.
Croutons
Ingredients
Bread
Olive Oil
Garlic
Preparation
My current go-to recipe is to:
Press garlic into a bowl of olive oil.
Baste bread slices with the garlic oil.
Put the slices in an oven until crispy.
Cut the toasted bread into croutons.
Chicken
Ingredients
Chicken Breast
Salt
Pepper
Olive Oil
Preparation
If the chicken breast is thicker than about 15mm, cut it in half (to make it thinner).
Pre-heat the oven to 200ºC (or 180 with fan on).
Salt and pepper the chicken.
Put some olive oil in a pan and heat it at high temperature.
Sear the chicken on all sides until golden on the outside.
Put the chicken in the oven, cook until the inner temperature is a bit under 75ºC.
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.
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.
Open terminal on the mac, run python -m SimpleHTTPServer.
Get your Mac’s local IP address (option+click on the network icon in the
toolbar).
Send yourself an iMessage with text http://YOUR_MACS_IP:8000.
Turn WiFi off on your iPhone. This is important because the Apple Watch
can use your phone’s WiFi.
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 -.
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:
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.swiftimportFoundationimportstructCoreGraphics.CGFloatimportclassCoreGraphics.CGColorimportclassCoreGraphics.CGColorSpaceimportclassUIKit.UIColorimportstructSwiftUI.ColorpublicclassSerializableColor:NSObject,NSCoding,NSSecureCoding{publicstaticvarsupportsSecureCoding:Bool=truepublicenumSerializableColorSpace:Int{casesRGB=0casedisplayP3=1}letcolorSpace:SerializableColorSpaceletr:Floatletg:Floatletb:Floatleta:Floatpublicfuncencode(withcoder: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")}requiredpublicinit?(coder:NSCoder){colorSpace=SerializableColorSpace(rawValue:coder.decodeInteger(forKey:"colorSpace"))??.sRGBr=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=colorSpaceself.r=redself.g=greenself.b=blueself.a=alpha}convenienceinit(fromcgColor:CGColor){varcolorSpace:SerializableColorSpace=.sRGBvarcomponents:[Float]=[0,0,0,0]// Transform the color into sRGB spaceifcgColor.colorSpace?.name==CGColorSpace.displayP3{ifletp3components=cgColor.components?.map({Float($0)}),cgColor.numberOfComponents==4{colorSpace=.displayP3components=p3components}}else{ifletsRGB=CGColorSpace(name:CGColorSpace.sRGB),letsRGBColor=cgColor.converted(to:sRGB,intent:.defaultIntent,options:nil),letsRGBcomponents=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])}convenienceinit(fromcolor:Color){self.init(from:UIColor(color))}convenienceinit(fromuiColor:UIColor){self.init(from:uiColor.cgColor)}varcgColor:CGColor{returnuiColor.cgColor}varcolor:Color{returnColor(self.uiColor)}varuiColor:UIColor{ifcolorSpace==.displayP3{returnUIColor(displayP3Red:CGFloat(r),green:CGFloat(g),blue:CGFloat(b),alpha:CGFloat(a))}else{returnUIColor(red:CGFloat(r),green:CGFloat(g),blue:CGFloat(b),alpha:CGFloat(a))}}}// MARK: Transformer Class// For CoreData compatibility.@objc(SerializableColorTransformer)classSerializableColorTransformer:NSSecureUnarchiveFromDataTransformer{overrideclassvarallowedTopLevelClasses:[AnyClass]{returnsuper.allowedTopLevelClasses+[SerializableColor.self]}publicoverrideclassfuncallowsReverseTransformation()->Bool{returntrue}publicoverridefunctransformedValue(_value:Any?)->Any?{guardletdata=valueas?Dataelse{returnnil}returntry!NSKeyedUnarchiver.unarchivedObject(ofClass:SerializableColor.self,from:data)}publicoverridefuncreverseTransformedValue(_value:Any?)->Any?{guardletcolor=valueas?SerializableColorelse{returnnil}returntry!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
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.
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.
#!/usr/bin/perlmy$file=$ARGV[0];openFILE,$fileordie"Failed to open file: $!";$/=undef;my$xml=<FILE>;closeFILE;my@weight=("Ultralight","Thin","Light","Regular","Medium","Semibold","Bold","Heavy","Black",);my@size=("S","M","L");formy$w(@weight){formy$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.
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.
Maybe only in part. Ideally the service would have more structured problems too. ↩︎
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.
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.
Since then it was bought and merged into another org. ↩︎
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.
Also they probably don’t read recruiting advice on random blogs. ↩︎
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.
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.
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.
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.
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.