Friday, August 28, 2020

How to wire up a delegate method in Objective-C

This post covers how to wire up a delegate method in Objective-C. I'm always forgetting how to do this, so this post will serve as a reference for myself going forward, and hopefully may be useful to others as well!

A delegate method is useful when a given object needs to call a method on an object to which it does not have a reference. This typically comes up when the calling object is the child in a parent-child relationship in the application's object model. 

A practical example of when I've used this pattern is in Vigil RPG, after a combat has concluded, the combat screen ViewController notifies the parent ViewController (either the world map, or the dungeon level) that the combat has concluded, so that the parent can resume playing its background music.

In the parent class (the CALLEE): 

In the .h file:

Add the delegate as an interface in the class declaration:

@interface ParentClass: NSObject<myDelegate>

(There's no need to declare the callee method in the class's public interface.)


In the .m file:

1. Wire up the delegate

This can be done in the init method, or in any location where a reference to the child object is available which gets called only once:

self.myChild.myDelegate = self;

2. Define the callee method

- (void) myDelegateMethod

 // Implementation of the method that we want to call 

// from the child class goes here.

In the child class (the CALLER):

In the .h file:

1. Define the delegate protocol

(This can alternatively be done in a standalone .h file if the delegate is going to be used from more than a single callee class.)


This can go just before the @interface:

@protocol myDelegate<NSObject>
- (void) myDelegateMethod;

2. Declare the delegate property

(This goes inside the @interface, along with any other @property items)

@property id<myDelegate> myDelegate;

In the .m file:

Add calls to the delegate methods 


These go in whatever are the appropriate place(s) in the class method implementations: 

if (self.myDelegate != nil)
// Calls myDelegateMethod on the parent class
    [self.myDelegate myDelegateMethod];

Wednesday, August 19, 2020

One Deck Dungeon Digital Scoresheet

One Deck Dungeon is a "Roguelike, dice-placement, single-player" board game. It's created and published by Asmadi games, and has a great digital adaptation for mobile devices and computers by Handelabra Games.  It's my current favorite "player vs environment" board game app on my iPhone.

The iPhone adaptation of One Deck Dungeon is particularly good! It plays in portrait mode, and does a great job of displaying the game, and allowing you to play it, that feels very natural on mobile. (This stands in contrast to many other mobile board game apps, which tend to have a single interface used for both the PC and mobile versions, with minimal adaptations to make the game more usable/playable on mobile devices.)

The mobile version of One Deck Dungeon unfortunately lacks one feature that its physical counterpart has: Tracking of which of the game's playable heroes have successfully completed which of the game's available dungeons. This feature of the physical game adds a lot of replayability, as you can work towards having a particular hero fully clear the game.

To address this, I've created a digital score sheet for One Deck Dungeon! Based on Google Sheets, it tracks which dungeons you've completed with which heroes, on which "challenge tier" (difficulty level), and displays your overall progress clearing the entire game with each hero.  Here's my current progress, as shown on the score sheet:

Now, before starting a new game of One Deck Dungeon on my phone, I'll open up this scoresheet on my phone's Google Sheets app, and choose a hero and dungeon that I haven't cleared yet (or want to improve my best completed difficulty tier on -- those are the values in the 1-4 range being filled in on the sheet). Then, I'll open up the actual One Deck Dungeon app, and start a new game with the selected hero and dungeon! If victorious, I'll come back to the sheet and fill in the appropriate cell.

Feel free to grab a blank copy of my One Deck Dungeon digital score sheet for yourself, and happy dungeoneering!
If you don't own a copy of One Deck Dungeon yet, I recommend it!

Thursday, August 13, 2020

Incrementally troubleshooting production server issues as sculpting marble

Near the beginning of Steve McConnell's classic book on software construction, Code Complete -- it's the one where Jeff Atwood's "Coding Horror" icon image originally came from -- several analogies for the process of creating software are considered, including penmanship, farming, and oyster pearl harvesting, before McConnell finally settles on "construction."

At my job, although I'm primarily a software developer, I do also occasionally put on the hats of DevOps specialist and DBA. In particular, this happens when a legacy server that I'm responsible for experiences production issues, and I'm one of the few with the knowledge and experience to figure out what's wrong, and get it up and running smoothly again.

This morning, while making another pass at troubleshooting a stubborn partial outage that had recently started cropping up on a near-nightly basis, where performance of the production website and/or database was being degraded in the early morning hours for a period of around an hour or two, another analogy for the type of production software maintenance occurred to me: that of sculpting!

Michelangelo's "David"
In troubleshooting this outage, my colleague and I have taken the approach over the past several days of of making a single change that might hopefully resolve the issue; then, waiting to see if the issue still recurred. If so, we'd make another change.
It occurred to me that this was not so dissimilar from the quote attributed to the sculptor Michelangelo saying, in reference to beginning a sculpture, that the desired figure is already in place; the excess marble that does not comprise the statue just needs to be chiseled away.
By way of example, here are some of the incremental changes we attempted, in working towards resolving our persistent nightly outage: 
  • Monitoring the performance of the database during the outage, we observed that a particular stored procedure was comprising much of the runtime.  We optimized that stored procedure by improving a couple of the existing table column indexes used in the sproc's query plan, to reduce the overall number of lookups the stored procedure needed to perform.
  • We altered one of our nightly scheduled jobs that was running concurrent with the outages to avoid calling the possibly-problematic stored procedure at all; instead, we fulfilled the job's data needs by adding some additional output fields to another query the job was already making.
  • Examining the raw server logs of incoming HTTP requests, we observed that a number of poorly-behaved bots (no user-agent strings; no reasonable rate limiting) were hitting our site very aggressively during the outage period -- and that the pages being hit were triggering calls to that same stored procedure. As at least an interim fix, we put rules in place to prevent the pages being hit from being rendered in response to those particular requests.

Each of these steps of trimming off poorly-performing code and problematic incoming requests has felt a bit like chiseling away some stone, hopefully in the end arriving at a nice statue -- or in our case, a stable production system!