Lukas Z's Tech Blog

CASIO W96-H: How to Turn the Hourly Beep on and Off

Here’s a quick guide on how to turn that hourly beep off with Casio W96-H.

casio w96h buttons


  1. In normal time mode press button 1 once to get into alarm mode (“AL” in the upper left).
  2. Press button 2 once to get into the hourly time signal screen (“:00” displayed).
  3. Press button 3 to toggle the hourly signal on and off.

Anyway, here’s a link to the manual.

Wie Wir Die Einnahmen Unserer GbR (iOS App Business) Gegenüber Dem Finanzamt Erklärt Haben

Es folgt eine kurze Anleitung, wie wir die Steuern unserer 2-Mann GbR erklärt haben. Die GbR hat auf der Einnahmenseite Apple (App Store Verkäufe und Abos) und Google (AdMob Werbung) und auf der Ausgabenseite Dinge wie Serverkosten, Übersetzungen, Kauf von Grafiken usw. Die Einnahmen werden 50:50 zwischen den beiden Gesellschaftler aufgeteilt. Es gilt die Kleinunternehmerregelung.

Die Steuererklärung wurde auf erstellt und verschickt.

Wichtiger Hinweis: Dieser Blogpost ist keine Beratung eines qualifizierten Menschen, z.B. eines Steuerberaters, sondern nur der anekdotenhafte Post eines Laien ohne Anspruch auf Korrektheit. Im Zweifel bitte auf jeden Fall also Gehirn benutzen und einen Profi fragen!

Die Arbeit gliederte sich in 2 Phasen:

  1. Aufstellung aller Einnahmen und Ausgaben anfertigen. (Rechnungen, Belege und Kontoauszüge checken und beiseite legen.)
  2. Elster-Formulare ausfüllen.

Elster Formulare

Auf dem Screenshot sieht man die 4 Formulare, die wir übermittelt haben. Ich beschreibe sie nacheinander von oben nach unten.

Formular 1: Die Gewerbesteuererklärung

GbR Gewerbesteuer

Im Screenshot sieht man, was wir ausgefüllt haben (alles andere wurde entsprechend nicht ausgefüllt). Das grüne Feld: Hier Gewinn Eintragen. Gewinn = Einnahmen - Ausgaben.

Formular 2: Gesonderte und einheitliche Festellung

GbR Gesonderte und einheitliche Feststellung 1

GbR Gesonderte und einheitliche Feststellung 1

GbR Gesonderte und einheitliche Feststellung 1

Hier haben wir 2 mal (weil wir 2 Gesellschafter sind) die Anlage FB hinzugefügt. Siehe Screenshots. Blauer und grüner Kasten: Infos der Gesellschaftler eintragen. In letzten Bild sieht man auch bei “Zähler” und “Nenner”, dass wir 50:50 geteilt haben.

Formular 3: Umsatzsteuererklärung

GbR Umsatzsteuer 1

GbR Umsatzsteuer 2

Hier haben wir im Grunde nur eine 0 erklärt. Die Beträge, die angefallen sind, unterliegen nämlich sämtlich §13b UstG (Stichwort “Reverse Charge Verfahren” bzw. “Steuerschuldumkehr”). Dies ist nicht immer der Fall! Bei uns aber schon, da unsere Einnahmen alle von Google und Apple stammen und diese Firmen es (in unserem Zusammenhang jedenfalls) so machen.

Das Grüne Feld: Hier Umsatz Eintragen. (Also Gewinn + Ausgaben.)

Formular 4: Die EÜR



Last but not least die Einnahmenüberschussrechnung.

Das grüne Feld: Hier Umsatz Eintragen. (Also Gewinn + Ausgaben.)

Das blaue Feld: Hier Ausgaben Eintragen. (Also Umsatz - Gewinn.)

Das rosa Feld: Hier Gewinn Eintragen. (Also Umsatz - Ausgaben.)


So, das wars. So viel ist das also gar nicht. Ich hoffe, ich konnte jemandem helfen, aber ich weise nochmal drauf hin, dass es hier erstens um einen Spezialfall geht: 2 Personen GbR, App Business, Einnahmen von Google und Apple. Und Zweitens übernehme ich ausdrücklich keine Haftung für Eure Taten, sprich Steuererklärungen. Ich bin Laie und berichte nur, wie wir das ausgefüllt haben.

Calculate Bank Loan Payments

I wrote a small tool that allows to compare different scenarios when borrowing money for buying real-estate.

There are a few parameters that can be set:

  • How much money do I need?
  • How much do I want to pay back every month?
  • For how long is the interest rate fixed? (Longer fixing = higher rate)
  • Am I planning to use unscheduled repayments? (Those are mandatory, but of course lower the cost of borrowing and shorten the time until you’re debt-free.)

The tool allows to compare different selections on three of those parameters (the loan sum is currently fixed).

Please try it out here and let me know if you like it!

Calculating the Deltas for performBatchUpdates

Poly Art Title

Time for a code snippet post.

If you have collection or table view that can change in unpredictable ways, and you wish to animate the changes, then you will usually use the performBatchUpdates method of UICollectionView and UITableView.

This method takes a block, and in that block you are expected to account for all the changes in your datasource. For instance, if the number of elements returned in a section is 20 and after the performBatchUpdates-call its 10, they you are required to either delete 10 rows or items within that block, or delete 11 and add 1, or delete 20 and add 10, and so forth. The numbers must add up, otherwise your app will crash with an NSInternatlInconsistencyException. (You can theoretically catch it but I haven’t tried because I suspect it will lead to many odd bugs if you do.)

Ok, so I googled for a generic way to calculate the necessary accounting, and I found this answer on StackOverflow.

The StackOverflow soltuon is presented in pseudocode, so here’s how it would look like with Swift.

import Foundation

enum ArrayAction {
    case added(Int)
    case deleted(Int)
    case moved(Int, Int)
    static func operationsPerformed(arrayBefore before: [Int], arrayAfter after: [Int]) -> [ArrayAction] {
        var arrayActions = [ArrayAction]()
        var map1 = [Int: Int]()
        var map2 = [Int: Int]()
        for (index, element) in before.enumerated() {
            map1[element] = index
        for (index, element) in after.enumerated() {
            map2[element] = index
        for key in map1.keys.sorted() {
            let index1 = map1[key]
            let index2 = map2[key]
            if index2 == nil {
                let deleteAction = ArrayAction.deleted(index1!)
            else if index1 != index2 {
                let moveAction = ArrayAction.moved(index1!, index2!)
                map2.removeValue(forKey: key)
            } else {
                map2.removeValue(forKey: key)
        for key in map2.keys {
            let addAction = ArrayAction.added(map2[key]!)
        return arrayActions

So as you can see there’s an enum called ArrayAction that represents the rows/items added, deleted and moved. Here’s how I use that method in my ViewController:

 private func performCollectionViewOperations(block: () -> (), completionBlock: (()->())? = nil) {
            let filteredBefore = { $0.index }
            let filteredAfter = { $0.index }
            for action in ArrayAction.operationsPerformed(arrayBefore: filteredBefore, arrayAfter: filteredAfter) {
                switch action {
                case .added(let i):
                    self.collectionView.insertItems(at: [IndexPath(item: i, section: 0)])
                case .deleted(let i):
                    self.collectionView.deleteItems(at: [IndexPath(item: i, section: 0)])
                case .moved(let i, let j):
                    self.collectionView.moveItem(at: IndexPath(item: i, section: 0), to: IndexPath(item: j, section: 0))
        }) { _ in

And that private method is used like that:

    self.performCollectionViewOperations(block: {
        // do some adding, removing and moving of elements here.
    }) // not using the completionBlock here

And that’s pretty much it.

Where do I use this code and might it have anything to do with the title picture? Glad you asked. :P

It’s used in Poly Art, a game a friend and I made for iOS. It’s free to play and we’d be happy if you gave it a try!

(I really had a hard time calculating the deltas in the game, because I stubbornly avoided the generic solution. Instead I was trying to anticipate what changes were possible, but as any programmer sooner or later learns: Even the simplest things can get very difficult if you use just a few of them combined.)

How to Scrape Wikiquote

Wikiquote logo

I like to use the command line tool fortune which displays a random quote from a database of files.

I wanted to add quotes from Wikiquote. But it turns out that Wikiquote is difficult to parse and the API is no help. (The API gives you a structured document but the actual content is just one large wall of text that has to be parsed just like the HTML page.)

So after giving up on scraping the webpage I had the idea to use Wikiquote’s edit page.

Here’s how it works: Let’s say you want the quotes from the Richard Feynman page. Open it on your browser, tap edit, and you get the edit page for Richard Feynman.

Voilà. It’s much easier to parse! Simply take all lines from the <textarea> that start with one or two asterisks (depending if you want the source) and then filter out a few things like formatting and lines that only contain quotes. A much better scraping experience.

So yeah, that’s my idea/innovation. I wrote a scraper in ruby that you can find on Github which generates the files needed by fortune. Check out the README there.

(Wikiquote image taken from here.)

AWS: How to Change S3 Response Headers

I have S3 buckets that I use to serve public assets with (behind CloudFront, with a custom domain name). However the response always included the header field “Server” with a value “AmazonS3”. I didn’t like to publish the fact that I was using S3 (because what for?) and I wondered If I could change that string.

It’s not entirely straightforward, but not difficult either.

Here’s how I did it:

  1. Set up S3 buckets

  2. Set up CloudFront for these buckets

  3. Set up a Lambda (Lambda@Edge only, this means us-east-1, N. Virginia)

  4. Insert code (see below)

  5. Actions/publish new version (must be versioned Lambda)

  6. Select version and add CloudWatch triggers. NOTE: Make sure you use the event type “origin-response”.

  7. Ensure permissions for Lambda (logs:CreateLogStream, logs:PutLogEvents, logs:CreateLogGroup, maybe others..)

  8. Set up trust relationship for lambda role, see below.

Note that depending on you CloudFront setup it can take a while for your changes to become visible. I used the “Invaldations” feature to invalidate the cache for some urls.

Lambda Code:

'use strict';

exports.handler = (event, context, callback) => {
    const response = event.Records[0].cf.response;
    response.headers['server'] = [{
        key: 'Server',
        value: 'Whatever you like',

    callback(null, response);

Trust Relationships:

"Service": ["", ""]

This is all based on this useful blogpost, definitely check it out:

How to Tag Stages in AWS API Gateway

I just realised that I cannot add tags to my API:Gateway-stage using CloudFormation templates. However, it’s possible to tag using the command line. But this requires knowledge of the id of the stage - which you don’t get when you use the !Ref function on the Stage.

But luckily it can be constructed easily:

aws apigateway tag-resource --resource-arn arn:aws:apigateway:<REGION-NAME>::/restapis/<RESTAPI-ID>/stages/<STAGE-NAME> --profile <PROFILE-NAME> --tags sometag=somevalue,anothertag=anothervalue

Perhaps this helps someone.

Setting AWS APIGateway Alarms for Certain URLs Only

Quick post on how to set Alarms in AWS CloudWatch that trigger for certain URLs only. This assumes you use API Gateway.

Bascially, you can set Alarms in 3 ways only: Either on the entire API, on the API and the Stage OR on API, Stage, Resource and Method. For the latter two Enable Detailed CloudWatch Metrics has to be checked in the Logs section of Stage properties.

That’s basically it. You need all four dimensions if you want to filter by URL and you need enable Cloudwatch metrics on the stage.

Here’s a YAML snippet showing the declaration of an alarm.

Type: "AWS::CloudWatch::Alarm"
  ActionsEnabled: true
  AlarmActions: [!ImportValue {"Fn::Sub": "SomeNotificationTopicArn"}]
  AlarmDescription: "Some Description"
  AlarmName: "alarm-name"
  ComparisonOperator: GreaterThanThreshold
    - Name: ApiName
      Value: "my-api"
    - Name: Stage
      Value: "LATEST"
    - Name: Resource
      Value: "/some_resource/{somePathParameter}/{anotherPathParameter}"
    - Name: Method
      Value: "GET"
  EvaluationPeriods: 1
  MetricName: 5XXError
  Namespace: AWS/ApiGateway
  OKActions: [!ImportValue {"Fn::Sub": "SomeNotificationTopicArn"}]
  Period: 60
  Statistic: Sum
  Threshold: 0
  TreatMissingData: notBreaching

(I didn’t check the YAML but it should be fine.)

iOS: Bugfix for Using the Sharing-extension of WhatsApp

If you, like me, started to recently get the error “This item cannot be shared. Please select a different item” when sharing to Whatsapp, then this might help you.

You are probably sharing a text-item. In my case it was a text containing an URL.

Instead, I now share the NSURL-object (or an array of objects, in which one is an NSURL object).

I am relatively sure this is a bug in the latest version of Whatsapp, but as long as it persists, this might be a workable workaround.