Saturday, November 07, 2020

New Free iOS App Now Available: Desktop Journey

 I've just released a new, free app for iPhone: Desktop Journey!

Desktop Journey is a single-page dashboard app for iPhone, with an attractive display of time, current and next calendar appointments, reminders, weather, and micro-break prompts. It gives your phone a purpose while it's in its charging cradle on your desk while you work.

With the exception of an optional weather add-in, Desktop Journey is completely free and ad-free.

Why did I make Desktop Journey?

For a while now, when I'm at work at my desk, I've had my iPhone sitting in a charging cradle on the desk.  I wanted to put the iPhone's screen to good use, so it wasn't just sitting there doing nothing.


This isn't adding value. We can do better!

I tried a few different alternatives to just a blank screen, such as a simple analog clock app, but I couldn't find any "dashboard" apps that took advantage of the phone's screen to display a variety of useful information. 

So, as with Vigil RPG, I decided to build it myself!

What does Desktop Journey do?

Here's an overview:

Annotated image of an iPhone 12 running the Desktop Journey app.

Read on for more details!

Current Time


The top portion of the screen is devoted to a simple analog clock. Before creating Desktop Journey, I was using a simple iOS analog clock app as a workaround, and I wanted to keep that core experience (while also adding more to it).

A little "desktop calendar page" icon, styled to look like the native iOS Calendar app icon, shows the current weekday, and day of the month.

"Now" and "Next"

These two panels, shown in the middle portion of the app, display calendar events, reminders, and -- with an optional subscription -- weather and temperature.

πŸ—“ Calendar events are taken from the device's calendar (after permission has been granted to read the calendar). Thus, if you've already set up your iPhone's calendar to sync with another source, such as a Google or Microsoft Outlook account, events from that calendar will appear.

The "Now" panel shows the meeting or appointment that is happening currently, if there is one. The "Next" panel shows just the upcoming appointment (not all of the day's remaining appointments) -- plus the single next appointment after that, if there is one immediately following -- which makes for a nice at-a-glance answer to the question "What do I have coming up next?".

Tapping on an event opens up the view of the current day's events in the native iOS Calendar app.

πŸŽ— Reminders likewise are sourced from the native iOS (again, only after you've granted permission).

The "Now" panel shows a reminder that was due earlier today that you haven't completed yet, if there is one. The "Next" panel shows today's next upcoming reminder that has a date and time set, if any.

Tapping a reminder opens it in the iOS Reminders app, so you can mark it as complete, or edit it.

Temperature and weather for the current locale, and today's upcoming high or low temperature, are optionally shown, with the purchase of a subscription. (These cost me money to subscribe to an API that provides worldwide weather information, and while I'm happy to provide Desktop Journey for free, and with no ads, I'd like to not lose money on it!)

Celsius and Fahrenheit are both supported. Which is displayed can be toggled on the Settings page.

The Hero's Journey

A hero character -- who may look familiar if you've played Vigil RPG! -- walks along at the bottom of the screen, on a long journey.

The biome changes each day, providing some visual variety. In other words, if you run the app again tomorrow, the hero will be somewhere else! There are several biomes, including taiga (pictured up above), desert (pictured here), grassland, caves, forest, dungeon, and more.

The sky also changes, based on the real time of day: Night (as shown here), sunrise, daytime, and dusk.

Encounters - Tasks and Exercises

Several times per day -- at semi-random intervals; typically a little bit less frequently than once per hour -- an enemy encounter will take place!

Each encounter prompts you with an action that you can perform yourself, "in real life," to defeat the enemy. Each of these are quick-to-perform actions that are good opportunities to take a very brief break from whatever task you're working on as the app is running. In this pictured example, you'd stand up from your chair and stretch; then, you'd tap the ✅ button.

There's no time pressure; encounters will last indefinitely (as long as the app is running), so you can delay if needed, and complete them at a convenient time. Alternatively, you can always just hit the Skip button to bypass the encounter instead.

There are two types of encounters: Tasks and exercises.

Tasks are like this one: A simple action that you can perform from your desk as a micro-break. Other tasks include "tidy your workspace" and "message a loved one".

Exercises can help you get moving a bit during your work day. An example is "Do 5 push-ups!". Exercise quantities can be adjusted with + and - buttons that appear during the encounter; so you could tell the app you did less or more than 5 push-ups, for example.

The encounters feature can be turned on or off in the Settings ⚙️ menu. Individual tasks and exercises can also be enabled or disabled, if (for example) your circumstances don't permit standing up, or if you don't feel like being prompted to do push-ups. πŸ™‚

When you complete an encounter, you'll get a stats display of how many of that task or exercise you've completed -- both today, and all-time.


Settings can be accessed by tapping the Settings ⚙️ icon at the top of the app's page. In addition to customizing exercises, you can disable screen lock while the app is in the foreground; toggle temperature units between Fahrenheit and Celsius; and view the in-app "About" and "Credits" pages. 

Give it a try!

As mentioned above, Desktop Journey has no ads, and is a free download, so give it a try, and let me know if you like it! Download Desktop Journey on the App Store.

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!

Sunday, May 17, 2020

Review: Eufy Security Video Doorbell

This past November, I jumped on the trend of replacing my house's existing traditional doorbell with an Internet-enabled doorbell. The key reason for making the change was to solve the fact that I couldn't hear my home's doorbell chime at all while working in my basement home office, which was causing me to miss service appointments, deliveries, and visitors.

After some research, I decided to go with the Eufy Security Video Doorbell. I purchased it from Amazon on a Black Friday sale for US $108, discounted from the full sticker price of $160. (At the time of this post, Amazon is selling this doorbell for $136.)

The doorbell was easy to install. I'm only a very amateur electrician, but I was able to get it installed in place of my existing traditional doorbell with no problem. The doorbell comes with a high-quality set of instructions, in the form of both a short video served through the doorbell's companion smartphone app, and written instructions.

The doorbell's companion app works great for viewing recordings -- which are captured automatically when the doorbell detects a nearby person -- as well as for viewing a live video feed from the doorbell's camera.

The doorbell comes with a remote chime that plugs into a wall socket and plays a sound when someone rings the doorbell. A ring of the doorbell also generates a push notification from the smartphone app.

Both my wife and I were able to create individual Eufy accounts and then associate those accounts with our new doorbell, such that we were both able to receive doorbell ring notifications and view video from the doorbell from our respective smartphones, without having to share a single set of credentials, which was a nice feature.

A major benefit of the Eufy doorbell is that there's no ongoing monthly cost to get its full functionality -- unlike competing devices, such as Amazon's market-leading Ring doorbell. All of the video storage is onboard the device itself, not in the cloud. You still view video recordings and the live video feed from the Eufy app your phone, but there's no cost associated with that capability.

Another major perk of the Eufy device relative to the Ring is the relative lack of privacy / surveillance concerns. If that's something that potentially concerns you, a Google search on terms like "ring police surveillance" is a good starting point for doing your own research.

The one major caveat as of the time of this writing is that the doorbell unit stopped working a few weeks ago. It went from working one day to being dead/unresponsive the next -- no visible lights on the doorbell unit, and no response to button presses. I turned off my power and checked the wiring connections on the unit, but the issue still persisted after I verified that the wiring was fine.

When I contacted Eufy Support about this, they promptly sent me out a new unit, free of charge, which worked fine when I hooked it up to replace the existing (dead) unit. Given this A+ level of support, I don't consider this problem a significant con -- provided this replacement unit manages to last for multiple years!

As of this writing, I highly recommend the Eufy Security Video Doorbell if you're in the market for replacing your home's traditional doorbell with a video doorbell.

Thursday, April 23, 2020 now supports skin tones now supports your choice of skin tone! Just select a tone from the dropdown in the page footer, and the selected tone will be applied to all supported emoji.

In the course of developing this update, I was admittedly surprised to realize that the basic "facial expression" emoji -- πŸ˜„, πŸ˜‚, πŸ€”, and so forth -- aren't toneable! The emoji for which tone support has been added by the Unicode Consortium -- the ones in this list -- are mostly the "hand" emoji (e.g. πŸ‘‹πŸΎ, πŸ‘πŸΌ, πŸ™ŒπŸΏ), plus the ones that depict people's activities or appearances (e.g. πŸ‘©πŸ½‍🦳, πŸ‘¨πŸ»‍🏫, πŸ„πŸ½‍♀️).

The only emoji not supported by ClipEmoji yet are the three "holding hands" emoji (πŸ‘«, πŸ‘­, πŸ‘¬), where the two people depicted have different skin tones. The main thing to be figured out there is an elegant UI to apply tones to that emoji, that doesn't "get in the way" of the other thousands of emoji!

The ClipEmoji site will remember your selection for future visits to the site, too (via a cookie).

I also added an About page to, so check that out if you're in the mood for even more ClipEmoji-related reading.

Enjoy the updated! πŸ‘πŸ»πŸ‘πŸΌπŸ‘πŸ½πŸ‘πŸΎπŸ‘πŸΏ

Sunday, April 05, 2020 updated with 2020 v13 Unicode emoji set

I've updated with the new 2020 v13 Unicode emoji set. This is a new set of emoji, such as "smiling face with tear" (πŸ₯²), that will be added to various platforms -- iOS / MacOS, Android phones, Windows, and so forth -- later this year.

(So if you're reading this post just after it was published, you most likely cannot see that above emoji character quite yet! πŸ˜‚ ClipEmoji is now ready to go, though, as soon as support does get added to your platform.) is a simple and lightweight web page that's designed in particular for desktop and laptop computers -- where the keyboard (obviously) doesn't include emoji characters -- to make it easy to get a particular emoji that you want to use in some message or text you're composing onto your clipboard, so that you can paste it into the text you're composing.

Thursday, March 26, 2020

Side-by-side Firefox browsers separating work and personal accounts

Firefox is my favorite web browser. It supports proper Ctrl+Tab behavior, it's fast, and it's a leader when it comes to prioritizing the privacy of individual users over the agendas of corporate ad networks.

I also like keeping my work-related stuff and my personal stuff organized into two separate browser programs. A big advantage of that is I can be logged in to my work Google account (for email, calendar, etc.) in the first browser, and my personal Google account in the second browser, and don't ever need to manually switch back and forth between which account is currently active.

(Separate browser programs -- not just browser windows -- is needed for this to work. Separate browser programs, such as Firefox and Chrome, don't share cookie storage with one another, which means that they have completely separate records of your login information for each website.)

As a bonus requirement, I want to be able to tell at a glance whether a given browser window that I've got open in front of me belongs to my "work" or "personal" browser.

Finally, with Firefox being my preferred browser, I'd like to be able to use it for both my work stuff, and my personal stuff. Given that I need to be using two different browser programs in order to keep my work and personal stuff separated, is it impossible?

Enter: Firefox Developer Edition

As you've likely guessed, this all is possible, by installing Firefox Developer Edition!

Firefox Developer Edition is like the standard Firefox browser, but with a few additional programmer-oriented bells and whistles.  For most practical purposes, though, it works just like the standard Firefox.

Most importantly for this post's use case, Firefox Developer Edition fully supports being installed, and running, side-by-side with Firefox.  The two browsers each have their own separate cookie storage, and so each supports being logged in with a different user account to the same website, such as Google, at the same time.

Firefox and Developer Edition can also be themed (given a custom appearance) separately. I've landed on assigning a simple orange theme to my Firefox, and a simple blue theme to my Firefox developer addition, so telling which browser a given window belongs to is as simple as glancing at the top of the window, and looking at the color.

I'd encourage even non-developers who like Firefox, and are interested in separating out your personal and work stuff into separate browsers, to give this a try. There's nothing "scary" about Firefox Developer Edition; it's easy to just use it as another copy of Firefox.

Tuesday, February 25, 2020

Workaround: Trello card shows "Loading..." instead of GitHub PR link

Here's a quick workaround for when you pull up a Trello card with the intention of clicking through to the linked GitHub pull request, but the "GitHub Pull Requests" section of the card stubbornly just displays "Loading...", without ever resolving to the link you're after. (This situation is likely related to a number of HTTP 500 error responses from requests to visible in the Network tab of the browser's developer tools.)

Scroll down to the Activity section of the card, and look for the events with the "attached" verb. One of them should include the GitHub PR link.

If you know your GitHub repository's name, you can do a Ctrl+F/⌘+F search for that. The word "GitHub" doesn't actually appear in the text of the activity log item, but the repo name does.

Sunday, February 09, 2020

Vigil RPG 5th Anniversary Edition Now Available!

Now available on the App Store for iOS devices: Vigil RPG, Special 5th Anniversary Edition!

Vigil RPG is a game that I solo-developed and released for iPhone back at the end of 2014.  Vigil RPG is the kind of designed-for-mobile RPG that I couldn't find on the App Store, so I wrote it myself! It features interesting combats (even in non-boss battles), while also allowing you to play each combat round in a single tap.

I'm happy to announce that Vigil RPG, Special 5th Anniversary Edition, is now available! It's a free update to the original Vigil RPG app. It features:

Universal (iPad) support

Vigil RPG had always been playable on iPad, but only as an automatically-scaled-up version of the original iPhone 4. Now, Vigil RPG is a full Universal iOS app, with every screen in the game having been updated to take advantage of iPad portrait-mode screen resolutions.

iPhone X and iPhone 11 support

Vigil RPG now also takes advantage of the full resolution available on newer iPhones, all the way up to the iPhone 11 Pro Max. (Future large phone models will now be automatically supported as well.) Enjoy Vigil RPG's beautiful pixel graphics in glorious high resolution on your new device!

New and reworked enemies

A new, unique early-game boss enemy has been added. Players familiar with Vigil RPG will no longer find it quite so easy to retrieve a quest item from one of the local farms early on in the game!

A number of existing enemies in the game have been reworked to make them more interesting to fight. A few examples:
  • The Tank Goblin, instead of just having thick armor, now has a defense that is really tough to crack.  Players familiar with how to beat King Hippo in Nintendo's Punch-Out! series, though, should have an advantage in figuring out how to hurt him effectively...
  • The Skeleton Fire Mage now chants some strange words at the beginning of combat. Could those words be somehow related to the combat round numbers in which it casts its FIRE spells?
  • The Fire Elemental now grows into a larger and larger flame as combat progresses! The ICE spell can help cut it back down to size, though!
  • It's now extra effective to use the never-miss FIRE spell on flying enemies, such as the Giant Hornet that is encountered early in the game.

New Music

Delight your ears with new 8-bit-style music tracks for many of Vigil RPG's unique boss enemies!

As a bonus, here's my favorite, as a free download: The new Arcane Tower boss battle theme!  (Composer credit: nene at!)

New In-Game Commentary

Everyone's favorite root-beer-serving bartender, Bo, has lots and lots of new commentary, gameplay tips, and humor for you at the Tavern. You can also now just tap him to get more tips, instead of having to leave the tavern and come back again.

Local High Scores

Vigil RPG has always featured an "elapsed turns" system, which tracks how efficiently and effectively you've been proceeding through the game. Completing quests in fewer turns gets you better quest completion rewards.

Now, a local high scores table for each quest is available at the Tavern! When you complete a quest at the Tavern, your performance compared to other save files and prior playthroughs on your device is now displayed.

Many more enhancements!

To name just a few:
  • Stats are now displayed at the end of combat for how well you did mitigating enemy special attacks (i.e. blocking them, or otherwise taking advantage of an enemy's weakness to stop them from being effective), and interrupting enemy spells.  These stats for the entirety of your playthrough are also now displayed on the Hero Status screen!
  • The Shaman (a late-game NPC located in a remote section of the world map) now invites the hero to rest at his hut (to restore HP) after he is done assigning the hero a quest!
  • The Town of Arboria now has a new and improved appearance!
  • Some quest rewards, hero level-up bonuses, item prices, and monster stats have been tweaked to make Vigil RPG even more finely balanced!
  • Picking up Fire Rubies in the Volcano now slightly restore the hero's HP!
  • 400+ new git commits of new sound effects, UI improvements, and other goodness!

Support your local Jon Schneider's Tech Blog indie game development scene!

To learn more about Vigil RPG, check out the Vigil RPG website.  Or help me buy my wife a delicious coffee by checking out the game that named one of the top 10 JRPG-style games available on iOS -- alongside such notable series as Final Fantasy, Dragon Quest, and Chrono Trigger! -- by buying your very own copy of Vigil RPG on the App Store!

Friday, February 07, 2020

Fix: "OS Error 4294956486" launching Children of Morta on Mac/Steam

Early this morning, I tried to launch the game Children of Morta on Steam running on my Mac. The game failed to open, and Steam displayed the error "OS Error 4294956486".


Deleting the game via the Steam (via Library → Right-click on Children of Morta in the list → Manage → Uninstall), and then redownloading and reinstalling Children of Morta, fixed the problem for me, and I was able to get a little gaming in before my work day.

Other solutions attempted that didn't work for me

  • Restarting Steam.
  • Restarting my Mac.
  • Launching the game manually via the command line.
  • Doing a "Verify Integrity of Game Files" (via Library > Right-click on Children of Morta → Properties → Local Files tab → "Verify Integrity of Game Files" button).