4 Years in Switzerland

view from the Klausenpass

Four years ago today my plane touched down in Switzerland. I’ll be honest: I had higher hopes for 2021 a year ago. In some ways things are definitely better: Trump is no longer the U.S. President and amazing COVID-19 vaccines have been developed and are widely available in both the U.S. and Europe. Everyone in my immediate family has been vaccinated. On the flip side, the Delta variant is worrying and I don’t know when if ever things will be normal-ish again. I haven’t had any German lessons since the pandemic began, and I feel like we haven’t made much progress on integrating into Swiss culture. But unable (or unwilling) to travel abroad, we have gotten to know more out of the way parts of Switzerland over the past year.

I spent a 9-month stint working with the amazing mobile team at BlockFi on a native re-write of their mobile app. I’m quite proud of the accessibility support that we built into the app. In May I returned to work at Y Media Labs in a role as principal engineer.

It’s been a cold, damp, rainy summer so far in our corner of Switzerland. The weather for water sports has not been very good. It feels like the summer that never was.

What’s next? I’m still not sure, but it looks like we’ll get another year here, maybe two. Hike even more mountains. Snowshoe through the winter. Work on my cross-country skiing.

3 Years in Switzerland

Wow. It’s sort of hard to believe that it’s been three years to the day that we arrived in Switzerland. Also hard to believe that it’s already been two years since I wrote this post. On the other hand, the past five months have felt like two years unto themselves.

Some things have changed, others have not. We still don’t know how much longer we’ll stay here (our right to stay depends upon a local job and a permit that must be renewed annually). We still have not really integrated into society here.

On the other hand, I’ve learned a lot more German (after two years of twice-weekly German classes), we’ve traveled pretty extensively both within Switzerland and around Europe (not so much in 2020), and I’ve hiked quite a few mountains.

I still love it here. It’s not perfect. No place is. As much as we complain about the (belated, confusing, mistake-ridden) coronavirus response here, it’s worlds better than the tragedy/disaster that is the United States’ handling of the pandemic.

What’s next? I’m not sure, but it looks like we’ll get another year here. Hike some more mountains, I guess. Explore Portugal more when it’s safe to do so. Work on our bagel baking.

Cooking in the time of coronavirus

More coronavirus cases are announced every day. Switzerland is a small country (population 8.2 million), but not only are we in the top 10 for total number of cases, we’re #2 for cases per capita. It’s not surprising really considering that we’re next door to Italy and precautions were not taken.

A few weeks ago the government banned events with more than a thousand people. Last Friday the government closed all schools until April 4, banned events with more than a hundred people, and limited restaurants to 50 people. On Monday the government closed all non-essential shops, restaurants, bars, cinemas, pools, gyms, etc. Today the government issued a recommendation that everyone should stay at home unless they need to leave for work, buy food or supplies, or help others. Maybe tomorrow that recommendation will be made mandatory.

So much for going for walks in the farm fields. That seemed to me to be harmless enough, but ok. Stay home it is.

Fine. I’ve worked from home for nearly two decades now. My wife was working from home two days a week, but now it’s full-time. Our son has online learning arranged through his school for 6 hours every school day. He’s on day 2 of that and really enjoying the novelty of it. It’s a bit hard to concentrate at times with everyone here, but we’re so lucky that we are able to stay put.

I’ve been cooking a lot recently. I started cooking more frequently in January when I got some new cookbooks and began adopting a new diet (more on that some other time), but in the last few weeks it’s been even more. Part of it is wanting to stock up the freezer in case we get quarantined. Whereas in the past I might make a big pot of beans or soup and eat it for three days in a row, now we’ll eat it for one day, freeze the rest, and I’ll cook something new the next night. Part of it is wanting to eat as healthy as possible in the hope that it will help our immune systems. Part of it is just indulging myself with delicious food. The world may seem as if it’s coming apart at the seams (it’s not really– I know that), but at least right now in this moment I am with my family, our bellies are full, and our tastebuds are happy. Part of it is probably that cooking is one aspect of my life where I can easily assert control. The world may feel like it’s spinning out of control, but here in our kitchen I can cook good food. Some people hoard toilet paper. I cook.

I suppose it’s a coping mechanism. I’m ok with that.

How (not to) encode an array

Sounds simple, right? But yesterday I messed up this simple operation and was stuck for a couple hours wondering why an API request wasn’t working.

Background

A pattern I follow is to use Swift struct‘s to represent each network request my app needs. Each struct has only let properties and conforms to Encodable so that it can be serialized to JSON and sent to the server by my networking class. Typically this is straightforward and the top-level JSON object is almost always a dictionary.

For example:

struct MyRequest: Encodable {
  let uid: String
  let date: Date
  let bar: Int?
}

Thanks to the magic of the compiler, this will encode to a JSON dictionary just fine without any boiler plate code on my side.

Occasionally if the names of the properties don’t match the names of the parameters the API expects (typically because the API is not in my control and follows some naming convention I don’t like – or even no naming convention whatsoever), then I need to declare CodingKeys to properly map the encoding.

Some times there’s no avoiding it though and I need to implement the encode function myself. I often need this when the server expects some weird Date format or some business logic is needed for the encoding.

The Issue

Yesterday I implemented a simple network request that needed to be serialized as an array.

struct CallsRequest: Encodable {
  let calls: [Call] // Call conforms to Encodable
}

The trouble is that this would encode to a Dictionary with one key “calls” whose value is the array. What I needed was to encode to an Array instead.

func encode(to encoder: Encoder) throws {
  var values = encoder.unkeyedContainer()
  try values.encode(calls)
}

I had never used unkeyedContainer() before. Previously I’ve always used container(keyedBy:), which encodes as a dictionary (hence keyed).

The output of the approach above was an object of type [[Call]]. That is, it encodes an array of arrays instead of just an array. (Someone else spotted the extra brackets in the JSON output. Thanks Anton!)

It took me a while to realize where the extra level of Array was coming from, but then it dawned on me: the unkeyedContainer() is an Array, just as the container(keyedBy:) is a Dictionary.

So what I really needed was something like this:

func encode(to encoder: Encoder) throws {
  var values = encoder.unkeyedContainer()
  for call in calls {
    try values.encode(call)
  }
}

This delivered the expected output of type [Call]. Problem solved.

Anyway, I hope that someday this might save someone a few hours of frustration.

 

Update

Where the real JSON encoding happens is via another custom protocol that I didn’t mention. Basically I want objects to be able to convert themselves to Data so that they could be set as the body to an HTTP PUT/POST/PATCH/DELETE request.

public protocol ParametersBuilder {
    func data() throws -> Data
    func dictionary() throws -> [String: Any]
}

dictionary() is for GET requests or for .formUrlEncoded request bodies. But data() is used for everything else.

The default implementation of data() is this:

extension Encodable {
  public func data() throws -> Data {
    return try JSONEncoder().encode(self)
  }
}

(which is why CallsRequest conforms to Encodable and what by default would turn it into a dictionary)

A simpler solution to my problem would have been to provide a custom implementation of data() on CallsRequest instead of conforming to Encodable:

struct CallsRequest: ParametersBuilder {
  let calls: [Call] // Call conforms to Encodable
    
  public func data() throws -> Data {
    return try JSONEncoder().encode(calls)
  }
}


 

Update 2

Rob Napier pointed out to me that I could use singleValueContainer to skip having to loop through the array.

func encode(to encoder: Encoder) throws {
  var values = encoder.singleValueContainer()
  try values.encode(calls)
}

Hiking Group: Mattstock

17 September 2019
7.6km, 630m ascent, 3h, 14°C

This was a difficult hike for me. I kept myself hydrated and good on salts, but it was very difficult nevertheless. The last time my heart worked this hard was the Stanserhorn hike almost a year ago (which involved twice as much ascending and is a much harder hike). I think it was because I let the group push me to hike faster than was good for my body. I can definitely do the hike, just not quite that fast. I also think I was feeling the altitude at the end of the hike.

It was a beautiful hike. The Mattstock is located in St. Gallen just north of the Walensee. We parked at Amden and then took a chairlift up to around 1,300m. From there we hiked to the summit at 1,935m. It was all steeply uphill and technically a bit difficult (T3 rating). While it was 14°C at the beginning of the hike at 10am, it was surely in the 20’s by the end and while we were approaching the mountain hut, I felt like I was burning up. Most of the hike was above the treeline, and the sun beat down upon us relentlessly. We should have had nice views of the Walensee throughout the hike, but it was mostly or partly obscured by clouds.

100m below the summit we stopped at a mountain hut to rest. When we first got there I was already exhausted and thought I would skip the summit. But after 5-10 minutes of rest I decided to try the summit at my own pace (stopping frequently). In the end I did make it to the top. The last bit involved scrambling with both hands and holding onto a steel cable bolted to the rock.

At the peak there was a cross and a logbook for hikers to sign their names in. This was the first time I have signed such a book. I hope to get the chance to sign a few more mountain books on this year’s hikes.

This was an out-and-back hike, so we had to retrace our steps and descend everything that we had climbed. The steep descent with tricky footing meant that it was not necessarily easy, but at least it was not a challenge cardio-vascularly. I developed a blister on my right big toe during the descent, probably because that boot was not tied tightly enough.

Route Map

Mattstock map

How to declare a Core Data transient property in Swift

TL;DR: use @objc
 
Source: the 5th comment to the 2nd answer to this Stack Overflow question.
 
Sample code:

@objc public var firstInitial: String {
        
    willAccessValue(forKey: "firstInitial")
    let name = self.name
    didAccessValue(forKey: "firstInitial")
        
    guard let groupName = name,
        let initial = groupName.first else { 
            return ""
    }
        
    return String(initial).uppercased()
}

(and yes I know there are many different ways I could compute the first initial of a string.)

Hiking Group: Sattel Hochstuckli

10 September 2019
11 km, 510m ascent, 3h, 9°C

First group hike of the new school year! This is my second season hiking with the group from my son’s school.

This was only my second time hiking up to the Hochstuckli, but I skied from there many times last winter.

Here’s the view from the top in winter and summer:

Status: 2019-09-11 19.14.14
Status: 2019-09-11 19.14.31

We took the gondola up from Sattel (in the valley) to Mostelberg and then did a loop hike from there up to Hochstuckli, the peak. The weather was gorgeous and the temperature perfect. The hike wasn’t too difficult considering how little hiking I’ve done since June, but I’d be lying if I said I wasn’t creaky and a bit sore by the end of it.

All in all a great start to the hiking season. I’m hoping to be able to do even more hikes this year. Last year I chickened out several times when I thought that the hike was either too hard or the weather too cold/rainy/snowy. I know now that I can manage all the hikes even if it takes me a bit longer than the (super fit and super awesome) leaders.

Route map

Hiking Group: Boden-Zugerberg loop

21 May 2019
8.3km, 300m ascent, 2h, 10°C

This was my first group hike since 9 April due to Spring break, a dev conference, and other scheduling conflicts plus inertia.

This was a new route up the Zugerberg for me and could become a regular workout hike since it is close to home and neither too hard nor too easy.

Boden hiking group

The Spring flowers are out in full force in the farm fields of the Ägerital. The first part of the hike works its way up through successively higher meadows before entering the forest for the final steep(ish) push up to the top of the Zugerberg. From there we followed the ridge south for a while before descending back into the valley to return to our starting point.

Hiking through Zugerberg forest

Route map from my watch:

Status: 2019-05-27 14.01.01

Hiking Group: Albishorn

Albishorn View

9 April 2019
7.7km, 410m ascent, 1h50m, 7°C
A nice hike up the Albishorn on a damp, drippy Spring morning. The hiking group did the Albishorn last September but from the west (Lake Zug) side. This hike ascended from the east (Lake Zürich) side. Once again the weather didn’t make for very good views.

Albishorn Group Photo

Route map:

Albishorn Route

Hiking Group: Zugerberg

Zugerberg Moors

2 April 2019
10.2km, 480m ascent, 2h30m, 8°C

This hike began with a steep ascent through the forest from Schönegg, continued with a relatively flat stretch traversing the moors atop the Zugerberg (shown above), and ended with a descent down into Unterägeri on the opposite side of the mountain.

Route:

Zugerberg Route